SQLSrvStatement.php 4.62 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\DBAL\Driver\SQLSrv;

5
use Doctrine\DBAL\Driver\Result as ResultInterface;
6 7
use Doctrine\DBAL\Driver\SQLSrv\Exception\Error;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
8
use Doctrine\DBAL\ParameterType;
9

10
use function assert;
Sergei Morozov's avatar
Sergei Morozov committed
11
use function is_int;
12 13 14 15 16
use function sqlsrv_execute;
use function sqlsrv_fetch;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
use function SQLSRV_PHPTYPE_STREAM;
17
use function SQLSRV_PHPTYPE_STRING;
18 19 20
use function sqlsrv_prepare;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
21

Grégoire Paris's avatar
Grégoire Paris committed
22 23
use const SQLSRV_ENC_BINARY;
use const SQLSRV_PARAM_IN;
24

25
/**
Benjamin Morel's avatar
Benjamin Morel committed
26
 * SQL Server Statement.
27 28
 *
 * @deprecated Use {@link Statement} instead
29
 */
30
class SQLSrvStatement implements StatementInterface
31 32
{
    /**
Benjamin Morel's avatar
Benjamin Morel committed
33
     * The SQLSRV Resource.
34 35 36 37 38 39
     *
     * @var resource
     */
    private $conn;

    /**
Benjamin Morel's avatar
Benjamin Morel committed
40
     * The SQL statement to execute.
41 42 43 44 45 46
     *
     * @var string
     */
    private $sql;

    /**
Benjamin Morel's avatar
Benjamin Morel committed
47
     * The SQLSRV statement resource.
48
     *
49
     * @var resource|null
50 51 52 53
     */
    private $stmt;

    /**
54
     * References to the variables bound as statement parameters.
55
     *
56
     * @var mixed
57
     */
58
    private $variables = [];
59 60 61 62

    /**
     * Bound parameter types.
     *
63
     * @var int[]
64
     */
65
    private $types = [];
66

67
    /**
Benjamin Morel's avatar
Benjamin Morel committed
68 69
     * The last insert ID.
     *
70
     * @var LastInsertId|null
71 72 73 74 75 76
     */
    private $lastInsertId;

    /**
     * Append to any INSERT query to retrieve the last insert id.
     */
77
    private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;';
78

Benjamin Morel's avatar
Benjamin Morel committed
79
    /**
80 81
     * @param resource $conn
     * @param string   $sql
Benjamin Morel's avatar
Benjamin Morel committed
82
     */
83
    public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null)
84 85
    {
        $this->conn = $conn;
86
        $this->sql  = $sql;
87

88 89
        if (stripos($sql, 'INSERT INTO ') !== 0) {
            return;
90
        }
91 92 93

        $this->sql         .= self::LAST_INSERT_ID_SQL;
        $this->lastInsertId = $lastInsertId;
94 95
    }

Benjamin Morel's avatar
Benjamin Morel committed
96 97 98
    /**
     * {@inheritdoc}
     */
99
    public function bindValue($param, $value, $type = ParameterType::STRING)
100
    {
101
        assert(is_int($param));
102 103

        $this->variables[$param] = $value;
104
        $this->types[$param]     = $type;
105 106

        return true;
107 108 109 110 111
    }

    /**
     * {@inheritdoc}
     */
112
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
113
    {
114
        assert(is_int($column));
115

116
        $this->variables[$column] =& $variable;
117
        $this->types[$column]     = $type;
118 119 120

        // unset the statement resource if it exists as the new one will need to be bound to the new variable
        $this->stmt = null;
121 122

        return true;
123 124
    }

Benjamin Morel's avatar
Benjamin Morel committed
125 126 127
    /**
     * {@inheritdoc}
     */
128
    public function execute($params = null): ResultInterface
129
    {
130
        if ($params !== null) {
131
            foreach ($params as $key => $val) {
132
                if (is_int($key)) {
Sergei Morozov's avatar
Sergei Morozov committed
133 134 135 136
                    $this->bindValue($key + 1, $val);
                } else {
                    $this->bindValue($key, $val);
                }
137 138 139
            }
        }

140
        if ($this->stmt === null) {
141
            $this->stmt = $this->prepare();
142 143
        }

144
        if (! sqlsrv_execute($this->stmt)) {
145
            throw Error::new();
146
        }
147

148
        if ($this->lastInsertId !== null) {
149 150
            sqlsrv_next_result($this->stmt);
            sqlsrv_fetch($this->stmt);
Steve Müller's avatar
Steve Müller committed
151
            $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0));
152
        }
153

154
        return new Result($this->stmt);
155 156
    }

157 158 159 160
    /**
     * Prepares SQL Server statement resource
     *
     * @return resource
161
     *
162 163 164 165
     * @throws SQLSrvException
     */
    private function prepare()
    {
166
        $params = [];
167 168

        foreach ($this->variables as $column => &$variable) {
169 170 171 172 173
            switch ($this->types[$column]) {
                case ParameterType::LARGE_OBJECT:
                    $params[$column - 1] = [
                        &$variable,
                        SQLSRV_PARAM_IN,
174 175
                        SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
                        SQLSRV_SQLTYPE_VARBINARY('max'),
176 177 178 179 180 181 182
                    ];
                    break;

                case ParameterType::BINARY:
                    $params[$column - 1] = [
                        &$variable,
                        SQLSRV_PARAM_IN,
183
                        SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY),
184 185 186 187 188 189
                    ];
                    break;

                default:
                    $params[$column - 1] =& $variable;
                    break;
190 191 192 193 194
            }
        }

        $stmt = sqlsrv_prepare($this->conn, $this->sql, $params);

195
        if ($stmt === false) {
196
            throw Error::new();
197 198 199 200
        }

        return $stmt;
    }
201
}