Passed
Push — allow-sf4-console ( abb17e...64a32a )
by Michael
252:50 queued 248:44
created

DB2Statement::errorCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 3
cp 0
cc 1
nc 1
nop 0
crap 2
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 223
    public function __construct($stmt)
99
    {
100 223
        $this->_stmt = $stmt;
101 223
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 34
    public function bindValue($param, $value, $type = ParameterType::STRING)
107
    {
108 34
        return $this->bindParam($param, $value, $type);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 41
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
115
    {
116 41
        $this->_bindParam[$column] =& $variable;
117
118 41
        if ($type && isset(self::$_typeMap[$type])) {
119 39
            $type = self::$_typeMap[$type];
120
        } else {
121 5
            $type = DB2_CHAR;
122
        }
123
124 41
        if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
125
            throw new DB2Exception(db2_stmt_errormsg());
126
        }
127
128 41
        return true;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 19
    public function closeCursor()
135
    {
136 19
        if ( ! $this->_stmt) {
137
            return false;
138
        }
139
140 19
        $this->_bindParam = [];
141
142 19
        if (!db2_free_result($this->_stmt)) {
143
            return false;
144
        }
145
146 19
        $this->result = false;
147
148 19
        return true;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 4
    public function columnCount()
155
    {
156 4
        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 4
        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 215
    public function execute($params = null)
186
    {
187 215
        if ( ! $this->_stmt) {
188
            return false;
189
        }
190
191 215
        if ($params === null) {
192 192
            ksort($this->_bindParam);
193
194 192
            $params = [];
195
196 192
            foreach ($this->_bindParam as $column => $value) {
197 41
                $params[] = $value;
198
            }
199
        }
200
201 215
        $retval = db2_execute($this->_stmt, $params);
202
203 210
        if ($retval === false) {
204
            throw new DB2Exception(db2_stmt_errormsg());
205
        }
206
207 210
        $this->result = true;
208
209 210
        return $retval;
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215 214
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
216
    {
217 214
        $this->_defaultFetchMode         = $fetchMode;
218 214
        $this->defaultFetchClass         = $arg2 ?: $this->defaultFetchClass;
219 214
        $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
220
221 214
        return true;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 3
    public function getIterator()
228
    {
229 3
        return new StatementIterator($this);
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235 201
    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 201
        if (!$this->result) {
240 9
            return false;
241
        }
242
243 192
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;
244
        switch ($fetchMode) {
245 192
            case FetchMode::COLUMN:
246 1
                return $this->fetchColumn();
247
248 192
            case FetchMode::MIXED:
249 2
                return db2_fetch_both($this->_stmt);
250
251 190
            case FetchMode::ASSOCIATIVE:
252 135
                return db2_fetch_assoc($this->_stmt);
253
254 57
            case FetchMode::CUSTOM_OBJECT:
255 3
                $className = $this->defaultFetchClass;
256 3
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
257
258 3
                if (func_num_args() >= 2) {
259 1
                    $args      = func_get_args();
260 1
                    $className = $args[1];
261 1
                    $ctorArgs  = $args[2] ?? [];
262
                }
263
264 3
                $result = db2_fetch_object($this->_stmt);
265
266 3
                if ($result instanceof \stdClass) {
267 3
                    $result = $this->castObject($result, $className, $ctorArgs);
268
                }
269
270 3
                return $result;
271
272 54
            case FetchMode::NUMERIC:
273 53
                return db2_fetch_array($this->_stmt);
274
275 1
            case FetchMode::STANDARD_OBJECT:
276 1
                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 89
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
287
    {
288 89
        $rows = [];
289
290
        switch ($fetchMode) {
291 89
            case FetchMode::CUSTOM_OBJECT:
292 1
                while (($row = $this->fetch(...func_get_args())) !== false) {
293 1
                    $rows[] = $row;
294
                }
295 1
                break;
296 88
            case FetchMode::COLUMN:
297 4
                while (($row = $this->fetchColumn()) !== false) {
298 4
                    $rows[] = $row;
299
                }
300 4
                break;
301
            default:
302 84
                while (($row = $this->fetch($fetchMode)) !== false) {
303 77
                    $rows[] = $row;
304
                }
305
        }
306
307 89
        return $rows;
308
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313 50
    public function fetchColumn($columnIndex = 0)
314
    {
315 50
        $row = $this->fetch(FetchMode::NUMERIC);
316
317 50
        if (false === $row) {
318 10
            return false;
319
        }
320
321 44
        return $row[$columnIndex] ?? null;
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327 83
    public function rowCount()
328
    {
329 83
        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 3
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
344
    {
345 3
        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 3
            $destinationClass = new \ReflectionClass($destinationClass);
353 3
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
354
        }
355
356 3
        $sourceReflection           = new \ReflectionObject($sourceObject);
357 3
        $destinationClassReflection = new \ReflectionObject($destinationClass);
358
        /** @var \ReflectionProperty[] $destinationProperties */
359 3
        $destinationProperties      = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER);
360
361 3
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
362 3
            $sourceProperty->setAccessible(true);
363
364 3
            $name  = $sourceProperty->getName();
365 3
            $value = $sourceProperty->getValue($sourceObject);
366
367
            // Try to find a case-matching property.
368 3
            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 3
            $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 3
            if (isset($destinationProperties[$name])) {
382
                $destinationProperty = $destinationProperties[$name];
383
384
                $destinationProperty->setAccessible(true);
385
                $destinationProperty->setValue($destinationClass, $value);
386
387
                continue;
388
            }
389
390 3
            $destinationClass->$name = $value;
391
        }
392
393 3
        return $destinationClass;
394
    }
395
}
396