Failed Conditions
Pull Request — develop (#3417)
by Sergei
46:21 queued 43:14
created

SQLAnywhereStatement   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 328
Duplicated Lines 0 %

Test Coverage

Coverage 1.8%

Importance

Changes 0
Metric Value
wmc 53
eloc 122
dl 0
loc 328
ccs 2
cts 111
cp 0.018
rs 6.96
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A rowCount() 0 3 1
A errorInfo() 0 3 1
A bindValue() 0 3 1
A execute() 0 19 6
A errorCode() 0 3 1
A fetchColumn() 0 10 3
B fetch() 0 43 11
A setFetchMode() 0 13 3
A __construct() 0 11 3
A columnCount() 0 3 1
A getIterator() 0 3 1
A fetchAll() 0 24 6
A closeCursor() 0 7 2
B bindParam() 0 29 8
A castObject() 0 34 5

How to fix   Complexity   

Complex Class

Complex classes like SQLAnywhereStatement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SQLAnywhereStatement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Driver\SQLAnywhere;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Driver\Statement;
7
use Doctrine\DBAL\Driver\StatementIterator;
8
use Doctrine\DBAL\FetchMode;
9
use Doctrine\DBAL\ParameterType;
10
use IteratorAggregate;
11
use ReflectionClass;
12
use ReflectionObject;
13
use stdClass;
14
use const SASQL_BOTH;
0 ignored issues
show
Bug introduced by
The constant SASQL_BOTH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
15
use function array_key_exists;
16
use function count;
17
use function func_get_args;
18
use function gettype;
19
use function is_array;
20
use function is_numeric;
21
use function is_object;
22
use function is_resource;
23
use function is_string;
24
use function sasql_fetch_array;
0 ignored issues
show
introduced by
The function sasql_fetch_array was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
25
use function sasql_fetch_assoc;
0 ignored issues
show
introduced by
The function sasql_fetch_assoc was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
26
use function sasql_fetch_object;
0 ignored issues
show
introduced by
The function sasql_fetch_object was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
27
use function sasql_fetch_row;
0 ignored issues
show
introduced by
The function sasql_fetch_row was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
28
use function sasql_prepare;
0 ignored issues
show
introduced by
The function sasql_prepare was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
29
use function sasql_stmt_affected_rows;
0 ignored issues
show
introduced by
The function sasql_stmt_affected_rows was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
30
use function sasql_stmt_bind_param_ex;
0 ignored issues
show
introduced by
The function sasql_stmt_bind_param_ex was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
31
use function sasql_stmt_errno;
0 ignored issues
show
introduced by
The function sasql_stmt_errno was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
32
use function sasql_stmt_error;
0 ignored issues
show
introduced by
The function sasql_stmt_error was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
33
use function sasql_stmt_execute;
0 ignored issues
show
introduced by
The function sasql_stmt_execute was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
34
use function sasql_stmt_field_count;
0 ignored issues
show
introduced by
The function sasql_stmt_field_count was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
35
use function sasql_stmt_reset;
0 ignored issues
show
introduced by
The function sasql_stmt_reset was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
36
use function sasql_stmt_result_metadata;
0 ignored issues
show
introduced by
The function sasql_stmt_result_metadata was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
Bug introduced by
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
Bug introduced by
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)) {
0 ignored issues
show
Bug introduced by
The function sasql_stmt_reset 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

138
        if (! /** @scrutinizer ignore-call */ sasql_stmt_reset($this->stmt)) {
Loading history...
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
                $key = $hasZeroIndex && is_numeric($key) ? $key + 1 : $key;
181
182
                $this->bindValue($key, $val);
183
            }
184
        }
185
186
        if (! sasql_stmt_execute($this->stmt)) {
0 ignored issues
show
Bug introduced by
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

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

190
        $this->result = /** @scrutinizer ignore-call */ sasql_stmt_result_metadata($this->stmt);
Loading history...
191
192
        return true;
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     *
198
     * @throws SQLAnywhereException
199
     */
200
    public function fetch($fetchMode = null, ...$args)
201
    {
202
        if (! is_resource($this->result)) {
203
            return false;
204
        }
205
206
        $fetchMode = $fetchMode ?: $this->defaultFetchMode;
207
208
        switch ($fetchMode) {
209
            case FetchMode::COLUMN:
210
                return $this->fetchColumn();
211
212
            case FetchMode::ASSOCIATIVE:
213
                return sasql_fetch_assoc($this->result);
0 ignored issues
show
Bug introduced by
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

213
                return /** @scrutinizer ignore-call */ sasql_fetch_assoc($this->result);
Loading history...
214
215
            case FetchMode::MIXED:
216
                return sasql_fetch_array($this->result, SASQL_BOTH);
0 ignored issues
show
Bug introduced by
The constant SASQL_BOTH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
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

216
                return /** @scrutinizer ignore-call */ sasql_fetch_array($this->result, SASQL_BOTH);
Loading history...
217
218
            case FetchMode::CUSTOM_OBJECT:
219
                $className = $this->defaultFetchClass;
220
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
221
222
                if (count($args) > 0) {
223
                    $className = $args[0];
224
                    $ctorArgs  = $args[1] ?? [];
225
                }
226
227
                $result = sasql_fetch_object($this->result);
0 ignored issues
show
Bug introduced by
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

227
                $result = /** @scrutinizer ignore-call */ sasql_fetch_object($this->result);
Loading history...
228
229
                if ($result instanceof stdClass) {
230
                    $result = $this->castObject($result, $className, $ctorArgs);
231
                }
232
233
                return $result;
234
235
            case FetchMode::NUMERIC:
236
                return sasql_fetch_row($this->result);
0 ignored issues
show
Bug introduced by
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

236
                return /** @scrutinizer ignore-call */ sasql_fetch_row($this->result);
Loading history...
237
238
            case FetchMode::STANDARD_OBJECT:
239
                return sasql_fetch_object($this->result);
240
241
            default:
242
                throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
243
        }
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249
    public function fetchAll($fetchMode = null, ...$args)
250
    {
251
        $rows = [];
252
253
        switch ($fetchMode) {
254
            case FetchMode::CUSTOM_OBJECT:
255
                while (($row = $this->fetch(...func_get_args())) !== false) {
256
                    $rows[] = $row;
257
                }
258
                break;
259
260
            case FetchMode::COLUMN:
261
                while (($row = $this->fetchColumn()) !== false) {
262
                    $rows[] = $row;
263
                }
264
                break;
265
266
            default:
267
                while (($row = $this->fetch($fetchMode)) !== false) {
268
                    $rows[] = $row;
269
                }
270
        }
271
272
        return $rows;
273
    }
274
275
    /**
276
     * {@inheritdoc}
277
     */
278
    public function fetchColumn($columnIndex = 0)
279
    {
280
        $row = $this->fetch(FetchMode::NUMERIC);
281
282
        if ($row === false) {
283
            return false;
284
        }
285
286 46
        if (! array_key_exists($columnIndex, $row)) {
287
            throw DBALException::invalidColumnIndex($columnIndex, count($row));
288 46
        }
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294
    public function getIterator()
295
    {
296
        return new StatementIterator($this);
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302
    public function rowCount() : int
303
    {
304
        return sasql_stmt_affected_rows($this->stmt);
0 ignored issues
show
Bug introduced by
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

304
        return /** @scrutinizer ignore-call */ sasql_stmt_affected_rows($this->stmt);
Loading history...
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function setFetchMode($fetchMode, ...$args)
311
    {
312
        $this->defaultFetchMode = $fetchMode;
313
314
        if (isset($args[0])) {
315
            $this->defaultFetchClass = $args[0];
316
        }
317
318
        if (! isset($args[1])) {
319
            return;
320
        }
321
322
        $this->defaultFetchClassCtorArgs = (array) $args[1];
323
    }
324
325
    /**
326
     * Casts a stdClass object to the given class name mapping its' properties.
327
     *
328
     * @param stdClass      $sourceObject     Object to cast from.
329
     * @param string|object $destinationClass Name of the class or class instance to cast to.
330
     * @param mixed[]       $ctorArgs         Arguments to use for constructing the destination class instance.
331
     *
332
     * @return object
333
     *
334
     * @throws SQLAnywhereException
335
     */
336
    private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
337
    {
338
        if (! is_string($destinationClass)) {
339
            if (! is_object($destinationClass)) {
340
                throw new SQLAnywhereException(sprintf(
341
                    'Destination class has to be of type string or object, %s given.',
342
                    gettype($destinationClass)
343
                ));
344
            }
345
        } else {
346
            $destinationClass = new ReflectionClass($destinationClass);
347
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
348
        }
349
350
        $sourceReflection           = new ReflectionObject($sourceObject);
351
        $destinationClassReflection = new ReflectionObject($destinationClass);
352
353
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
354
            $sourceProperty->setAccessible(true);
355
356
            $name  = $sourceProperty->getName();
357
            $value = $sourceProperty->getValue($sourceObject);
358
359
            if ($destinationClassReflection->hasProperty($name)) {
360
                $destinationProperty = $destinationClassReflection->getProperty($name);
361
362
                $destinationProperty->setAccessible(true);
363
                $destinationProperty->setValue($destinationClass, $value);
364
            } else {
365
                $destinationClass->$name = $value;
366
            }
367
        }
368
369
        return $destinationClass;
370
    }
371
}
372