Completed
Push — master ( c7757e...39cb21 )
by Luís
16s
created

lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php (15 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,
0 ignored issues
show
The constant Doctrine\DBAL\Driver\IBMDB2\DB2_LONG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
66
        \PDO::PARAM_STR => DB2_CHAR,
0 ignored issues
show
The constant Doctrine\DBAL\Driver\IBMDB2\DB2_CHAR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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;
0 ignored issues
show
The constant Doctrine\DBAL\Driver\IBMDB2\DB2_CHAR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
96
        }
97
98
        if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
0 ignored issues
show
The function db2_bind_param 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

98
        if (!/** @scrutinizer ignore-call */ db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
Loading history...
The constant Doctrine\DBAL\Driver\IBMDB2\DB2_PARAM_IN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
99
            throw new DB2Exception(db2_stmt_errormsg());
0 ignored issues
show
The function db2_stmt_errormsg 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

99
            throw new DB2Exception(/** @scrutinizer ignore-call */ db2_stmt_errormsg());
Loading history...
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);
0 ignored issues
show
The function db2_num_fields 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

134
        return /** @scrutinizer ignore-call */ db2_num_fields($this->_stmt);
Loading history...
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function errorCode()
141
    {
142
        return db2_stmt_error();
0 ignored issues
show
The function db2_stmt_error 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

142
        return /** @scrutinizer ignore-call */ db2_stmt_error();
Loading history...
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);
0 ignored issues
show
The function db2_execute 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

175
        $retval = @/** @scrutinizer ignore-call */ db2_execute($this->_stmt, $params);
Loading history...
176
177
        if ($retval === false) {
178
            throw new DB2Exception(db2_stmt_errormsg());
0 ignored issues
show
The function db2_stmt_errormsg 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

178
            throw new DB2Exception(/** @scrutinizer ignore-call */ db2_stmt_errormsg());
Loading history...
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)
0 ignored issues
show
This method seems to be duplicated in 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...
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 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);
0 ignored issues
show
The function db2_fetch_both 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

220
                return /** @scrutinizer ignore-call */ db2_fetch_both($this->_stmt);
Loading history...
221
            case \PDO::FETCH_ASSOC:
222
                return db2_fetch_assoc($this->_stmt);
0 ignored issues
show
The function db2_fetch_assoc 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

222
                return /** @scrutinizer ignore-call */ db2_fetch_assoc($this->_stmt);
Loading history...
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);
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);
0 ignored issues
show
The function db2_fetch_array 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

241
                return /** @scrutinizer ignore-call */ db2_fetch_array($this->_stmt);
Loading history...
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;
360
    }
361
}
362