Failed Conditions
Push — phpstan ( 752f9f...1acc4a )
by Michael
21:57
created

DB2Statement::columnCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
ccs 0
cts 6
cp 0
cc 2
nc 2
nop 0
crap 6
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\IBMDB2;
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 const DB2_CHAR;
27
use const DB2_LONG;
28
use const DB2_PARAM_IN;
29
use function array_change_key_case;
30
use function call_user_func_array;
31
use function db2_bind_param;
32
use function db2_execute;
33
use function db2_fetch_array;
34
use function db2_fetch_assoc;
35
use function db2_fetch_both;
36
use function db2_fetch_object;
37
use function db2_free_result;
38
use function db2_num_fields;
39
use function db2_num_rows;
40
use function db2_stmt_error;
41
use function db2_stmt_errormsg;
42
use function func_get_args;
43
use function func_num_args;
44
use function gettype;
45
use function is_object;
46
use function is_string;
47
use function ksort;
48
use function sprintf;
49
use function strtolower;
50
51
class DB2Statement implements \IteratorAggregate, Statement
52
{
53
    /**
54
     * @var resource
55
     */
56
    private $_stmt;
57
58
    /**
59
     * @var array
60
     */
61
    private $_bindParam = [];
62
63
    /**
64
     * @var string Name of the default class to instantiate when fetching class instances.
65
     */
66
    private $defaultFetchClass = '\stdClass';
67
68
    /**
69
     * @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances.
70
     */
71
    private $defaultFetchClassCtorArgs = [];
72
73
    /**
74
     * @var int
75
     */
76
    private $_defaultFetchMode = FetchMode::MIXED;
77
78
    /**
79
     * Indicates whether the statement is in the state when fetching results is possible
80
     *
81
     * @var bool
82
     */
83
    private $result = false;
84
85
    /**
86
     * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG
87
     *
88
     * @var array
89
     */
90
    static private $_typeMap = [
91
        ParameterType::INTEGER => DB2_LONG,
92
        ParameterType::STRING  => DB2_CHAR,
93
    ];
94
95
    /**
96
     * @param resource $stmt
97
     */
98
    public function __construct($stmt)
99
    {
100
        $this->_stmt = $stmt;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function bindValue($param, $value, $type = ParameterType::STRING)
107
    {
108
        return $this->bindParam($param, $value, $type);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
115
    {
116
        $this->_bindParam[$column] =& $variable;
117
118
        if ($type && isset(self::$_typeMap[$type])) {
119
            $type = self::$_typeMap[$type];
120
        } else {
121
            $type = DB2_CHAR;
122
        }
123
124
        if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
125
            throw new DB2Exception(db2_stmt_errormsg());
126
        }
127
128
        return true;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function closeCursor()
135
    {
136
        if ( ! $this->_stmt) {
137
            return false;
138
        }
139
140
        $this->_bindParam = [];
141
142
        if (!db2_free_result($this->_stmt)) {
143
            return false;
144
        }
145
146
        $this->result = false;
147
148
        return true;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function columnCount()
155
    {
156
        if ( ! $this->_stmt) {
157
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Res...tatement::columnCount() of integer.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
158
        }
159
160
        return db2_num_fields($this->_stmt);
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function errorCode()
167
    {
168
        return db2_stmt_error();
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function errorInfo()
175
    {
176
        return [
177
            db2_stmt_errormsg(),
178
            db2_stmt_error(),
179
        ];
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185
    public function execute($params = null)
186
    {
187
        if ( ! $this->_stmt) {
188
            return false;
189
        }
190
191
        if ($params === null) {
192
            ksort($this->_bindParam);
193
194
            $params = [];
195
196
            foreach ($this->_bindParam as $column => $value) {
197
                $params[] = $value;
198
            }
199
        }
200
201
        $retval = db2_execute($this->_stmt, $params);
202
203
        if ($retval === false) {
204
            throw new DB2Exception(db2_stmt_errormsg());
205
        }
206
207
        $this->result = true;
208
209
        return $retval;
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
216
    {
217
        $this->_defaultFetchMode         = $fetchMode;
218
        $this->defaultFetchClass         = $arg2 ? $arg2 : $this->defaultFetchClass;
219
        $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
220
221
        return true;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    public function getIterator()
228
    {
229
        return new StatementIterator($this);
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
236
    {
237
        // do not try fetching from the statement if it's not expected to contain result
238
        // in order to prevent exceptional situation
239
        if (!$this->result) {
240
            return false;
241
        }
242
243
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;
244
        switch ($fetchMode) {
245
            case FetchMode::COLUMN:
246
                return $this->fetchColumn();
247
248
            case FetchMode::MIXED:
249
                return db2_fetch_both($this->_stmt);
250
251
            case FetchMode::ASSOCIATIVE:
252
                return db2_fetch_assoc($this->_stmt);
253
254
            case FetchMode::CUSTOM_OBJECT:
255
                $className = $this->defaultFetchClass;
256
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
257
258
                if (func_num_args() >= 2) {
259
                    $args      = func_get_args();
260
                    $className = $args[1];
261
                    $ctorArgs  = $args[2] ?? [];
262
                }
263
264
                $result = db2_fetch_object($this->_stmt);
265
266
                if ($result instanceof \stdClass) {
267
                    $result = $this->castObject($result, $className, $ctorArgs);
268
                }
269
270
                return $result;
271
272
            case FetchMode::NUMERIC:
273
                return db2_fetch_array($this->_stmt);
274
275
            case FetchMode::STANDARD_OBJECT:
276
                return db2_fetch_object($this->_stmt);
277
278
            default:
279
                throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
280
        }
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
287
    {
288
        $rows = [];
289
290
        switch ($fetchMode) {
291
            case FetchMode::CUSTOM_OBJECT:
292
                while (($row = $this->fetch(...func_get_args())) !== false) {
293
                    $rows[] = $row;
294
                }
295
                break;
296
            case FetchMode::COLUMN:
297
                while (($row = $this->fetchColumn()) !== false) {
298
                    $rows[] = $row;
299
                }
300
                break;
301
            default:
302
                while (($row = $this->fetch($fetchMode)) !== false) {
303
                    $rows[] = $row;
304
                }
305
        }
306
307
        return $rows;
308
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313
    public function fetchColumn($columnIndex = 0)
314
    {
315
        $row = $this->fetch(FetchMode::NUMERIC);
316
317
        if (false === $row) {
318
            return false;
319
        }
320
321
        return $row[$columnIndex] ?? null;
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327
    public function rowCount()
328
    {
329
        return (@db2_num_rows($this->_stmt)) ? : 0;
330
    }
331
332
    /**
333
     * Casts a stdClass object to the given class name mapping its' properties.
334
     *
335
     * @param \stdClass     $sourceObject     Object to cast from.
336
     * @param string|object $destinationClass Name of the class or class instance to cast to.
337
     * @param array         $ctorArgs         Arguments to use for constructing the destination class instance.
338
     *
339
     * @return object
340
     *
341
     * @throws DB2Exception
342
     */
343
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
344
    {
345
        if ( ! is_string($destinationClass)) {
346
            if ( ! is_object($destinationClass)) {
347
                throw new DB2Exception(sprintf(
348
                    'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
349
                ));
350
            }
351
        } else {
352
            $destinationClass = new \ReflectionClass($destinationClass);
353
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
354
        }
355
356
        $sourceReflection           = new \ReflectionObject($sourceObject);
357
        $destinationClassReflection = new \ReflectionObject($destinationClass);
358
        /** @var \ReflectionProperty[] $destinationProperties */
359
        $destinationProperties      = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER);
360
361
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
362
            $sourceProperty->setAccessible(true);
363
364
            $name  = $sourceProperty->getName();
365
            $value = $sourceProperty->getValue($sourceObject);
366
367
            // Try to find a case-matching property.
368
            if ($destinationClassReflection->hasProperty($name)) {
369
                $destinationProperty = $destinationClassReflection->getProperty($name);
370
371
                $destinationProperty->setAccessible(true);
372
                $destinationProperty->setValue($destinationClass, $value);
373
374
                continue;
375
            }
376
377
            $name = strtolower($name);
378
379
            // Try to find a property without matching case.
380
            // Fallback for the driver returning either all uppercase or all lowercase column names.
381
            if (isset($destinationProperties[$name])) {
382
                $destinationProperty = $destinationProperties[$name];
383
384
                $destinationProperty->setAccessible(true);
385
                $destinationProperty->setValue($destinationClass, $value);
386
387
                continue;
388
            }
389
390
            $destinationClass->$name = $value;
391
        }
392
393
        return $destinationClass;
394
    }
395
}
396