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

lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php (2 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;
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...
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;
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
    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  = $args[2] ?? [];
231
                }
232
233
                $result = db2_fetch_object($this->_stmt);
234
235
                if ($result instanceof \stdClass) {
236
                    $result = $this->castObject($result, $className, $ctorArgs);
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)) {
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);
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