Completed
Push — master ( baf761...81d404 )
by Terry
03:36
created

OciStubPdo   C

Complexity

Total Complexity 58

Size/Duplication

Total Lines 483
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 483
rs 6.3005
c 0
b 0
f 0
wmc 58
lcom 3
cbo 5

28 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 34 8
A getTables() 0 4 1
A getColumns() 0 4 1
A getForeignKeys() 0 4 1
A getTableCounts() 0 4 1
A getCharset() 0 4 1
A _getCharset() 0 22 3
A getConnection() 0 4 1
A query() 0 13 2
A exec() 0 11 2
A setAttribute() 0 8 4
A getAttribute() 0 10 3
A getAutoCommit() 0 4 1
A commit() 0 7 1
A rollBack() 0 7 1
A beginTransaction() 0 6 1
A setLimit() 0 18 4
A prepare() 0 9 2
A setError() 0 12 4
A errorCode() 0 7 2
A errorInfo() 0 11 2
A close() 0 8 2
A errorHandler() 0 10 2
A getAvailableDrivers() 0 8 2
A inTransaction() 0 4 1
A quote() 0 6 1
A lastInsertId() 0 14 3
A getFieldComment() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like OciStubPdo 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 OciStubPdo, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
/**
3
 * @package Terah\FluentPdoModel
4
 *
5
 * Licensed under The MIT License
6
 * For full copyright and license information, please see the LICENSE.txt
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
10
 */
11
12
namespace Terah\FluentPdoModel\Drivers;
13
14
use \PDO;
15
use \PDOException;
16
use Psr\Log\LoggerInterface;
17
use Psr\Log\NullLogger;
18
use Terah\FluentPdoModel\Column;
19
use Terah\FluentPdoModel\ForeignKey;
20
use Terah\RedisCache\CacheInterface;
21
use function Terah\Assert\Assert;
22
use Terah\RedisCache\NullCache;
23
24
/**
25
 * PDOOCI
26
 *
27
 * PHP version 5.3
28
 *
29
 * @category OciStubPdo
30
 * @package  OciStubPdo
31
 * @author   Eustáquio Rangel <[email protected]>
32
 * @license  http://www.gnu.org/licenses/gpl-2.0.html GPLv2
33
 * @link     http://github.com/taq/pdooci
34
 */
35
36
/**
37
 * Main class of OciStubPdo
38
 *
39
 * PHP version 5.3
40
 *
41
 * @category Connection
42
 * @package  OciStubPdo
43
 * @author   Eustáquio Rangel <[email protected]>
44
 * @license  http://www.gnu.org/licenses/gpl-2.0.html GPLv2
45
 * @link     http://github.com/taq/pdooci
46
 */
47
class OciStubPdo extends AbstractPdo implements DriverInterface
48
{
49
    /**
50
     * @var resource
51
     */
52
    private $_con           = null;
53
    /**
54
     * @var bool
55
     */
56
    private $_autocommit    = true;
57
    /**
58
     * @var array
59
     */
60
    private $_last_error    = null;
61
    /**
62
     * @var string
63
     */
64
    private $_charset       = null;
65
66
    /**
67
     * OciStubPdo constructor.
68
     * @param string $dsn
69
     * @param string $username
70
     * @param string $password
71
     * @param array $options
72
     * @param LoggerInterface|null $logger
73
     * @param CacheInterface|null $cache
74
     */
75
    public function __construct(string $dsn, string $username='', string $password='', array $options=[], LoggerInterface $logger=null, CacheInterface $cache=null)
76
    {
77
        if (!function_exists("\\oci_parse")) {
78
            throw new PDOException(PHP_VERSION . " : No support for Oracle, please install the OCI driver");
79
        }
80
81
        // find charset
82
        $charset    = null;
0 ignored issues
show
Unused Code introduced by
$charset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
83
        $dsn        = preg_replace('/^oci:/', '', $dsn);
84
        $tokens     = preg_split('/;/', $dsn);
85
        $data       = $tokens[0];
86
        $charset    = $this->_getCharset($tokens);
87
88
        try {
89
            if (!is_null($options) && array_key_exists(\PDO::ATTR_PERSISTENT, $options)) {
90
                $this->_con = @\oci_pconnect($username, $password, $data, $charset);
91
                $this->setError();
92
            } else {
93
                $this->_con = @\oci_connect($username, $password, $data, $charset);
94
                $this->setError();
95
            }
96
            if (!$this->_con) {
97
                $error = oci_error();
98
                throw new \Exception($error['code'] . ': ' . $error['message']);
99
            }
100
        } catch ( \Exception $exception ) {
101
            throw new PDOException($exception->getMessage());
102
        }
103
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
104
        $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
105
        $this->setConfig($options, 'oci:' . $data);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type null; however, Terah\FluentPdoModel\Dri...bstractPdo::setConfig() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
106
        $this->setLogger($logger ? $logger : new NullLogger());
107
        $this->setCache($cache ? $cache : new NullCache());
108
    }
109
110
111
    /**
112
     * @param bool $include_views
113
     * @return array
114
     */
115
    public function getTables(bool $include_views=false) : array
116
    {
117
        return [];
118
    }
119
120
    /**
121
     * @param bool $include_views
122
     * @param string $table
123
     * @return Column[]
124
     */
125
    public function getColumns(bool $include_views=false, string $table='') : array
126
    {
127
        return [];
128
    }
129
130
    /**
131
     * @param string $table
132
     * @return ForeignKey[]
133
     */
134
    public function getForeignKeys(string $table='') : array
135
    {
136
        return [];
137
    }
138
139
    /**
140
     * @param bool|false $include_views
141
     * @param string $table
142
     * @return array
143
     */
144
    public function getTableCounts(bool $include_views=false, string $table='') : array
145
    {
146
        return [];
147
    }
148
149
    /**
150
     * Return the charset
151
     *
152
     * @return string charset
153
     */
154
    public function getCharset()
155
    {
156
        return $this->_charset;
157
    }
158
159
    /**
160
     * Find the charset
161
     *
162
     * @param array $charset charset
163
     *
164
     * @return string charset
165
     */
166
    private function _getCharset(array $charset = null)
167
    {
168
        if ( ! $charset )
169
        {
170
            $langs = array_filter([getenv("NLS_LANG")], "strlen");
171
172
            return array_shift($langs);
173
        }
174
175
        $expr   = '/^(charset=)(\w+)$/';
176
        $tokens = array_filter($charset, function ($token) use ($expr) {
177
            return preg_match($expr, $token, $matches);
178
        });
179
        $this->_charset = null;
180
        if ( sizeof($tokens) > 0 )
181
        {
182
            preg_match($expr, array_shift($tokens), $matches);
183
            $this->_charset = $matches[2];
184
        }
185
186
        return $this->_charset;
187
    }
188
189
    /**
190
     * Return the connection
191
     *
192
     * @return resource handle
193
     */
194
    public function getConnection()
195
    {
196
        return $this->_con;
197
    }
198
199
    /**
200
     * Execute a query
201
     *
202
     * @param string $statement sql query
203
     * @param int    $mode      PDO query() mode
204
     * @param int    $p1        PDO query() first parameter
205
     * @param int    $p2        PDO query() second parameter
206
     *
207
     * @return OciPdoStubStatement
208
     * @throws PDOException
209
     */
210
    public function query($statement, $mode = null, $p1 = null, $p2 = null)
0 ignored issues
show
Unused Code introduced by
The parameter $mode 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 $p1 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 $p2 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...
211
    {
212
        // TODO: use mode and parameters
213
        $stmt = null;
214
        try {
215
            $stmt = new OciPdoStubStatement($this, $statement);
216
            $stmt->execute();
217
            $this->setError();
218
            return $stmt;
219
        } catch ( \Exception $e ) {
220
            throw new PDOException($e->getMessage());
221
        }
222
    }
223
224
    /**
225
     * Execute query
226
     *
227
     * @param string $sql query
228
     *
229
     * @return integer of affected rows
230
     * @throws PDOException
231
     */
232
    public function exec($sql)
233
    {
234
        try {
235
            $stmt = $this->query($sql);
236
            $rows = $stmt->rowCount();
237
            $stmt->closeCursor();
238
            return $rows;
239
        } catch ( \Exception $e ) {
240
            throw new PDOException($e->getMessage());
241
        }
242
    }
243
244
    /**
245
     * Set an attribute
246
     *
247
     * @param int   $attr  attribute
248
     * @param mixed $value value
249
     *
250
     * @return boolean|null if set was ok
251
     */
252
    public function setAttribute($attr, $value)
253
    {
254
        switch ($attr) {
255
            case \PDO::ATTR_AUTOCOMMIT:
256
                $this->_autocommit = ( is_bool($value) && $value ) || in_array(strtolower($value), array("on", "true"));
257
                return;
258
        }
259
    }
260
261
    /**
262
     * Return an attribute
263
     *
264
     * @param int $attr attribute
265
     *
266
     * @return mixed attribute value
267
     */
268
    public function getAttribute($attr)
269
    {
270
        switch ($attr) {
271
            case \PDO::ATTR_AUTOCOMMIT:
272
                return $this->_autocommit;
273
            case \PDO::ATTR_DRIVER_NAME:
274
                return 'oci';
275
        }
276
        return null;
277
    }
278
279
    /**
280
     * Return the auto commit flag
281
     *
282
     * @return boolean auto commit flag
283
     */
284
    public function getAutoCommit()
285
    {
286
        return $this->_autocommit;
287
    }
288
289
    /**
290
     * Commit connection
291
     *
292
     * @return bool if commit was executed
293
     */
294
    public function commit() : bool
295
    {
296
        \oci_commit($this->_con);
297
        $this->setError();
298
299
        return true;
300
    }
301
302
    /**
303
     * Rollback connection
304
     *
305
     * @return bool if rollback was executed
306
     */
307
    public function rollBack() : bool
308
    {
309
        \oci_rollback($this->_con);
310
        $this->setError();
311
312
        return true;
313
    }
314
315
    /**
316
     * Start a transaction, setting auto commit to off
317
     *
318
     * @return bool
319
     */
320
    public function beginTransaction() : bool
321
    {
322
        $this->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
323
324
        return true;
325
    }
326
327
    /**
328
     * @param string $query
329
     * @param integer $limit
330
     * @param null|integer $offset
331
     * @return string
332
     */
333
    public function setLimit(string $query, int $limit=0, int $offset=0) : string
334
    {
335
        Assert($query)->string()->notEmpty();
336
        Assert($limit)->nullOr()->integer();
337
        Assert($offset)->nullOr()->integer();
338
        if ( $offset )
339
        {
340
            $limit  = $limit ?: 1;
341
            $limit  = $limit + $offset;
342
            return "SELECT * FROM ({$query}) WHERE ROWNUM between {$offset} AND {$limit}";
343
        }
344
        if ( $limit  )
345
        {
346
            return "SELECT * FROM ({$query}) WHERE ROWNUM <= {$limit}";
347
        }
348
349
        return $query;
350
    }
351
352
    /**
353
     * Prepare a statement
354
     *
355
     * @param string $query   for statement
356
     * @param mixed  $options for driver
357
     *
358
     * @return OciPdoStubStatement
359
     * @throws PDOException
360
     */
361
    public function prepare($query, $options = null)
362
    {
363
        $stmt = null;
0 ignored issues
show
Unused Code introduced by
$stmt is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
364
        try {
365
            return new OciPdoStubStatement($this, $query);
366
        } catch ( \Exception $e ) {
367
            throw new PDOException($e->getMessage());
368
        }
369
    }
370
371
    /**
372
     * Sets the last error found
373
     *
374
     * @param resource $obj optional object to extract error from
375
     *
376
     * @return null
377
     */
378
    public function setError($obj = null)
379
    {
380
        $obj = $obj ? $obj : $this->_con;
381
        if (!is_resource($obj)) {
382
            return;
383
        }
384
        $error = \oci_error($obj);
385
        if (!$error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error 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...
386
            return null;
387
        }
388
        $this->_last_error = $error;
389
    }
390
391
    /**
392
     * Returns the last error found
393
     *
394
     * @return int error code
395
     */
396
    public function errorCode()
397
    {
398
        if (!$this->_last_error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_last_error 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...
399
            return null;
400
        }
401
        return intval($this->_last_error["code"]);
402
    }
403
404
    /**
405
     * Returns the last error info
406
     *
407
     * @return array error info
408
     */
409
    public function errorInfo()
410
    {
411
        if (!$this->_last_error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_last_error 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...
412
            return null;
413
        }
414
        return [
415
            $this->_last_error["code"],
416
            $this->_last_error["code"],
417
            $this->_last_error["message"]
418
        ];
419
    }
420
421
    /**
422
     * Close connection
423
     *
424
     * @return null
425
     */
426
    public function close()
427
    {
428
        if (is_null($this->_con)) {
429
            return;
430
        }
431
        \oci_close($this->_con);
432
        $this->_con = null;
433
    }
434
435
    /**
436
     * Trigger stupid errors who should be exceptions
437
     *
438
     * @param int    $errno   error number
439
     * @param string $errstr  error message
440
     * @param mixed  $errfile error file
441
     * @param int    $errline error line
442
     *
443
     * @return null
444
     */
445
    public function errorHandler($errno, $errstr, $errfile, $errline)
446
    {
447
        unset($errno, $errfile, $errline);
448
        preg_match('/(ORA-)(\d+)/', $errstr, $ora_error);
449
        if ($ora_error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ora_error of type string[] 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...
450
            $this->_last_error = intval($ora_error[2]);
0 ignored issues
show
Documentation Bug introduced by
It seems like intval($ora_error[2]) of type integer is incompatible with the declared type array of property $_last_error.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
451
        } else {
452
            $this->setError($this->_con);
453
        }
454
    }
455
456
    /**
457
     * Return available drivers
458
     * Will insert the OCI driver on the list, if not exist
459
     *
460
     * @return array with drivers
461
     */
462
    public static function getAvailableDrivers()
463
    {
464
        $drivers = \PDO::getAvailableDrivers();
465
        if (!in_array("oci", $drivers)) {
466
            array_push($drivers, "oci");
467
        }
468
        return $drivers;
469
    }
470
471
    /**
472
     * Return if is on a transaction
473
     *
474
     * @return boolean on a transaction
475
     */
476
    public function inTransaction()
477
    {
478
        return !$this->_autocommit;
479
    }
480
481
    /**
482
     * Quote a string
483
     *
484
     * @param string $string to be quoted
485
     * @param int    $type   parameter type
486
     *
487
     * @return string quoted
488
     */
489
    public function quote($string, $type = null)
490
    {
491
        $string = preg_replace('/\'/', "''", $string);
492
        $string = "'$string'";
493
        return $string;
494
    }
495
496
    /**
497
     * Return the last inserted id
498
     * If the sequence name is not sent, throws an exception
499
     *
500
     * @param string $sequence name
501
     *
502
     * @return integer last id
503
     * @throws PDOException
504
     */
505
    public function lastInsertId($sequence = null)
506
    {
507
        if (!$sequence) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sequence of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
508
            throw new PDOException("SQLSTATE[IM001]: Driver does not support this function: driver does not support getting attributes in system_requirements");
509
        }
510
        $id = 0;
511
        try {
512
            $stmt = $this->query("select $sequence.currval from dual");
513
            $data = $stmt->fetch(\PDO::FETCH_ASSOC);
514
            $id   = intval($data["CURRVAL"]);
515
        } catch ( PDOException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
516
        }
517
        return $id;
518
    }
519
520
    /**
521
     * @param string $table
522
     * @param string $column
523
     * @return string
524
     */
525
    public function getFieldComment(string $table, string $column) : string
526
    {
527
        return '';
528
    }
529
}
530