Failed Conditions
Push — master ( 656579...2742cd )
by Marco
11:55
created

SQLAnywhereStatement   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 313
Duplicated Lines 16.61 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 52
dl 52
loc 313
ccs 0
cts 173
cp 0
rs 7.9487
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A errorCode() 0 3 1
A fetchColumn() 9 9 3
A rowCount() 0 3 1
A setFetchMode() 0 5 3
A columnCount() 0 3 1
A errorInfo() 0 3 1
A getIterator() 0 3 1
A bindValue() 0 3 1
B fetchAll() 22 22 6
B execute() 9 19 6
A __construct() 0 11 3
A closeCursor() 0 7 2
C bindParam() 0 23 7
B castObject() 10 33 5
C fetch() 0 36 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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\StatementIterator;
23
use IteratorAggregate;
24
use PDO;
25
use Doctrine\DBAL\Driver\Statement;
26
27
/**
28
 * SAP SQL Anywhere implementation of the Statement interface.
29
 *
30
 * @author Steve Müller <[email protected]>
31
 * @link   www.doctrine-project.org
32
 * @since  2.5
33
 */
34
class SQLAnywhereStatement implements IteratorAggregate, Statement
35
{
36
    /**
37
     * @var resource The connection resource.
38
     */
39
    private $conn;
40
41
    /**
42
     * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
43
     */
44
    private $defaultFetchClass = '\stdClass';
45
46
    /**
47
     * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
48
     */
49
    private $defaultFetchClassCtorArgs = [];
50
51
    /**
52
     * @var int Default fetch mode to use.
53
     */
54
    private $defaultFetchMode = PDO::FETCH_BOTH;
55
56
    /**
57
     * @var resource The result set resource to fetch.
58
     */
59
    private $result;
60
61
    /**
62
     * @var resource The prepared SQL statement to execute.
63
     */
64
    private $stmt;
65
66
    /**
67
     * Constructor.
68
     *
69
     * Prepares given statement for given connection.
70
     *
71
     * @param resource $conn The connection resource to use.
72
     * @param string   $sql  The SQL statement to prepare.
73
     *
74
     * @throws SQLAnywhereException
75
     */
76
    public function __construct($conn, $sql)
77
    {
78
        if ( ! is_resource($conn)) {
79
            throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn);
80
        }
81
82
        $this->conn = $conn;
83
        $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

83
        $this->stmt = /** @scrutinizer ignore-call */ sasql_prepare($conn, $sql);
Loading history...
84
85
        if ( ! is_resource($this->stmt)) {
86
            throw SQLAnywhereException::fromSQLAnywhereError($conn);
87
        }
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     *
93
     * @throws SQLAnywhereException
94
     */
95
    public function bindParam($column, &$variable, $type = null, $length = null)
96
    {
97
        switch ($type) {
98
            case PDO::PARAM_INT:
99
            case PDO::PARAM_BOOL:
100
                $type = 'i';
101
                break;
102
            case PDO::PARAM_LOB:
103
                $type = 'b';
104
                break;
105
            case PDO::PARAM_NULL:
106
            case PDO::PARAM_STR:
107
                $type = 's';
108
                break;
109
            default:
110
                throw new SQLAnywhereException('Unknown type: ' . $type);
111
        }
112
113
        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

113
        if ( ! /** @scrutinizer ignore-call */ sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) {
Loading history...
114
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
115
        }
116
117
        return true;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function bindValue($param, $value, $type = null)
124
    {
125
        return $this->bindParam($param, $value, $type);
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     *
131
     * @throws SQLAnywhereException
132
     */
133
    public function closeCursor()
134
    {
135
        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

135
        if (!/** @scrutinizer ignore-call */ sasql_stmt_reset($this->stmt)) {
Loading history...
136
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
137
        }
138
139
        return true;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function columnCount()
146
    {
147
        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

147
        return /** @scrutinizer ignore-call */ sasql_stmt_field_count($this->stmt);
Loading history...
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function errorCode()
154
    {
155
        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

155
        return /** @scrutinizer ignore-call */ sasql_stmt_errno($this->stmt);
Loading history...
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function errorInfo()
162
    {
163
        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

163
        return /** @scrutinizer ignore-call */ sasql_stmt_error($this->stmt);
Loading history...
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     *
169
     * @throws SQLAnywhereException
170
     */
171
    public function execute($params = null)
172
    {
173 View Code Duplication
        if (is_array($params)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
174
            $hasZeroIndex = array_key_exists(0, $params);
175
176
            foreach ($params as $key => $val) {
177
                $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key;
178
179
                $this->bindValue($key, $val);
180
            }
181
        }
182
183
        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

183
        if ( ! /** @scrutinizer ignore-call */ sasql_stmt_execute($this->stmt)) {
Loading history...
184
            throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
185
        }
186
187
        $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

187
        $this->result = /** @scrutinizer ignore-call */ sasql_stmt_result_metadata($this->stmt);
Loading history...
188
189
        return true;
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     *
195
     * @throws SQLAnywhereException
196
     */
197
    public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
198
    {
199
        if ( ! is_resource($this->result)) {
200
            return false;
201
        }
202
203
        $fetchMode = $fetchMode ?: $this->defaultFetchMode;
204
205
        switch ($fetchMode) {
206
            case PDO::FETCH_ASSOC:
207
                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

207
                return /** @scrutinizer ignore-call */ sasql_fetch_assoc($this->result);
Loading history...
208
            case PDO::FETCH_BOTH:
209
                return sasql_fetch_array($this->result, SASQL_BOTH);
0 ignored issues
show
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

209
                return /** @scrutinizer ignore-call */ sasql_fetch_array($this->result, SASQL_BOTH);
Loading history...
Bug introduced by
The constant Doctrine\DBAL\Driver\SQLAnywhere\SASQL_BOTH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
210
            case PDO::FETCH_CLASS:
211
                $className = $this->defaultFetchClass;
212
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
213
214
                if (func_num_args() >= 2) {
215
                    $args      = func_get_args();
216
                    $className = $args[1];
217
                    $ctorArgs  = isset($args[2]) ? $args[2] : [];
218
                }
219
220
                $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

220
                $result = /** @scrutinizer ignore-call */ sasql_fetch_object($this->result);
Loading history...
221
222
                if ($result instanceof \stdClass) {
223
                    $result = $this->castObject($result, $className, $ctorArgs);
0 ignored issues
show
Bug introduced by
It seems like $ctorArgs can also be of type string; however, parameter $ctorArgs of Doctrine\DBAL\Driver\SQL...Statement::castObject() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
                    $result = $this->castObject($result, $className, /** @scrutinizer ignore-type */ $ctorArgs);
Loading history...
224
                }
225
226
                return $result;
227
            case PDO::FETCH_NUM:
228
                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

228
                return /** @scrutinizer ignore-call */ sasql_fetch_row($this->result);
Loading history...
229
            case PDO::FETCH_OBJ:
230
                return sasql_fetch_object($this->result);
231
            default:
232
                throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
233
        }
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239 View Code Duplication
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
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...
240
    {
241
        $rows = [];
242
243
        switch ($fetchMode) {
244
            case PDO::FETCH_CLASS:
245
                while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) {
246
                    $rows[] = $row;
247
                }
248
                break;
249
            case PDO::FETCH_COLUMN:
250
                while ($row = $this->fetchColumn()) {
251
                    $rows[] = $row;
252
                }
253
                break;
254
            default:
255
                while ($row = $this->fetch($fetchMode)) {
256
                    $rows[] = $row;
257
                }
258
        }
259
260
        return $rows;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266 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...
267
    {
268
        $row = $this->fetch(PDO::FETCH_NUM);
269
270
        if (false === $row) {
271
            return false;
272
        }
273
274
        return isset($row[$columnIndex]) ? $row[$columnIndex] : null;
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280
    public function getIterator()
281
    {
282
        return new StatementIterator($this);
283
    }
284
285
    /**
286
     * {@inheritdoc}
287
     */
288
    public function rowCount()
289
    {
290
        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

290
        return /** @scrutinizer ignore-call */ sasql_stmt_affected_rows($this->stmt);
Loading history...
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
297
    {
298
        $this->defaultFetchMode          = $fetchMode;
299
        $this->defaultFetchClass         = $arg2 ? $arg2 : $this->defaultFetchClass;
300
        $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...
301
    }
302
303
    /**
304
     * Casts a stdClass object to the given class name mapping its' properties.
305
     *
306
     * @param \stdClass     $sourceObject     Object to cast from.
307
     * @param string|object $destinationClass Name of the class or class instance to cast to.
308
     * @param array         $ctorArgs         Arguments to use for constructing the destination class instance.
309
     *
310
     * @return object
311
     *
312
     * @throws SQLAnywhereException
313
     */
314
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
315
    {
316 View Code Duplication
        if ( ! is_string($destinationClass)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
317
            if ( ! is_object($destinationClass)) {
318
                throw new SQLAnywhereException(sprintf(
319
                    'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
320
                ));
321
            }
322
        } else {
323
            $destinationClass = new \ReflectionClass($destinationClass);
324
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
325
        }
326
327
        $sourceReflection           = new \ReflectionObject($sourceObject);
328
        $destinationClassReflection = new \ReflectionObject($destinationClass);
0 ignored issues
show
Bug introduced by
It seems like $destinationClass can also be of type string; however, parameter $argument of ReflectionObject::__construct() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
        $destinationClassReflection = new \ReflectionObject(/** @scrutinizer ignore-type */ $destinationClass);
Loading history...
329
330
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
331
            $sourceProperty->setAccessible(true);
332
333
            $name  = $sourceProperty->getName();
334
            $value = $sourceProperty->getValue($sourceObject);
335
336
            if ($destinationClassReflection->hasProperty($name)) {
337
                $destinationProperty = $destinationClassReflection->getProperty($name);
338
339
                $destinationProperty->setAccessible(true);
340
                $destinationProperty->setValue($destinationClass, $value);
0 ignored issues
show
Unused Code introduced by
The call to ReflectionProperty::setValue() has too many arguments starting with $value. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

340
                $destinationProperty->/** @scrutinizer ignore-call */ 
341
                                      setValue($destinationClass, $value);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
341
            } else {
342
                $destinationClass->$name = $value;
343
            }
344
        }
345
346
        return $destinationClass;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $destinationClass also could return the type string which is incompatible with the documented return type object.
Loading history...
347
    }
348
}
349