-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Statement.php
173 lines (144 loc) · 4.53 KB
/
Statement.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\SQLSrv\Exception\Error;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use function assert;
use function is_int;
use function sqlsrv_execute;
use function SQLSRV_PHPTYPE_STREAM;
use function SQLSRV_PHPTYPE_STRING;
use function sqlsrv_prepare;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_ENC_CHAR;
use const SQLSRV_PARAM_IN;
final class Statement implements StatementInterface
{
/**
* The SQLSRV statement resource.
*
* @var resource|null
*/
private $stmt;
/**
* References to the variables bound as statement parameters.
*
* @var array<int, mixed>
*/
private array $variables = [];
/**
* Bound parameter types.
*
* @var array<int, ParameterType>
*/
private array $types = [];
/**
* Append to any INSERT query to retrieve the last insert id.
*/
private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;';
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $conn
*/
public function __construct(
private readonly mixed $conn,
private string $sql
) {
if (stripos($sql, 'INSERT INTO ') !== 0) {
return;
}
$this->sql .= self::LAST_INSERT_ID_SQL;
}
public function bindValue(int|string $param, mixed $value, ParameterType $type): void
{
assert(is_int($param));
$this->variables[$param] = $value;
$this->types[$param] = $type;
}
public function bindParam(
int|string $param,
mixed &$variable,
ParameterType $type,
?int $length = null
): void {
assert(is_int($param));
$this->variables[$param] =& $variable;
$this->types[$param] = $type;
// unset the statement resource if it exists as the new one will need to be bound to the new variable
$this->stmt = null;
}
public function execute(?array $params = null): Result
{
if ($params !== null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5556',
'Passing $params to Statement::execute() is deprecated. Bind parameters using'
. ' Statement::bindParam() or Statement::bindValue() instead.'
);
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val, ParameterType::STRING);
} else {
$this->bindValue($key, $val, ParameterType::STRING);
}
}
}
$this->stmt ??= $this->prepare();
if (! sqlsrv_execute($this->stmt)) {
throw Error::new();
}
return new Result($this->stmt);
}
/**
* Prepares SQL Server statement resource
*
* @return resource
*
* @throws Exception
*/
private function prepare()
{
$params = [];
foreach ($this->variables as $column => &$variable) {
switch ($this->types[$column]) {
case ParameterType::LARGE_OBJECT:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
SQLSRV_SQLTYPE_VARBINARY('max'),
];
break;
case ParameterType::BINARY:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY),
];
break;
case ParameterType::ASCII:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR),
];
break;
default:
$params[$column - 1] =& $variable;
break;
}
}
$stmt = sqlsrv_prepare($this->conn, $this->sql, $params);
if ($stmt === false) {
throw Error::new();
}
return $stmt;
}
}