Failed Conditions
Push — master ( edfbda...298c91 )
by Luís
16s
created

lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php (6 issues)

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
25
class DB2Statement implements \IteratorAggregate, Statement
26
{
27
    /**
28
     * @var resource
29
     */
30
    private $_stmt;
31
32
    /**
33
     * @var array
34
     */
35
    private $_bindParam = [];
36
37
    /**
38
     * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
39
     */
40
    private $defaultFetchClass = '\stdClass';
41
42
    /**
43
     * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
44
     */
45
    private $defaultFetchClassCtorArgs = [];
46
47
    /**
48
     * @var integer
49
     */
50
    private $_defaultFetchMode = \PDO::FETCH_BOTH;
51
52
    /**
53
     * Indicates whether the statement is in the state when fetching results is possible
54
     *
55
     * @var bool
56
     */
57
    private $result = false;
58
59
    /**
60
     * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG
61
     *
62
     * @var array
63
     */
64
    static private $_typeMap = [
65
        \PDO::PARAM_INT => DB2_LONG,
66
        \PDO::PARAM_STR => DB2_CHAR,
67
    ];
68
69
    /**
70
     * @param resource $stmt
71
     */
72
    public function __construct($stmt)
73
    {
74
        $this->_stmt = $stmt;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function bindValue($param, $value, $type = null)
81
    {
82
        return $this->bindParam($param, $value, $type);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function bindParam($column, &$variable, $type = null, $length = null)
89
    {
90
        $this->_bindParam[$column] =& $variable;
91
92
        if ($type && isset(self::$_typeMap[$type])) {
93
            $type = self::$_typeMap[$type];
94
        } else {
95
            $type = DB2_CHAR;
96
        }
97
98
        if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
99
            throw new DB2Exception(db2_stmt_errormsg());
100
        }
101
102
        return true;
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function closeCursor()
109
    {
110
        if ( ! $this->_stmt) {
111
            return false;
112
        }
113
114
        $this->_bindParam = [];
115
116
        if (!db2_free_result($this->_stmt)) {
117
            return false;
118
        }
119
120
        $this->result = false;
121
122
        return true;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function columnCount()
129
    {
130
        if ( ! $this->_stmt) {
131
            return false;
132
        }
133
134
        return db2_num_fields($this->_stmt);
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function errorCode()
141
    {
142
        return db2_stmt_error();
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    public function errorInfo()
149
    {
150
        return [
151
            db2_stmt_errormsg(),
152
            db2_stmt_error(),
153
        ];
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function execute($params = null)
160
    {
161
        if ( ! $this->_stmt) {
162
            return false;
163
        }
164
165
        if ($params === null) {
166
            ksort($this->_bindParam);
167
168
            $params = [];
169
170
            foreach ($this->_bindParam as $column => $value) {
171
                $params[] = $value;
172
            }
173
        }
174
175
        $retval = @db2_execute($this->_stmt, $params);
176
177
        if ($retval === false) {
178
            throw new DB2Exception(db2_stmt_errormsg());
179
        }
180
181
        $this->result = true;
182
183
        return $retval;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 View Code Duplication
    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
190
    {
191
        $this->_defaultFetchMode         = $fetchMode;
192
        $this->defaultFetchClass         = $arg2 ? $arg2 : $this->defaultFetchClass;
193
        $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...
194
195
        return true;
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    public function getIterator()
202
    {
203
        return new StatementIterator($this);
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209 View Code Duplication
    public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
210
    {
211
        // do not try fetching from the statement if it's not expected to contain result
212
        // in order to prevent exceptional situation
213
        if (!$this->result) {
214
            return false;
215
        }
216
217
        $fetchMode = $fetchMode ?: $this->_defaultFetchMode;
218
        switch ($fetchMode) {
219
            case \PDO::FETCH_BOTH:
220
                return db2_fetch_both($this->_stmt);
221
            case \PDO::FETCH_ASSOC:
222
                return db2_fetch_assoc($this->_stmt);
223
            case \PDO::FETCH_CLASS:
224
                $className = $this->defaultFetchClass;
225
                $ctorArgs  = $this->defaultFetchClassCtorArgs;
226
227
                if (func_num_args() >= 2) {
228
                    $args      = func_get_args();
229
                    $className = $args[1];
230
                    $ctorArgs  = isset($args[2]) ? $args[2] : [];
231
                }
232
233
                $result = db2_fetch_object($this->_stmt);
0 ignored issues
show
The function db2_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

233
                $result = /** @scrutinizer ignore-call */ db2_fetch_object($this->_stmt);
Loading history...
234
235
                if ($result instanceof \stdClass) {
236
                    $result = $this->castObject($result, $className, $ctorArgs);
0 ignored issues
show
It seems like $ctorArgs can also be of type string; however, parameter $ctorArgs of Doctrine\DBAL\Driver\IBM...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

236
                    $result = $this->castObject($result, $className, /** @scrutinizer ignore-type */ $ctorArgs);
Loading history...
237
                }
238
239
                return $result;
240
            case \PDO::FETCH_NUM:
241
                return db2_fetch_array($this->_stmt);
242
            case \PDO::FETCH_OBJ:
243
                return db2_fetch_object($this->_stmt);
244
            default:
245
                throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
246
        }
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252 View Code Duplication
    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
253
    {
254
        $rows = [];
255
256
        switch ($fetchMode) {
257
            case \PDO::FETCH_CLASS:
258
                while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) {
259
                    $rows[] = $row;
260
                }
261
                break;
262
            case \PDO::FETCH_COLUMN:
263
                while ($row = $this->fetchColumn()) {
264
                    $rows[] = $row;
265
                }
266
                break;
267
            default:
268
                while ($row = $this->fetch($fetchMode)) {
269
                    $rows[] = $row;
270
                }
271
        }
272
273
        return $rows;
274
    }
275
276
    /**
277
     * {@inheritdoc}
278
     */
279 View Code Duplication
    public function fetchColumn($columnIndex = 0)
280
    {
281
        $row = $this->fetch(\PDO::FETCH_NUM);
282
283
        if (false === $row) {
284
            return false;
285
        }
286
287
        return isset($row[$columnIndex]) ? $row[$columnIndex] : null;
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293
    public function rowCount()
294
    {
295
        return (@db2_num_rows($this->_stmt)) ? : 0;
296
    }
297
298
    /**
299
     * Casts a stdClass object to the given class name mapping its' properties.
300
     *
301
     * @param \stdClass     $sourceObject     Object to cast from.
302
     * @param string|object $destinationClass Name of the class or class instance to cast to.
303
     * @param array         $ctorArgs         Arguments to use for constructing the destination class instance.
304
     *
305
     * @return object
306
     *
307
     * @throws DB2Exception
308
     */
309
    private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
310
    {
311 View Code Duplication
        if ( ! is_string($destinationClass)) {
0 ignored issues
show
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...
312
            if ( ! is_object($destinationClass)) {
313
                throw new DB2Exception(sprintf(
314
                    'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
315
                ));
316
            }
317
        } else {
318
            $destinationClass = new \ReflectionClass($destinationClass);
319
            $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
320
        }
321
322
        $sourceReflection           = new \ReflectionObject($sourceObject);
323
        $destinationClassReflection = new \ReflectionObject($destinationClass);
0 ignored issues
show
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

323
        $destinationClassReflection = new \ReflectionObject(/** @scrutinizer ignore-type */ $destinationClass);
Loading history...
324
        /** @var \ReflectionProperty[] $destinationProperties */
325
        $destinationProperties      = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER);
326
327
        foreach ($sourceReflection->getProperties() as $sourceProperty) {
328
            $sourceProperty->setAccessible(true);
329
330
            $name  = $sourceProperty->getName();
331
            $value = $sourceProperty->getValue($sourceObject);
332
333
            // Try to find a case-matching property.
334
            if ($destinationClassReflection->hasProperty($name)) {
335
                $destinationProperty = $destinationClassReflection->getProperty($name);
336
337
                $destinationProperty->setAccessible(true);
338
                $destinationProperty->setValue($destinationClass, $value);
339
340
                continue;
341
            }
342
343
            $name = strtolower($name);
344
345
            // Try to find a property without matching case.
346
            // Fallback for the driver returning either all uppercase or all lowercase column names.
347
            if (isset($destinationProperties[$name])) {
348
                $destinationProperty = $destinationProperties[$name];
349
350
                $destinationProperty->setAccessible(true);
351
                $destinationProperty->setValue($destinationClass, $value);
352
353
                continue;
354
            }
355
356
            $destinationClass->$name = $value;
357
        }
358
359
        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...
360
    }
361
}
362