Completed
Push — master ( 5dd66e...5b5c2c )
by Marco
114:19 queued 111:15
created

DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php (13 issues)

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);
0 ignored issues
show
The function sasql_prepare 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 ignore-call  annotation

80
        $this->stmt = /** @scrutinizer ignore-call */ sasql_prepare($conn, $sql);
Loading history...
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)) {
0 ignored issues
show
The function sasql_stmt_bind_param_ex 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 ignore-call  annotation

116
        if (! /** @scrutinizer ignore-call */ sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) {
Loading history...
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);
0 ignored issues
show
The function sasql_stmt_field_count 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 ignore-call  annotation

150
        return /** @scrutinizer ignore-call */ sasql_stmt_field_count($this->stmt);
Loading history...
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function errorCode()
157
    {
158
        return sasql_stmt_errno($this->stmt);
0 ignored issues
show
The function sasql_stmt_errno 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 ignore-call  annotation

158
        return /** @scrutinizer ignore-call */ sasql_stmt_errno($this->stmt);
Loading history...
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164
    public function errorInfo()
165
    {
166
        return sasql_stmt_error($this->stmt);
0 ignored issues
show
The function sasql_stmt_error 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 ignore-call  annotation

166
        return /** @scrutinizer ignore-call */ sasql_stmt_error($this->stmt);
Loading history...
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)) {
0 ignored issues
show
The function sasql_stmt_execute 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 ignore-call  annotation

188
        if (! /** @scrutinizer ignore-call */ sasql_stmt_execute($this->stmt)) {
Loading history...
189
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
190
        }
191
192
        $this->result = sasql_stmt_result_metadata($this->stmt);
0 ignored issues
show
The function sasql_stmt_result_metadata 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 ignore-call  annotation

192
        $this->result = /** @scrutinizer ignore-call */ sasql_stmt_result_metadata($this->stmt);
Loading history...
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);
0 ignored issues
show
The function sasql_fetch_assoc 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 ignore-call  annotation

215
                return /** @scrutinizer ignore-call */ sasql_fetch_assoc($this->result);
Loading history...
216
217
            case FetchMode::MIXED:
218
                return sasql_fetch_array($this->result, SASQL_BOTH);
0 ignored issues
show
The function sasql_fetch_array 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 ignore-call  annotation

218
                return /** @scrutinizer ignore-call */ sasql_fetch_array($this->result, SASQL_BOTH);
Loading history...
The constant SASQL_BOTH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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);
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 ignore-call  annotation

230
                $result = /** @scrutinizer ignore-call */ sasql_fetch_object($this->result);
Loading history...
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);
0 ignored issues
show
The function sasql_fetch_row 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 ignore-call  annotation

239
                return /** @scrutinizer ignore-call */ sasql_fetch_row($this->result);
Loading history...
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
    public function fetchColumn($columnIndex = 0)
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 54
    public function getIterator()
296
    {
297 54
        return new StatementIterator($this);
298
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303
    public function rowCount()
304
    {
305
        return sasql_stmt_affected_rows($this->stmt);
0 ignored issues
show
The function sasql_stmt_affected_rows 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 ignore-call  annotation

305
        return /** @scrutinizer ignore-call */ sasql_stmt_affected_rows($this->stmt);
Loading history...
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