Passed
Push — master ( 437ad0...319e20 )
by Marco
13:56
created

DB2Statement::rowCount()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 3
cp 0.6667
rs 10
c 0
b 0
f 0
cc 2
eloc 1
nc 1
nop 0
crap 2.1481
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 db2_bind_param;
31
use function db2_execute;
32
use function db2_fetch_array;
33
use function db2_fetch_assoc;
34
use function db2_fetch_both;
35
use function db2_fetch_object;
36
use function db2_free_result;
37
use function db2_num_fields;
38
use function db2_num_rows;
39
use function db2_stmt_error;
40
use function db2_stmt_errormsg;
41
use function func_get_args;
42
use function func_num_args;
43
use function gettype;
44
use function is_object;
45
use function is_string;
46
use function ksort;
47
use function sprintf;
48
use function strtolower;
49
50
class DB2Statement implements \IteratorAggregate, Statement
51
{
52
    /**
53
     * @var resource
54
     */
55
    private $_stmt;
56
57
    /**
58
     * @var array
59
     */
60
    private $_bindParam = [];
61
62
    /**
63
     * @var string Name of the default class to instantiate when fetching class instances.
64
     */
65
    private $defaultFetchClass = '\stdClass';
66
67
    /**
68
     * @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances.
69
     */
70
    private $defaultFetchClassCtorArgs = [];
71
72
    /**
73
     * @var int
74
     */
75
    private $_defaultFetchMode = FetchMode::MIXED;
76
77
    /**
78
     * Indicates whether the statement is in the state when fetching results is possible
79
     *
80
     * @var bool
81
     */
82
    private $result = false;
83
84
    /**
85
     * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG
86
     *
87
     * @var array
88
     */
89
    static private $_typeMap = [
90
        ParameterType::INTEGER => DB2_LONG,
91
        ParameterType::STRING  => DB2_CHAR,
92
    ];
93
94
    /**
95
     * @param resource $stmt
96
     */
97 223
    public function __construct($stmt)
98
    {
99 223
        $this->_stmt = $stmt;
100 223
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 34
    public function bindValue($param, $value, $type = ParameterType::STRING)
106
    {
107 34
        return $this->bindParam($param, $value, $type);
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 41
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
114
    {
115 41
        $this->_bindParam[$column] =& $variable;
116
117 41
        if ($type && isset(self::$_typeMap[$type])) {
118 39
            $type = self::$_typeMap[$type];
119
        } else {
120 5
            $type = DB2_CHAR;
121
        }
122
123 41
        if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
124
            throw new DB2Exception(db2_stmt_errormsg());
125
        }
126
127 41
        return true;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 19
    public function closeCursor()
134
    {
135 19
        if ( ! $this->_stmt) {
136
            return false;
137
        }
138
139 19
        $this->_bindParam = [];
140
141 19
        if (!db2_free_result($this->_stmt)) {
142
            return false;
143
        }
144
145 19
        $this->result = false;
146
147 19
        return true;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 4
    public function columnCount()
154
    {
155 4
        if ( ! $this->_stmt) {
156
            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...
157
        }
158
159 4
        return db2_num_fields($this->_stmt);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function errorCode()
166
    {
167
        return db2_stmt_error();
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function errorInfo()
174
    {
175
        return [
176
            db2_stmt_errormsg(),
177
            db2_stmt_error(),
178
        ];
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184 215
    public function execute($params = null)
185
    {
186 215
        if ( ! $this->_stmt) {
187
            return false;
188
        }
189
190 215
        if ($params === null) {
191 192
            ksort($this->_bindParam);
192
193 192
            $params = [];
194
195 192
            foreach ($this->_bindParam as $column => $value) {
196 41
                $params[] = $value;
197
            }
198
        }
199
200 215
        $retval = db2_execute($this->_stmt, $params);
201
202 210
        if ($retval === false) {
203
            throw new DB2Exception(db2_stmt_errormsg());
204
        }
205
206 210
        $this->result = true;
207
208 210
        return $retval;
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214 214
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
215
    {
216 214
        $this->_defaultFetchMode         = $fetchMode;
217 214
        $this->defaultFetchClass         = $arg2 ?: $this->defaultFetchClass;
218 214
        $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
219
220 214
        return true;
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226 3
    public function getIterator()
227
    {
228 3
        return new StatementIterator($this);
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234 201
    public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
235
    {
236
        // do not try fetching from the statement if it's not expected to contain result
237
        // in order to prevent exceptional situation
238 201
        if (!$this->result) {
239 9
            return false;
240
        }
241
242 192
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;
243
        switch ($fetchMode) {
244 192
            case FetchMode::COLUMN:
245 1
                return $this->fetchColumn();
246
247 192
            case FetchMode::MIXED:
248 2
                return db2_fetch_both($this->_stmt);
249
250 190
            case FetchMode::ASSOCIATIVE:
251 135
                return db2_fetch_assoc($this->_stmt);
252
253 57
            case FetchMode::CUSTOM_OBJECT:
254 3
                $className = $this->defaultFetchClass;
255 3
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
256
257 3
                if (func_num_args() >= 2) {
258 1
                    $args      = func_get_args();
259 1
                    $className = $args[1];
260 1
                    $ctorArgs  = $args[2] ?? [];
261
                }
262
263 3
                $result = db2_fetch_object($this->_stmt);
264
265 3
                if ($result instanceof \stdClass) {
266 3
                    $result = $this->castObject($result, $className, $ctorArgs);
267
                }
268
269 3
                return $result;
270
271 54
            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...
272 53
                return db2_fetch_array($this->_stmt);
273
274 1
            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...
275 1
                return db2_fetch_object($this->_stmt);
276
277
            default:
278
                throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
279
        }
280
    }
281
282
    /**
283
     * {@inheritdoc}
284
     */
285 89
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
286
    {
287 89
        $rows = [];
288
289
        switch ($fetchMode) {
290 89
            case FetchMode::CUSTOM_OBJECT:
291 1
                while (($row = $this->fetch(...func_get_args())) !== false) {
292 1
                    $rows[] = $row;
293
                }
294 1
                break;
295 88
            case FetchMode::COLUMN:
296 4
                while (($row = $this->fetchColumn()) !== false) {
297 4
                    $rows[] = $row;
298
                }
299 4
                break;
300
            default:
301 84
                while (($row = $this->fetch($fetchMode)) !== false) {
302 77
                    $rows[] = $row;
303
                }
304
        }
305
306 89
        return $rows;
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312 50
    public function fetchColumn($columnIndex = 0)
313
    {
314 50
        $row = $this->fetch(FetchMode::NUMERIC);
315
316 50
        if (false === $row) {
317 10
            return false;
318
        }
319
320 44
        return $row[$columnIndex] ?? null;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326 83
    public function rowCount()
327
    {
328 83
        return (@db2_num_rows($this->_stmt)) ? : 0;
329
    }
330
331
    /**
332
     * Casts a stdClass object to the given class name mapping its' properties.
333
     *
334
     * @param \stdClass     $sourceObject     Object to cast from.
335
     * @param string|object $destinationClass Name of the class or class instance to cast to.
336
     * @param array         $ctorArgs         Arguments to use for constructing the destination class instance.
337
     *
338
     * @return object
339
     *
340
     * @throws DB2Exception
341
     */
342 3
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
343
    {
344 3
        if ( ! is_string($destinationClass)) {
345
            if ( ! is_object($destinationClass)) {
346
                throw new DB2Exception(sprintf(
347
                    'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
348
                ));
349
            }
350
        } else {
351 3
            $destinationClass = new \ReflectionClass($destinationClass);
352 3
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
353
        }
354
355 3
        $sourceReflection           = new \ReflectionObject($sourceObject);
356 3
        $destinationClassReflection = new \ReflectionObject($destinationClass);
357
        /** @var \ReflectionProperty[] $destinationProperties */
358 3
        $destinationProperties      = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER);
359
360 3
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
361 3
            $sourceProperty->setAccessible(true);
362
363 3
            $name  = $sourceProperty->getName();
364 3
            $value = $sourceProperty->getValue($sourceObject);
365
366
            // Try to find a case-matching property.
367 3
            if ($destinationClassReflection->hasProperty($name)) {
368
                $destinationProperty = $destinationClassReflection->getProperty($name);
369
370
                $destinationProperty->setAccessible(true);
371
                $destinationProperty->setValue($destinationClass, $value);
372
373
                continue;
374
            }
375
376 3
            $name = strtolower($name);
377
378
            // Try to find a property without matching case.
379
            // Fallback for the driver returning either all uppercase or all lowercase column names.
380 3
            if (isset($destinationProperties[$name])) {
381
                $destinationProperty = $destinationProperties[$name];
382
383
                $destinationProperty->setAccessible(true);
384
                $destinationProperty->setValue($destinationClass, $value);
385
386
                continue;
387
            }
388
389 3
            $destinationClass->$name = $value;
390
        }
391
392 3
        return $destinationClass;
393
    }
394
}
395