Completed
Pull Request — master (#3610)
by Sergei
06:29
created

SQLAnywhereStatement::fetchAll()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 0
cts 21
cp 0
rs 8.8977
c 0
b 0
f 0
cc 6
nc 6
nop 3
crap 42
1
<?php
2
3
namespace Doctrine\DBAL\Driver\SQLAnywhere;
4
5
use Doctrine\DBAL\Driver\Statement;
6
use Doctrine\DBAL\Driver\StatementIterator;
7
use Doctrine\DBAL\FetchMode;
8
use Doctrine\DBAL\ParameterType;
9
use IteratorAggregate;
10
use PDO;
11
use ReflectionClass;
12
use ReflectionObject;
13
use stdClass;
14
use const SASQL_BOTH;
15
use function array_key_exists;
16
use function func_get_args;
17
use function func_num_args;
18
use function gettype;
19
use function is_array;
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;
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_errno;
32
use function sasql_stmt_error;
33
use function sasql_stmt_execute;
34
use function sasql_stmt_field_count;
35
use function sasql_stmt_reset;
36
use function sasql_stmt_result_metadata;
37
use function sprintf;
38
39
/**
40
 * SAP SQL Anywhere implementation of the Statement interface.
41
 */
42
class SQLAnywhereStatement implements IteratorAggregate, Statement
43
{
44
    /** @var resource The connection resource. */
45
    private $conn;
46
47
    /** @var string Name of the default class to instantiate when fetching class instances. */
48
    private $defaultFetchClass = '\stdClass';
49
50
    /** @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances. */
51
    private $defaultFetchClassCtorArgs = [];
52
53
    /** @var int Default fetch mode to use. */
54
    private $defaultFetchMode = FetchMode::MIXED;
55
56
    /** @var resource The result set resource to fetch. */
57
    private $result;
58
59
    /** @var resource The prepared SQL statement to execute. */
60
    private $stmt;
61
62
    /** @var mixed[] The references to bound parameter values. */
63
    private $boundValues = [];
64
65
    /**
66
     * Prepares given statement for given connection.
67
     *
68
     * @param resource $conn The connection resource to use.
69
     * @param string   $sql  The SQL statement to prepare.
70
     *
71
     * @throws SQLAnywhereException
72
     */
73
    public function __construct($conn, $sql)
74
    {
75
        if (! is_resource($conn)) {
76
            throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn);
77
        }
78
79
        $this->conn = $conn;
80
        $this->stmt = sasql_prepare($conn, $sql);
81
82
        if (! is_resource($this->stmt)) {
83
            throw SQLAnywhereException::fromSQLAnywhereError($conn);
84
        }
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     *
90
     * @throws SQLAnywhereException
91
     */
92
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
93
    {
94
        switch ($type) {
95
            case ParameterType::INTEGER:
96
            case ParameterType::BOOLEAN:
97
                $type = 'i';
98
                break;
99
100
            case ParameterType::LARGE_OBJECT:
101
                $type = 'b';
102
                break;
103
104
            case ParameterType::NULL:
105
            case ParameterType::STRING:
106
            case ParameterType::BINARY:
107
                $type = 's';
108
                break;
109
110
            default:
111
                throw new SQLAnywhereException('Unknown type: ' . $type);
112
        }
113
114
        $this->boundValues[$column] =& $variable;
115
116
        if (! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) {
117
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
118
        }
119
120
        return true;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function bindValue($param, $value, $type = ParameterType::STRING)
127
    {
128
        return $this->bindParam($param, $value, $type);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     *
134
     * @throws SQLAnywhereException
135
     */
136
    public function closeCursor()
137
    {
138
        if (! sasql_stmt_reset($this->stmt)) {
139
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
140
        }
141
142
        return true;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    public function columnCount()
149
    {
150
        return sasql_stmt_field_count($this->stmt);
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function errorCode()
157
    {
158
        return sasql_stmt_errno($this->stmt);
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164
    public function errorInfo()
165
    {
166
        return sasql_stmt_error($this->stmt);
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     *
172
     * @throws SQLAnywhereException
173
     */
174
    public function execute($params = null)
175
    {
176
        if (is_array($params)) {
177
            $hasZeroIndex = array_key_exists(0, $params);
178
179
            foreach ($params as $key => $val) {
180
                if ($hasZeroIndex && is_int($key)) {
181
                    $this->bindValue($key + 1, $val);
182
                } else {
183
                    $this->bindValue($key, $val);
184
                }
185
            }
186
        }
187
188
        if (! sasql_stmt_execute($this->stmt)) {
189
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
190
        }
191
192
        $this->result = sasql_stmt_result_metadata($this->stmt);
193
194
        return true;
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     *
200
     * @throws SQLAnywhereException
201
     */
202
    public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
203
    {
204
        if (! is_resource($this->result)) {
205
            return false;
206
        }
207
208
        $fetchMode = $fetchMode ?: $this->defaultFetchMode;
209
210
        switch ($fetchMode) {
211
            case FetchMode::COLUMN:
212
                return $this->fetchColumn();
213
214
            case FetchMode::ASSOCIATIVE:
215
                return sasql_fetch_assoc($this->result);
216
217
            case FetchMode::MIXED:
218
                return sasql_fetch_array($this->result, SASQL_BOTH);
219
220
            case FetchMode::CUSTOM_OBJECT:
221
                $className = $this->defaultFetchClass;
222
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
223
224
                if (func_num_args() >= 2) {
225
                    $args      = func_get_args();
226
                    $className = $args[1];
227
                    $ctorArgs  = $args[2] ?? [];
228
                }
229
230
                $result = sasql_fetch_object($this->result);
231
232
                if ($result instanceof stdClass) {
233
                    $result = $this->castObject($result, $className, $ctorArgs);
234
                }
235
236
                return $result;
237
238
            case FetchMode::NUMERIC:
239
                return sasql_fetch_row($this->result);
240
241
            case FetchMode::STANDARD_OBJECT:
242
                return sasql_fetch_object($this->result);
243
244
            default:
245
                throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
246
        }
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
253
    {
254
        $rows = [];
255
256
        switch ($fetchMode) {
257
            case FetchMode::CUSTOM_OBJECT:
258
                while (($row = $this->fetch(...func_get_args())) !== false) {
259
                    $rows[] = $row;
260
                }
261
                break;
262
263
            case FetchMode::COLUMN:
264
                while (($row = $this->fetchColumn()) !== false) {
265
                    $rows[] = $row;
266
                }
267
                break;
268
269
            default:
270
                while (($row = $this->fetch($fetchMode)) !== false) {
271
                    $rows[] = $row;
272
                }
273
        }
274
275
        return $rows;
276
    }
277
278
    /**
279
     * {@inheritdoc}
280
     */
281 View Code Duplication
    public function fetchColumn($columnIndex = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
    {
283
        $row = $this->fetch(FetchMode::NUMERIC);
284
285
        if ($row === false) {
286
            return false;
287
        }
288
289
        return $row[$columnIndex] ?? null;
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 4
    public function getIterator()
296
    {
297 4
        return new StatementIterator($this);
298
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303
    public function rowCount()
304
    {
305
        return sasql_stmt_affected_rows($this->stmt);
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
312
    {
313
        $this->defaultFetchMode          = $fetchMode;
314
        $this->defaultFetchClass         = $arg2 ?: $this->defaultFetchClass;
315
        $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
316
    }
317
318
    /**
319
     * Casts a stdClass object to the given class name mapping its' properties.
320
     *
321
     * @param stdClass      $sourceObject     Object to cast from.
322
     * @param string|object $destinationClass Name of the class or class instance to cast to.
323
     * @param mixed[]       $ctorArgs         Arguments to use for constructing the destination class instance.
324
     *
325
     * @return object
326
     *
327
     * @throws SQLAnywhereException
328
     */
329
    private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
330
    {
331
        if (! is_string($destinationClass)) {
332
            if (! is_object($destinationClass)) {
333
                throw new SQLAnywhereException(sprintf(
334
                    'Destination class has to be of type string or object, %s given.',
335
                    gettype($destinationClass)
336
                ));
337
            }
338
        } else {
339
            $destinationClass = new ReflectionClass($destinationClass);
340
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
341
        }
342
343
        $sourceReflection           = new ReflectionObject($sourceObject);
344
        $destinationClassReflection = new ReflectionObject($destinationClass);
345
346
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
347
            $sourceProperty->setAccessible(true);
348
349
            $name  = $sourceProperty->getName();
350
            $value = $sourceProperty->getValue($sourceObject);
351
352
            if ($destinationClassReflection->hasProperty($name)) {
353
                $destinationProperty = $destinationClassReflection->getProperty($name);
354
355
                $destinationProperty->setAccessible(true);
356
                $destinationProperty->setValue($destinationClass, $value);
357
            } else {
358
                $destinationClass->$name = $value;
359
            }
360
        }
361
362
        return $destinationClass;
363
    }
364
}
365