1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Doctrine\DBAL\Driver\SQLAnywhere; |
||||
6 | |||||
7 | use Doctrine\DBAL\Driver\Statement; |
||||
8 | use Doctrine\DBAL\Driver\StatementIterator; |
||||
9 | use Doctrine\DBAL\Exception\GetVariableType; |
||||
10 | use Doctrine\DBAL\Exception\InvalidColumnIndex; |
||||
11 | use Doctrine\DBAL\FetchMode; |
||||
12 | use Doctrine\DBAL\ParameterType; |
||||
13 | use IteratorAggregate; |
||||
14 | use ReflectionClass; |
||||
15 | use ReflectionObject; |
||||
16 | use stdClass; |
||||
17 | use function array_key_exists; |
||||
18 | use function assert; |
||||
19 | use function count; |
||||
20 | use function is_int; |
||||
21 | use function is_object; |
||||
22 | use function is_resource; |
||||
23 | use function is_string; |
||||
24 | use function sasql_fetch_array; |
||||
25 | use function sasql_fetch_assoc; |
||||
26 | use function sasql_fetch_object; |
||||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
27 | use function sasql_fetch_row; |
||||
28 | use function sasql_prepare; |
||||
29 | use function sasql_stmt_affected_rows; |
||||
30 | use function sasql_stmt_bind_param_ex; |
||||
31 | use function sasql_stmt_execute; |
||||
32 | use function sasql_stmt_field_count; |
||||
33 | use function sasql_stmt_reset; |
||||
34 | use function sasql_stmt_result_metadata; |
||||
35 | use function sprintf; |
||||
36 | use const SASQL_BOTH; |
||||
37 | |||||
38 | /** |
||||
39 | * SAP SQL Anywhere implementation of the Statement interface. |
||||
40 | */ |
||||
41 | final class SQLAnywhereStatement implements IteratorAggregate, Statement |
||||
42 | { |
||||
43 | /** @var resource The connection resource. */ |
||||
44 | private $conn; |
||||
45 | |||||
46 | /** @var string Name of the default class to instantiate when fetching class instances. */ |
||||
47 | private $defaultFetchClass = '\stdClass'; |
||||
48 | |||||
49 | /** @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances. */ |
||||
50 | private $defaultFetchClassCtorArgs = []; |
||||
51 | |||||
52 | /** @var int Default fetch mode to use. */ |
||||
53 | private $defaultFetchMode = FetchMode::MIXED; |
||||
54 | |||||
55 | /** @var resource|null The result set resource to fetch. */ |
||||
56 | private $result; |
||||
57 | |||||
58 | /** @var resource The prepared SQL statement to execute. */ |
||||
59 | private $stmt; |
||||
60 | |||||
61 | /** @var mixed[] The references to bound parameter values. */ |
||||
62 | private $boundValues = []; |
||||
63 | |||||
64 | /** |
||||
65 | * Prepares given statement for given connection. |
||||
66 | * |
||||
67 | * @param resource $conn The connection resource to use. |
||||
68 | * @param string $sql The SQL statement to prepare. |
||||
69 | * |
||||
70 | * @throws SQLAnywhereException |
||||
71 | */ |
||||
72 | public function __construct($conn, string $sql) |
||||
73 | { |
||||
74 | if (! is_resource($conn)) { |
||||
75 | throw new SQLAnywhereException(sprintf( |
||||
76 | 'Invalid SQL Anywhere connection resource, %s given.', |
||||
77 | (new GetVariableType())->__invoke($conn) |
||||
78 | )); |
||||
79 | } |
||||
80 | |||||
81 | $this->conn = $conn; |
||||
82 | $this->stmt = sasql_prepare($conn, $sql); |
||||
83 | |||||
84 | if (! is_resource($this->stmt)) { |
||||
85 | throw SQLAnywhereException::fromSQLAnywhereError($conn); |
||||
86 | } |
||||
87 | } |
||||
88 | |||||
89 | /** |
||||
90 | * {@inheritdoc} |
||||
91 | * |
||||
92 | * @throws SQLAnywhereException |
||||
93 | */ |
||||
94 | public function bindParam($param, &$variable, int $type = ParameterType::STRING, ?int $length = null) : void |
||||
95 | { |
||||
96 | assert(is_int($param)); |
||||
97 | |||||
98 | switch ($type) { |
||||
99 | case ParameterType::INTEGER: |
||||
100 | case ParameterType::BOOLEAN: |
||||
101 | $type = 'i'; |
||||
102 | break; |
||||
103 | |||||
104 | case ParameterType::LARGE_OBJECT: |
||||
105 | $type = 'b'; |
||||
106 | break; |
||||
107 | |||||
108 | case ParameterType::NULL: |
||||
109 | case ParameterType::STRING: |
||||
110 | case ParameterType::BINARY: |
||||
111 | $type = 's'; |
||||
112 | break; |
||||
113 | |||||
114 | default: |
||||
115 | throw new SQLAnywhereException(sprintf('Unknown type %d.', $type)); |
||||
116 | } |
||||
117 | |||||
118 | $this->boundValues[$param] =& $variable; |
||||
119 | |||||
120 | if (! sasql_stmt_bind_param_ex($this->stmt, $param - 1, $variable, $type, $variable === null)) { |
||||
121 | throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); |
||||
122 | } |
||||
123 | } |
||||
124 | |||||
125 | /** |
||||
126 | * {@inheritdoc} |
||||
127 | */ |
||||
128 | public function bindValue($param, $value, int $type = ParameterType::STRING) : void |
||||
129 | { |
||||
130 | $this->bindParam($param, $value, $type); |
||||
131 | } |
||||
132 | |||||
133 | public function closeCursor() : void |
||||
134 | { |
||||
135 | sasql_stmt_reset($this->stmt); |
||||
136 | } |
||||
137 | |||||
138 | public function columnCount() : int |
||||
139 | { |
||||
140 | return sasql_stmt_field_count($this->stmt); |
||||
141 | } |
||||
142 | |||||
143 | /** |
||||
144 | * {@inheritdoc} |
||||
145 | * |
||||
146 | * @throws SQLAnywhereException |
||||
147 | */ |
||||
148 | public function execute(?array $params = null) : void |
||||
149 | { |
||||
150 | if ($params !== null) { |
||||
151 | $hasZeroIndex = array_key_exists(0, $params); |
||||
152 | |||||
153 | foreach ($params as $key => $val) { |
||||
154 | if ($hasZeroIndex && is_int($key)) { |
||||
155 | $this->bindValue($key + 1, $val); |
||||
156 | } else { |
||||
157 | $this->bindValue($key, $val); |
||||
158 | } |
||||
159 | } |
||||
160 | } |
||||
161 | |||||
162 | if (! sasql_stmt_execute($this->stmt)) { |
||||
163 | throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); |
||||
164 | } |
||||
165 | |||||
166 | $this->result = sasql_stmt_result_metadata($this->stmt); |
||||
167 | } |
||||
168 | |||||
169 | /** |
||||
170 | * {@inheritdoc} |
||||
171 | * |
||||
172 | * @throws SQLAnywhereException |
||||
173 | */ |
||||
174 | public function fetch(?int $fetchMode = null, ...$args) |
||||
175 | { |
||||
176 | if (! is_resource($this->result)) { |
||||
177 | return false; |
||||
178 | } |
||||
179 | |||||
180 | $fetchMode = $fetchMode ?? $this->defaultFetchMode; |
||||
181 | |||||
182 | switch ($fetchMode) { |
||||
183 | case FetchMode::COLUMN: |
||||
184 | return $this->fetchColumn(); |
||||
185 | |||||
186 | case FetchMode::ASSOCIATIVE: |
||||
187 | return sasql_fetch_assoc($this->result); |
||||
188 | |||||
189 | case FetchMode::MIXED: |
||||
190 | return sasql_fetch_array($this->result, SASQL_BOTH); |
||||
191 | |||||
192 | case FetchMode::CUSTOM_OBJECT: |
||||
193 | $className = $this->defaultFetchClass; |
||||
194 | $ctorArgs = $this->defaultFetchClassCtorArgs; |
||||
195 | |||||
196 | if (count($args) > 0) { |
||||
197 | $className = $args[0]; |
||||
198 | $ctorArgs = $args[1] ?? []; |
||||
199 | } |
||||
200 | |||||
201 | $result = sasql_fetch_object($this->result); |
||||
0 ignored issues
–
show
The function
sasql_fetch_object was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
202 | |||||
203 | if ($result instanceof stdClass) { |
||||
204 | $result = $this->castObject($result, $className, $ctorArgs); |
||||
205 | } |
||||
206 | |||||
207 | return $result; |
||||
208 | |||||
209 | case FetchMode::NUMERIC: |
||||
210 | return sasql_fetch_row($this->result); |
||||
211 | |||||
212 | case FetchMode::STANDARD_OBJECT: |
||||
213 | return sasql_fetch_object($this->result); |
||||
214 | |||||
215 | default: |
||||
216 | throw new SQLAnywhereException(sprintf('Fetch mode is not supported %d.', $fetchMode)); |
||||
217 | } |
||||
218 | } |
||||
219 | |||||
220 | /** |
||||
221 | * {@inheritdoc} |
||||
222 | */ |
||||
223 | public function fetchAll(?int $fetchMode = null, ...$args) : array |
||||
224 | { |
||||
225 | $rows = []; |
||||
226 | |||||
227 | switch ($fetchMode) { |
||||
228 | case FetchMode::CUSTOM_OBJECT: |
||||
229 | while (($row = $this->fetch($fetchMode, ...$args)) !== false) { |
||||
230 | $rows[] = $row; |
||||
231 | } |
||||
232 | |||||
233 | break; |
||||
234 | |||||
235 | case FetchMode::COLUMN: |
||||
236 | while (($row = $this->fetchColumn()) !== false) { |
||||
237 | $rows[] = $row; |
||||
238 | } |
||||
239 | |||||
240 | break; |
||||
241 | |||||
242 | default: |
||||
243 | while (($row = $this->fetch($fetchMode)) !== false) { |
||||
244 | $rows[] = $row; |
||||
245 | } |
||||
246 | } |
||||
247 | |||||
248 | return $rows; |
||||
249 | } |
||||
250 | |||||
251 | /** |
||||
252 | * {@inheritdoc} |
||||
253 | */ |
||||
254 | public function fetchColumn(int $columnIndex = 0) |
||||
255 | { |
||||
256 | $row = $this->fetch(FetchMode::NUMERIC); |
||||
257 | |||||
258 | if ($row === false) { |
||||
259 | return false; |
||||
260 | } |
||||
261 | |||||
262 | if (! array_key_exists($columnIndex, $row)) { |
||||
263 | throw InvalidColumnIndex::new($columnIndex, count($row)); |
||||
264 | } |
||||
265 | |||||
266 | return $row[$columnIndex]; |
||||
267 | } |
||||
268 | |||||
269 | /** |
||||
270 | * {@inheritdoc} |
||||
271 | */ |
||||
272 | public function getIterator() |
||||
273 | { |
||||
274 | return new StatementIterator($this); |
||||
275 | } |
||||
276 | |||||
277 | public function rowCount() : int |
||||
278 | { |
||||
279 | return sasql_stmt_affected_rows($this->stmt); |
||||
280 | } |
||||
281 | |||||
282 | /** |
||||
283 | * {@inheritdoc} |
||||
284 | */ |
||||
285 | public function setFetchMode(int $fetchMode, ...$args) : void |
||||
286 | { |
||||
287 | $this->defaultFetchMode = $fetchMode; |
||||
288 | |||||
289 | if (isset($args[0])) { |
||||
290 | $this->defaultFetchClass = $args[0]; |
||||
291 | } |
||||
292 | |||||
293 | if (! isset($args[1])) { |
||||
294 | return; |
||||
295 | } |
||||
296 | |||||
297 | $this->defaultFetchClassCtorArgs = (array) $args[1]; |
||||
298 | } |
||||
299 | |||||
300 | /** |
||||
301 | * Casts a stdClass object to the given class name mapping its' properties. |
||||
302 | * |
||||
303 | * @param stdClass $sourceObject Object to cast from. |
||||
304 | * @param string|object $destinationClass Name of the class or class instance to cast to. |
||||
305 | * @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance. |
||||
306 | * |
||||
307 | * @throws SQLAnywhereException |
||||
308 | */ |
||||
309 | private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = []) : object |
||||
310 | { |
||||
311 | if (! is_string($destinationClass)) { |
||||
312 | if (! is_object($destinationClass)) { |
||||
313 | throw new SQLAnywhereException(sprintf( |
||||
314 | 'Destination class has to be of type string or object, "%s" given.', |
||||
315 | (new GetVariableType())->__invoke($destinationClass) |
||||
316 | )); |
||||
317 | } |
||||
318 | } else { |
||||
319 | $destinationClass = new ReflectionClass($destinationClass); |
||||
320 | $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); |
||||
321 | } |
||||
322 | |||||
323 | $sourceReflection = new ReflectionObject($sourceObject); |
||||
324 | $destinationClassReflection = new ReflectionObject($destinationClass); |
||||
325 | |||||
326 | foreach ($sourceReflection->getProperties() as $sourceProperty) { |
||||
327 | $sourceProperty->setAccessible(true); |
||||
328 | |||||
329 | $name = $sourceProperty->getName(); |
||||
330 | $value = $sourceProperty->getValue($sourceObject); |
||||
331 | |||||
332 | if ($destinationClassReflection->hasProperty($name)) { |
||||
333 | $destinationProperty = $destinationClassReflection->getProperty($name); |
||||
334 | |||||
335 | $destinationProperty->setAccessible(true); |
||||
336 | $destinationProperty->setValue($destinationClass, $value); |
||||
337 | } else { |
||||
338 | $destinationClass->$name = $value; |
||||
339 | } |
||||
340 | } |
||||
341 | |||||
342 | return $destinationClass; |
||||
343 | } |
||||
344 | } |
||||
345 |