Passed
Push — 2.7 ( bd5e19...0d26c7 )
by Sergei
14:54
created

SQLAnywhereStatement   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 326
Duplicated Lines 0 %

Test Coverage

Coverage 1.68%

Importance

Changes 0
Metric Value
wmc 51
dl 0
loc 326
ccs 2
cts 118
cp 0.0168
rs 8.3206
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A errorCode() 0 3 1
A fetchColumn() 0 9 2
A rowCount() 0 3 1
A columnCount() 0 3 1
A closeCursor() 0 7 2
B execute() 0 19 6
C fetch() 0 44 11
A setFetchMode() 0 5 3
A __construct() 0 11 3
A errorInfo() 0 3 1
A getIterator() 0 3 1
A bindValue() 0 3 1
B fetchAll() 0 24 6
C bindParam() 0 26 7
B castObject() 0 33 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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Driver\SQLAnywhere;
21
22
use Doctrine\DBAL\Driver\Statement;
23
use Doctrine\DBAL\Driver\StatementIterator;
24
use Doctrine\DBAL\FetchMode;
25
use Doctrine\DBAL\ParameterType;
26
use IteratorAggregate;
27
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...
28
use function array_key_exists;
29
use function call_user_func_array;
30
use function func_get_args;
31
use function func_num_args;
32
use function gettype;
33
use function is_array;
34
use function is_numeric;
35
use function is_object;
36
use function is_resource;
37
use function is_string;
38
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...
39
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...
40
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...
41
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...
42
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...
43
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...
44
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...
45
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...
46
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...
47
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...
48
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...
49
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...
50
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...
51
use function sprintf;
52
53
/**
54
 * SAP SQL Anywhere implementation of the Statement interface.
55
 *
56
 * @author Steve Müller <[email protected]>
57
 * @link   www.doctrine-project.org
58
 * @since  2.5
59
 */
60
class SQLAnywhereStatement implements IteratorAggregate, Statement
61
{
62
    /**
63
     * @var resource The connection resource.
64
     */
65
    private $conn;
66
67
    /**
68
     * @var string Name of the default class to instantiate when fetching class instances.
69
     */
70
    private $defaultFetchClass = '\stdClass';
71
72
    /**
73
     * @var string Constructor arguments for the default class to instantiate when fetching class instances.
74
     */
75
    private $defaultFetchClassCtorArgs = [];
76
77
    /**
78
     * @var int Default fetch mode to use.
79
     */
80
    private $defaultFetchMode = FetchMode::MIXED;
81
82
    /**
83
     * @var resource The result set resource to fetch.
84
     */
85
    private $result;
86
87
    /**
88
     * @var resource The prepared SQL statement to execute.
89
     */
90
    private $stmt;
91
92
    /**
93
     * Constructor.
94
     *
95
     * Prepares given statement for given connection.
96
     *
97
     * @param resource $conn The connection resource to use.
98
     * @param string   $sql  The SQL statement to prepare.
99
     *
100
     * @throws SQLAnywhereException
101
     */
102
    public function __construct($conn, $sql)
103
    {
104
        if ( ! is_resource($conn)) {
105
            throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn);
106
        }
107
108
        $this->conn = $conn;
109
        $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

109
        $this->stmt = /** @scrutinizer ignore-call */ sasql_prepare($conn, $sql);
Loading history...
110
111
        if ( ! is_resource($this->stmt)) {
112
            throw SQLAnywhereException::fromSQLAnywhereError($conn);
113
        }
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     *
119
     * @throws SQLAnywhereException
120
     */
121
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
122
    {
123
        switch ($type) {
124
            case ParameterType::INTEGER:
125
            case ParameterType::BOOLEAN:
126
                $type = 'i';
127
                break;
128
129
            case ParameterType::LARGE_OBJECT:
130
                $type = 'b';
131
                break;
132
133
            case ParameterType::NULL:
134
            case ParameterType::STRING:
135
                $type = 's';
136
                break;
137
138
            default:
139
                throw new SQLAnywhereException('Unknown type: ' . $type);
140
        }
141
142
        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

142
        if ( ! /** @scrutinizer ignore-call */ sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) {
Loading history...
143
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
144
        }
145
146
        return true;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function bindValue($param, $value, $type = ParameterType::STRING)
153
    {
154
        return $this->bindParam($param, $value, $type);
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     *
160
     * @throws SQLAnywhereException
161
     */
162
    public function closeCursor()
163
    {
164
        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

164
        if (!/** @scrutinizer ignore-call */ sasql_stmt_reset($this->stmt)) {
Loading history...
165
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
166
        }
167
168
        return true;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function columnCount()
175
    {
176
        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

176
        return /** @scrutinizer ignore-call */ sasql_stmt_field_count($this->stmt);
Loading history...
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function errorCode()
183
    {
184
        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

184
        return /** @scrutinizer ignore-call */ sasql_stmt_errno($this->stmt);
Loading history...
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    public function errorInfo()
191
    {
192
        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

192
        return /** @scrutinizer ignore-call */ sasql_stmt_error($this->stmt);
Loading history...
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     *
198
     * @throws SQLAnywhereException
199
     */
200
    public function execute($params = null)
201
    {
202
        if (is_array($params)) {
203
            $hasZeroIndex = array_key_exists(0, $params);
204
205
            foreach ($params as $key => $val) {
206
                $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key;
207
208
                $this->bindValue($key, $val);
209
            }
210
        }
211
212
        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

212
        if ( ! /** @scrutinizer ignore-call */ sasql_stmt_execute($this->stmt)) {
Loading history...
213
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
214
        }
215
216
        $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

216
        $this->result = /** @scrutinizer ignore-call */ sasql_stmt_result_metadata($this->stmt);
Loading history...
217
218
        return true;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     *
224
     * @throws SQLAnywhereException
225
     */
226
    public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
227
    {
228
        if ( ! is_resource($this->result)) {
229
            return false;
230
        }
231
232
        $fetchMode = $fetchMode ?: $this->defaultFetchMode;
233
234
        switch ($fetchMode) {
235
            case FetchMode::COLUMN:
236
                return $this->fetchColumn();
237
238
            case FetchMode::ASSOCIATIVE:
239
                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

239
                return /** @scrutinizer ignore-call */ sasql_fetch_assoc($this->result);
Loading history...
240
241
            case FetchMode::MIXED:
242
                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

242
                return /** @scrutinizer ignore-call */ sasql_fetch_array($this->result, SASQL_BOTH);
Loading history...
243
244
            case FetchMode::CUSTOM_OBJECT:
245
                $className = $this->defaultFetchClass;
246
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
247
248
                if (func_num_args() >= 2) {
249
                    $args      = func_get_args();
250
                    $className = $args[1];
251
                    $ctorArgs  = $args[2] ?? [];
252
                }
253
254
                $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

254
                $result = /** @scrutinizer ignore-call */ sasql_fetch_object($this->result);
Loading history...
255
256
                if ($result instanceof \stdClass) {
257
                    $result = $this->castObject($result, $className, $ctorArgs);
258
                }
259
260
                return $result;
261
262
            case FetchMode::NUMERIC:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
263
                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

263
                return /** @scrutinizer ignore-call */ sasql_fetch_row($this->result);
Loading history...
264
265
            case FetchMode::STANDARD_OBJECT:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
266
                return sasql_fetch_object($this->result);
267
268
            default:
269
                throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
270
        }
271
    }
272
273
    /**
274
     * {@inheritdoc}
275
     */
276
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
277
    {
278
        $rows = [];
279
280
        switch ($fetchMode) {
281
            case FetchMode::CUSTOM_OBJECT:
282
                while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) {
283
                    $rows[] = $row;
284
                }
285
                break;
286
287
            case FetchMode::COLUMN:
288
                while ($row = $this->fetchColumn()) {
289
                    $rows[] = $row;
290
                }
291
                break;
292
293
            default:
294
                while ($row = $this->fetch($fetchMode)) {
295
                    $rows[] = $row;
296
                }
297
        }
298
299
        return $rows;
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function fetchColumn($columnIndex = 0)
306
    {
307
        $row = $this->fetch(FetchMode::NUMERIC);
308
309
        if (false === $row) {
310
            return false;
311
        }
312
313
        return $row[$columnIndex] ?? null;
314
    }
315
316
    /**
317
     * {@inheritdoc}
318
     */
319 2
    public function getIterator()
320
    {
321 2
        return new StatementIterator($this);
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327
    public function rowCount()
328
    {
329
        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

329
        return /** @scrutinizer ignore-call */ sasql_stmt_affected_rows($this->stmt);
Loading history...
330
    }
331
332
    /**
333
     * {@inheritdoc}
334
     */
335
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
336
    {
337
        $this->defaultFetchMode          = $fetchMode;
338
        $this->defaultFetchClass         = $arg2 ? $arg2 : $this->defaultFetchClass;
339
        $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
0 ignored issues
show
Documentation Bug introduced by
It seems like $arg3 ? (array)$arg3 : $...faultFetchClassCtorArgs can also be of type array. However, the property $defaultFetchClassCtorArgs is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
340
    }
341
342
    /**
343
     * Casts a stdClass object to the given class name mapping its' properties.
344
     *
345
     * @param \stdClass     $sourceObject     Object to cast from.
346
     * @param string|object $destinationClass Name of the class or class instance to cast to.
347
     * @param array         $ctorArgs         Arguments to use for constructing the destination class instance.
348
     *
349
     * @return object
350
     *
351
     * @throws SQLAnywhereException
352
     */
353
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
354
    {
355
        if ( ! is_string($destinationClass)) {
356
            if ( ! is_object($destinationClass)) {
357
                throw new SQLAnywhereException(sprintf(
358
                    'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
359
                ));
360
            }
361
        } else {
362
            $destinationClass = new \ReflectionClass($destinationClass);
363
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
364
        }
365
366
        $sourceReflection           = new \ReflectionObject($sourceObject);
367
        $destinationClassReflection = new \ReflectionObject($destinationClass);
368
369
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
370
            $sourceProperty->setAccessible(true);
371
372
            $name  = $sourceProperty->getName();
373
            $value = $sourceProperty->getValue($sourceObject);
374
375
            if ($destinationClassReflection->hasProperty($name)) {
376
                $destinationProperty = $destinationClassReflection->getProperty($name);
377
378
                $destinationProperty->setAccessible(true);
379
                $destinationProperty->setValue($destinationClass, $value);
380
            } else {
381
                $destinationClass->$name = $value;
382
            }
383
        }
384
385
        return $destinationClass;
386
    }
387
}
388