Completed
Branch dev-0.1.x (cbb4c7)
by Josué
06:10
created

PdoOci8Statement   D

Complexity

Total Complexity 103

Size/Duplication

Total Lines 696
Duplicated Lines 3.02 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 81.01%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 103
c 3
b 0
f 2
lcom 1
cbo 5
dl 21
loc 696
ccs 256
cts 316
cp 0.8101
rs 4.4629

30 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 5 42 6
A bindColumn() 0 12 3
B bindParam() 0 27 6
A bindValue() 0 4 1
A closeCursor() 0 4 1
A columnCount() 0 4 1
B debugDumpParams() 0 36 5
A errorCode() 12 12 2
A errorInfo() 0 20 2
B execute() 0 28 5
C fetch() 0 66 14
A fetchAll() 0 7 1
A fetchColumn() 0 14 3
A fetchObject() 0 16 3
A getAttribute() 0 8 2
B getColumnMeta() 0 35 4
A nextRowset() 0 4 1
A rowCount() 0 4 1
A setAttribute() 4 15 3
A setFetchMode() 0 10 2
B getTableName() 0 23 4
D getPDODataType() 0 25 10
C getDriverDataType() 0 31 14
A getConnection() 0 4 1
A getInternalIterator() 0 16 3
A current() 0 6 1
A next() 0 6 1
A key() 0 6 1
A valid() 0 6 1
A rewind() 0 6 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PdoOci8Statement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PdoOci8Statement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Jpina\PdoOci8;
4
5
use Jpina\Oci8\Oci8ConnectionInterface;
6
use Jpina\Oci8\Oci8FieldInterface;
7
use Jpina\Oci8\Oci8StatementInterface;
8
use Iterator;
9
use Traversable;
10
11
/**
12
 * Custom PDO_OCI implementation via OCI8 driver
13
 *
14
 * @see http://php.net/manual/en/class.pdostatement.php
15
 */
16
class PdoOci8Statement implements \Iterator
17
{
18
    /** @var  Oci8ConnectionInterface */
19
    private $connection;
20
21
    /** @var  Oci8StatementInterface */
22
    private $statement;
23
24
    /** @var string */
25
    private $sqlText = '';
26
27
    /** @var array */
28
    private $boundParameters = array();
29
30
    /** @var array */
31
    private $options;
32
33
    /** @var \ArrayIterator */
34
    private $iterator;
35
36 38
    public function __construct(Oci8ConnectionInterface $connection, $sqlText, $options = array())
37
    {
38 38
        if (!is_string($sqlText)) {
39
            throw new PdoOci8Exception('$sqlText is not a string');
40
        }
41
42 38
        $this->connection = $connection;
43 38
        $this->sqlText = $sqlText;
44
45 38
        $this->options = array(
46 38
            \PDO::ATTR_AUTOCOMMIT          => true,
47 38
            \PDO::ATTR_CASE                => \PDO::CASE_NATURAL,
48 38
            \PDO::ATTR_ERRMODE             => \PDO::ERRMODE_SILENT,
49 38
            \PDO::ATTR_ORACLE_NULLS        => \PDO::NULL_NATURAL,
50 38
            \PDO::ATTR_PREFETCH            => 100,
51 38
            \PDO::ATTR_TIMEOUT             => 600,
52 38
            \PDO::ATTR_STRINGIFY_FETCHES   => false,
53 38
            \PDO::ATTR_STATEMENT_CLASS     => null,
54 38
            \PDO::ATTR_EMULATE_PREPARES    => false,
55 38
            \PDO::ATTR_DEFAULT_FETCH_MODE  => \PDO::FETCH_BOTH,
56 38
            \PDO::ATTR_FETCH_TABLE_NAMES   => false,
57 38
            \PDO::ATTR_FETCH_CATALOG_NAMES => false,
58 38
            \PDO::ATTR_MAX_COLUMN_LEN      => 0,
59 38
            PdoOci8::OCI_ATTR_RETURN_LOBS  => false,
60
        );
61
62 38 View Code Duplication
        foreach ($options as $option => $value) {
0 ignored issues
show
Duplication introduced by
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...
63 4
            if (array_key_exists($option, $this->options)) {
64 4
                $this->options[$option] = $value;
65 4
            }
66 38
        }
67
68 38
        foreach ($options as $attribute => $value) {
69 4
            $this->setAttribute($attribute, $value);
70 38
        }
71
72
        try {
73 38
            $this->statement = $connection->parse($sqlText);
74 38
        } catch (\Exception $ex) {
75
            throw new PdoOci8Exception($ex->getMessage(), $ex->getCode(), $ex);
76
        }
77 38
    }
78
79
    /**
80
     * @param int|string $column
81
     * @param mixed $param
82
     * @param int $type
83
     * @param int $maxlen
84
     * @param mixed $driverdata
85
     *
86
     * @link http://php.net/manual/en/pdostatement.bindcolumn.php
87
     * @return bool
88
     */
89 2
    public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null)
0 ignored issues
show
Unused Code introduced by
The parameter $maxlen is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $driverdata is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
90
    {
91
        try {
92 2
            $type = $type === null ? \PDO::PARAM_STR : $type;
93 2
            $dataType = $this->getDriverDataType($type);
94 2
            return $this->statement->defineByName($column, $param, $dataType);
95
        } catch (\Exception $ex) {
96
            //throw new PdoOci8Exception($ex->getMessage(), $ex->getCode(), $ex);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
        }
98
99
        return false;
100
    }
101
102
    /**
103
     * @param string $parameter
104
     * @param mixed $variable
105
     * @param int $data_type
106
     * @param int $length
107
     * @param mixed $driver_options
108
     *
109
     * @link http://php.net/manual/en/pdostatement.bindparam.php
110
     * @return bool
111
     */
112 8
    public function bindParam(
113
        $parameter,
114
        &$variable,
115
        $data_type = \PDO::PARAM_STR,
116
        $length = null,
117
        $driver_options = null
0 ignored issues
show
Unused Code introduced by
The parameter $driver_options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
118
    ) {
119 8
        $isBound = false;
120
        try {
121 8
            $data_type = $data_type === null ? \PDO::PARAM_STR : $data_type;
122 8
            $dataType = $this->getDriverDataType($data_type);
123 8
            $length = $length !== null ? $length : -1;
124 8
            $isBound = $this->statement->bindByName($parameter, $variable, $length, $dataType);
125 4
            if ($isBound) {
126 4
                $this->boundParameters[$parameter] = array(
127 4
                    'name'     => is_int($parameter) ? '' : $parameter,
128 4
                    'position' => strrpos($this->sqlText, $parameter),
129 4
                    'value'    => &$variable,
130 4
                    'type'     => $data_type,
131 1
                );
132 4
            }
133 8
        } catch (\Exception $ex) {
134
            //throw new PdoOci8Exception($ex->getMessage(), $ex->getCode(), $ex);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
135
        }
136
137 8
        return $isBound;
138
    }
139
140
    /**
141
     * @param string $parameter
142
     * @param $value
143
     * @param int $data_type
144
     *
145
     * @link http://php.net/manual/en/pdostatement.bindvalue.php
146
     * @return bool
147
     */
148 3
    public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR)
149
    {
150 3
        return $this->bindParam($parameter, $value, $data_type);
151
    }
152
153
    /**
154
     * @link http://php.net/manual/en/pdostatement.closecursor.php
155
     * @return bool
156
     */
157 1
    public function closeCursor()
158
    {
159 1
        return $this->statement->cancel();
160
    }
161
162
    /**
163
     * @link http://php.net/manual/en/pdostatement.columncount.php
164
     * @return int
165
     */
166 1
    public function columnCount()
167
    {
168 1
        return 0;
169
    }
170
171
    /**
172
     * @link http://php.net/manual/en/pdostatement.debugdumpparams.php
173
     */
174 2
    public function debugDumpParams()
175
    {
176 2
        $sqlText = $this->sqlText;
177 2
        $sqlTextLength = strlen($sqlText);
178 2
        $parameters = $this->boundParameters;
179 2
        usort($parameters, function ($a, $b) {
180 2
            if ($a['position'] === $b['position']) {
181
                return 0;
182
            }
183
184 2
            return $a['position'] < $b['position'] ? -1 : 1;
185 2
        });
186 2
        $parametersCount = count($parameters);
187
188 2
        echo "SQL: [{$sqlTextLength}] {$sqlText}". PHP_EOL .
189 2
            "Params: {$parametersCount}";
190 2
        foreach ($parameters as $key => $parameter) {
191
            //TODO Add parameter position and number
192 2
            $position = $parameter['position'];
193 2
            $nameLength = strlen($parameter['name']);
194 2
            $name = $parameter['name'];
195 2
            $index = $key + 1;
196 2
            $dataType = $parameter['type'];
197
198 2
            echo PHP_EOL;
199 2
            if ($name === '') {
200
                echo "Key: Position #{$position}:". PHP_EOL;
201
            } else {
202 2
                echo "Key: Name: [{$nameLength}] {$name}". PHP_EOL;
203
            }
204 2
            echo "paramno={$index}". PHP_EOL .
205 2
                "name=[{$nameLength}]{$name}". PHP_EOL .
206 2
                "is_param=1". PHP_EOL .
207 2
                "param_type={$dataType}";
208 2
        }
209 2
    }
210
211
    /**
212
     * @link http://php.net/manual/en/pdostatement.errorcode.php
213
     * @return string
214
     */
215 2 View Code Duplication
    public function errorCode()
0 ignored issues
show
Duplication introduced by
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...
216
    {
217 2
        $driverError = $this->statement->getError();
218 2
        if (!$driverError) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $driverError of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
219 1
            return null;
220
        }
221
222 1
        $error = $this->errorInfo();
223 1
        $sqlStateErrorCode = $error[0];
224
225 1
        return $sqlStateErrorCode;
226
    }
227
228
    /**
229
     * @link http://php.net/manual/en/pdo.errorinfo.php
230
     * @return array
231
     */
232 3
    public function errorInfo()
233
    {
234 3
        $driverError = $this->statement->getError();
235 3
        if ($driverError) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $driverError of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
236 2
            $driverErrorMessage = $driverError['message'];
237 2
            $driverErrorCode = $driverError['code'];
238 2
        } else {
239 1
            $driverErrorMessage = null;
240 1
            $driverErrorCode = null;
241
        }
242
243 3
        $sqlStateErrorCode = OracleSqlStateCode::getSqlStateErrorCode((int)$driverErrorCode);
244
        $error = array(
245 3
            $sqlStateErrorCode,
246 3
            $driverErrorCode,
247
            $driverErrorMessage
248 3
        );
249
250 3
        return $error;
251
    }
252
253
    /**
254
     * @param array $input_parameters
255
     *
256
     * @link http://php.net/manual/en/pdostatement.execute.php
257
     * @return bool
258
     */
259 19
    public function execute($input_parameters = array())
260
    {
261
        try {
262 19
            foreach ($input_parameters as $key => $value) {
263
                if (is_int($key)) {
264
                    $parameterName = $key + 1;
265
                } else {
266
                    $parameterName = $key;
267
                }
268
                $this->bindValue($parameterName, $value);
269 19
            }
270
271 19
            if ($this->getAttribute(\PDO::ATTR_AUTOCOMMIT)) {
272 19
                $isCommitOnSuccess = OCI_NO_AUTO_COMMIT;
273 19
            } else {
274
                $isCommitOnSuccess = OCI_COMMIT_ON_SUCCESS;
275
            }
276
277 19
            $result = $this->statement->execute($isCommitOnSuccess);
278
279 16
            return $result;
280 3
        } catch (\Exception $ex) {
281
            //TODO Handle Exception
282 3
            new PdoOci8Exception($ex->getMessage(), $ex->getCode(), $ex);
283
        }
284
285 3
        return false;
286
    }
287
288
    /**
289
     * @param int $fetch_style
290
     * @param int $cursor_orientation
291
     * @param int $cursor_offset
292
     *
293
     * @link http://php.net/manual/en/pdostatement.fetch.php
294
     * @return mixed
295
     */
296 9
    public function fetch(
297
        $fetch_style = \PDO::ATTR_DEFAULT_FETCH_MODE,
298
        $cursor_orientation = \PDO::FETCH_ORI_NEXT,
0 ignored issues
show
Unused Code introduced by
The parameter $cursor_orientation is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
299
        $cursor_offset = 0
0 ignored issues
show
Unused Code introduced by
The parameter $cursor_offset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
300
    ) {
301 9
        if ($fetch_style === null) {
302
            $fetch_style === \PDO::ATTR_DEFAULT_FETCH_MODE;
303
        }
304
305
        try {
306
//            if ($fetch_style === \PDO::ATTR_DEFAULT_FETCH_MODE) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
307
//                $fetch_style = $this->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE);
308
//            }
309
310
            switch ($fetch_style) {
311 9
                case \PDO::FETCH_ASSOC:
312 2
                    $mode = OCI_ASSOC;
313 2
                    break;
314 7
                case \PDO::FETCH_BOUND:
315
                    // returns TRUE and assigns the values of the columns in your result set to the PHP
316
                    // variables to which they were bound with the PDOStatement::bindColumn() method
317 1
                    return $this->statement->fetch();
318 6
                case \PDO::FETCH_CLASS:
319
                    // returns a new instance of the requested class, mapping the columns of the result
320
                    // set to named properties in the class
321
                    break;
322 6
                case \PDO::FETCH_CLASS | \PDO::FETCH_CLASSTYPE:
323
                    // the name of the class is determined from a value of the first column.
324
                    break;
325 6
                case \PDO::FETCH_INTO:
326
                    // updates an existing instance of the requested class, mapping the columns of the
327
                    // result set to named properties in the class
328
                    break;
329 6
                case \PDO::FETCH_LAZY:
330 6
                case \PDO::FETCH_BOTH + \PDO::FETCH_OBJ:
331
                     // combines PDO::FETCH_BOTH and PDO::FETCH_OBJ, creating the object variable names
332
                    // as they are accessed
333
                    break;
334 6
                case \PDO::FETCH_NAMED:
335
                    // returns an array with the same form as PDO::FETCH_ASSOC, except that if there are
336
                    // multiple columns with the same name, the value referred to by that key will be an
337
                    // array of all the values in the row that had that column name
338
                    break;
339 6
                case \PDO::FETCH_NUM:
340 4
                    $mode = OCI_NUM;
341 4
                    break;
342 2
                case \PDO::FETCH_OBJ:
343
                    // returns an anonymous object with property names that correspond to the column names
344
                    // returned in your result set
345 1
                    return $this->statement->fetchObject();
346
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
347 1
                case \PDO::FETCH_BOTH:
348 1
                default:
349 1
                    $mode = OCI_BOTH;
350 1
                    break;
351 1
            }
352
            // TODO Combine other flags: eg. OCI_NULLS and OCI_LOBS
353
            // TODO update $this->numRows on successfull fetch
354
            // TODO update $this->isIteratorValid on NOT successfull fetch
355 7
            return $this->statement->fetchArray($mode);
0 ignored issues
show
Bug introduced by
The variable $mode does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
356
        } catch (\Exception $ex) {
357
            new PdoOci8Exception($ex->getMessage(), $ex->getCode(), $ex);
358
        }
359
360
        return false;
361
    }
362
363
    /**
364
     * @param int $fetch_style
365
     * @param mixed $fetch_argument
366
     * @param array $ctor_args
367
     *
368
     * @link http://php.net/manual/en/pdostatement.fetchall.php
369
     * @return array
370
     */
371 2
    public function fetchAll($fetch_style = null, $fetch_argument = null, $ctor_args = array())
0 ignored issues
show
Unused Code introduced by
The parameter $fetch_style is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fetch_argument is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $ctor_args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
372
    {
373
        // TODO Implement properly (use all other fetch modes)
374 2
        $this->statement->fetchAll($rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_ASSOC);
375
376 2
        return $rows;
377
    }
378
379
    /**
380
     * @param int $column_number
381
     *
382
     * @link http://php.net/manual/en/pdostatement.fetchcolumn.php
383
     * @return mixed
384
     */
385 3
    public function fetchColumn($column_number = 0)
386
    {
387 3
        $row = $this->fetch(\PDO::FETCH_NUM);
388 3
        if ($row === false) {
389 1
            return false;
390
        }
391
392 3
        if (array_key_exists($column_number, $row)) {
393 2
            return $row[$column_number];
394
        }
395
396
        // TODO Throw Exception
397 1
        return false;
398
    }
399
400
401
    /**
402
     * @param string $class_name
403
     * @param array $ctor_args
404
     *
405
     * @link http://php.net/manual/en/pdostatement.fetchobject.php
406
     * @return bool|object
407
     */
408 1
    public function fetchObject($class_name = "stdClass", $ctor_args = array())
409
    {
410 1
        $row = $this->fetch(\PDO::FETCH_ASSOC);
411 1
        if ($row === false) {
412
            return false;
413
        }
414
415 1
        $reflexionClass = new \ReflectionClass($class_name);
416 1
        $classInstance = $reflexionClass->newInstanceArgs($ctor_args);
417
418 1
        foreach ($row as $property => $value) {
0 ignored issues
show
Bug introduced by
The expression $row of type boolean|object|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
419 1
            $classInstance->{$property} = $value;
420 1
        }
421
422 1
        return $classInstance;
423
    }
424
425
    /**
426
     * @param int $attribute
427
     *
428
     * @link http://php.net/manual/en/pdostatement.getattribute.php
429
     * @return mixed
430
     */
431 22
    public function getAttribute($attribute)
432
    {
433 22
        if (array_key_exists($attribute, $this->options)) {
434 22
            return $this->options[$attribute];
435
        }
436
437
        return null;
438
    }
439
440
    /**
441
     * @param int $column
442
     *
443
     * @throws PdoOci8Exception
444
     *
445
     * @link http://php.net/manual/en/pdostatement.getcolumnmeta.php
446
     * @return bool|array
447
     */
448 4
    public function getColumnMeta($column)
449
    {
450 4
        $statementType = $this->statement->getType();
451 4
        if ($statementType !== 'SELECT') {
452 1
            return false;
453
        }
454
455 3
        $table = $this->getTableName();
456
457 3
        $sqlText = $this->sqlText;
458 3
        $statement = $this->getConnection()->parse($sqlText);
459 3
        $statement->execute(OCI_DESCRIBE_ONLY);
460 3
        $field = $statement->getField($column + 1);
461
462 3
        if ($field instanceof Oci8FieldInterface) {
463
            // Oracle returns attributes in upper case by default
464 3
            $fieldName = $field->getName();
465 3
            if ($this->getAttribute(\PDO::ATTR_CASE) === \PDO::CASE_LOWER) {
466
                $fieldName = strtolower($fieldName);
467
            }
468
469
            return array(
470 3
                'native_type'      => $field->getRawType(),
471 3
                'driver:decl_type' => $field->getType(),
472 3
                'flags'            => array(),
473 3
                'name'             => $fieldName,
474 3
                'table'            => $table,
475 3
                'len'              => $field->getSize(),
476 3
                'precision'        => $field->getPrecision() - $field->getScale(),
477 3
                'pdo_type'         => $this->getPDODataType($field->getType()),
478 3
            );
479
        }
480
481
        return false;
482
    }
483
484
    /**
485
     * @link http://php.net/manual/en/pdostatement.nextrowset.php
486
     * @return bool
487
     */
488
    public function nextRowset()
489
    {
490
        // TODO Implement
491
    }
492
493
    /**
494
     * @link http://php.net/manual/en/pdostatement.rowcount.php
495
     * @return int
496
     */
497 3
    public function rowCount()
498
    {
499 3
        return $this->statement->getNumRows();
500
    }
501
502
503
    /**
504
     * @param $attribute
505
     * @param $value
506
     *
507
     * @link http://php.net/manual/en/pdostatement.setattribute.php
508
     * @return bool
509
     */
510 7
    public function setAttribute($attribute, $value)
511
    {
512
        $readOnlyAttributes = array(
513 7
            \PDO::ATTR_AUTOCOMMIT,
514 7
        );
515
516 7 View Code Duplication
        if (array_search($attribute, $readOnlyAttributes) !== false ||
0 ignored issues
show
Duplication introduced by
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...
517 7
            !array_key_exists($attribute, $this->options)) {
518 4
            return false;
519
        }
520
521 7
        $this->options[$attribute] = $value;
522
523 7
        return true;
524
    }
525
526
    /**
527
     * @param int $mode
528
     * @param string|int|object $target
529
     * @param array $ctor_args
530
     *
531
     * @link http://php.net/manual/en/pdostatement.setfetchmode.php
532
     * @return bool
533
     */
534 3
    public function setFetchMode($mode, $target = null, $ctor_args = array())
535
    {
536 3
        $isSuccess = $this->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, $mode);
537 3
        if ($isSuccess) {
538 3
            $this->fetchTarget = $target;
0 ignored issues
show
Bug introduced by
The property fetchTarget does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
539 3
            $this->fetchTargetConstructorArgs = $ctor_args;
0 ignored issues
show
Bug introduced by
The property fetchTargetConstructorArgs does not seem to exist. Did you mean fetchTarget?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
540 3
        }
541
542 3
        return $isSuccess;
543
    }
544
545
    /**
546
     * @return string
547
     */
548 3
    protected function getTableName()
549
    {
550 3
        $statementType = $this->statement->getType();
551 3
        if ($statementType !== 'SELECT') {
552
            return '';
553
        }
554
555 3
        $sqlText = strtoupper($this->sqlText);
556 3
        $idx     = strpos($sqlText, ' FROM ');
557 3
        $table   = substr($this->sqlText, $idx + 6);
558 3
        $table   = trim($table);
559
560 3
        if (strpos($table, '(') !== false) {
561 1
            return '';
562
        }
563
564 2
        $idxSpace = strpos($table, ' ');
565 2
        if ($idxSpace !== false) {
566 1
            $table = substr($table, 0, $idxSpace);
567 1
        }
568
569 2
        return $table;
570
    }
571
572
    /**
573
     * @param string $data_type The data type name
574
     *
575
     * @return int
576
     */
577 3
    protected function getPDODataType($data_type)
578
    {
579
        //TODO Add all oracle data types
580 3
        $pdoDataType = \PDO::PARAM_STR;
581
        switch ($data_type) {
582 3
            case 'NUMBER':
583 1
                $pdoDataType = \PDO::PARAM_INT;
584 1
                break;
585 2
            case 'CHAR':
586 2
            case 'VARCHAR2':
587 2
            case 'NVARCHAR2':
588 2
                $pdoDataType = \PDO::PARAM_STR;
589 2
                break;
590
            case 'LOB':
591
            case 'CLOB':
592
            case 'BLOB':
593
            case 'NCLOB':
594
                $pdoDataType = \PDO::PARAM_LOB;
595
                break;
596
            case 'BOOLEAN':
597
                $pdoDataType = \PDO::PARAM_BOOL;
598
        }
599
600 3
        return $pdoDataType;
601
    }
602
603
    /**
604
     * @param int $type
605
     * @throws PdoOci8Exception
606
     * @return int
607
     */
608 10
    protected function getDriverDataType($type)
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
609
    {
610 10
        $dataType = null;
611
        switch ($dataType) {
612 10
            case \PDO::PARAM_BOOL:
613
                $dataType = SQLT_BOL;
614
                break;
615 10
            case \PDO::PARAM_INT:
616
                $dataType = SQLT_INT;
617
                break;
618 10
            case \PDO::PARAM_LOB:
619
                $dataType = SQLT_CLOB;
620
                break;
621 10
            case \PDO::PARAM_STMT:
622
                throw new PdoOci8Exception('Parameter type \PDO::PARAM_STMT is not currently supported.');
623 10
            case \PDO::PARAM_NULL:
624 10
            case \PDO::PARAM_STR:
625 10
                $dataType = SQLT_CHR;
626 10
                break;
627
            case \PDO::PARAM_INPUT_OUTPUT:
628
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_BOOL:
629
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_INT:
630
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_LOB:
631
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_STMT:
632
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_NULL:
633
            case \PDO::PARAM_INPUT_OUTPUT | \PDO::PARAM_STR:
634
                throw new PdoOci8Exception('Parameter type \PDO::PARAM_INPUT_OUTPUT is not currently supported.');
635
        }
636
637 10
        return $dataType;
638
    }
639
640
    /**
641
     * @return Oci8ConnectionInterface
642
     */
643 3
    protected function getConnection()
644
    {
645 3
        return $this->connection;
646
    }
647
648
    /**
649
     * @return \Traversable
650
     */
651 2
    protected function getInternalIterator()
652
    {
653 2
        if ($this->iterator instanceof \Traversable) {
654 2
            return $this->iterator;
655
        }
656
657 2
        $rows = $this->fetchAll();
658 2
        if ($rows === false) {
659
            //TODO Throw Exception?
660
            $rows = array();
661
        }
662
663 2
        $this->iterator = new \ArrayIterator($rows);
664
665 2
        return $this->iterator;
666
    }
667
668
    /**
669
     * @return array
670
     */
671 2
    public function current()
672
    {
673 2
        $iterator = $this->getInternalIterator();
674
675 2
        return $iterator->current();
676
    }
677
678 2
    public function next()
679
    {
680 2
        $iterator = $this->getInternalIterator();
681
682 2
        $iterator->next();
683 2
    }
684
685
    /**
686
     * @return int|null
687
     */
688
    public function key()
689
    {
690
        $iterator = $this->getInternalIterator();
691
692
        return $iterator->key();
693
    }
694
695
    /**
696
     * @return bool
697
     */
698 2
    public function valid()
699
    {
700 2
        $iterator = $this->getInternalIterator();
701
702 2
        return $iterator->valid();
703
    }
704
705 1
    public function rewind()
706
    {
707 1
        $iterator = $this->getInternalIterator();
708
709 1
        $iterator->rewind();
710 1
    }
711
}
712