OciStubPdo::prepare()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
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
        {
256
            case \PDO::ATTR_AUTOCOMMIT:
257
                $this->_autocommit = ( is_bool($value) && $value ) || in_array(strtolower($value), ["on", "true"]);
258
                return;
259
        }
260
261
        return;
262
    }
263
264
    /**
265
     * Return an attribute
266
     *
267
     * @param int $attr attribute
268
     *
269
     * @return mixed attribute value
270
     */
271
    public function getAttribute($attr)
272
    {
273
        switch ($attr) {
274
            case \PDO::ATTR_AUTOCOMMIT:
275
                return $this->_autocommit;
276
            case \PDO::ATTR_DRIVER_NAME:
277
                return 'oci';
278
        }
279
        return null;
280
    }
281
282
    /**
283
     * Return the auto commit flag
284
     *
285
     * @return boolean auto commit flag
286
     */
287
    public function getAutoCommit()
288
    {
289
        return $this->_autocommit;
290
    }
291
292
    /**
293
     * Commit connection
294
     *
295
     * @return bool if commit was executed
296
     */
297
    public function commit() : bool
298
    {
299
        \oci_commit($this->_con);
300
        $this->setError();
301
302
        return true;
303
    }
304
305
    /**
306
     * Rollback connection
307
     *
308
     * @return bool if rollback was executed
309
     */
310
    public function rollBack() : bool
311
    {
312
        \oci_rollback($this->_con);
313
        $this->setError();
314
315
        return true;
316
    }
317
318
    /**
319
     * Start a transaction, setting auto commit to off
320
     *
321
     * @return bool
322
     */
323
    public function beginTransaction() : bool
324
    {
325
        $this->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
326
327
        return true;
328
    }
329
330
    /**
331
     * @param string $query
332
     * @param integer $limit
333
     * @param null|integer $offset
334
     * @return string
335
     */
336
    public function setLimit(string $query, int $limit=0, int $offset=0) : string
337
    {
338
        Assert($query)->string()->notEmpty();
339
        Assert($limit)->nullOr()->integer();
340
        Assert($offset)->nullOr()->integer();
341
        if ( $offset )
342
        {
343
            $limit  = $limit ?: 1;
344
            $limit  = $limit + $offset;
345
            return "SELECT * FROM ({$query}) WHERE ROWNUM between {$offset} AND {$limit}";
346
        }
347
        if ( $limit  )
348
        {
349
            return "SELECT * FROM ({$query}) WHERE ROWNUM <= {$limit}";
350
        }
351
352
        return $query;
353
    }
354
355
    /**
356
     * Prepare a statement
357
     *
358
     * @param string $query   for statement
359
     * @param mixed  $options for driver
360
     *
361
     * @return OciPdoStubStatement
362
     * @throws PDOException
363
     */
364
    public function prepare($query, $options = null)
365
    {
366
        $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...
367
        try {
368
            return new OciPdoStubStatement($this, $query);
369
        } catch ( \Exception $e ) {
370
            throw new PDOException($e->getMessage());
371
        }
372
    }
373
374
    /**
375
     * Sets the last error found
376
     *
377
     * @param resource $obj optional object to extract error from
378
     *
379
     * @return null
380
     */
381
    public function setError($obj = null)
382
    {
383
        $obj = $obj ? $obj : $this->_con;
384
        if (!is_resource($obj)) {
385
            return;
386
        }
387
        $error = \oci_error($obj);
388
        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...
389
            return null;
390
        }
391
        $this->_last_error = $error;
392
393
        return null;
394
    }
395
396
    /**
397
     * Returns the last error found
398
     *
399
     * @return int error code
400
     */
401
    public function errorCode()
402
    {
403
        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...
404
            return null;
405
        }
406
        return intval($this->_last_error["code"]);
407
    }
408
409
    /**
410
     * Returns the last error info
411
     *
412
     * @return array error info
413
     */
414
    public function errorInfo()
415
    {
416
        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...
417
            return null;
418
        }
419
        return [
420
            $this->_last_error["code"],
421
            $this->_last_error["code"],
422
            $this->_last_error["message"]
423
        ];
424
    }
425
426
    /**
427
     * Close connection
428
     *
429
     * @return null
430
     */
431
    public function close()
432
    {
433
        if (is_null($this->_con)) {
434
            return;
435
        }
436
        \oci_close($this->_con);
437
        $this->_con = null;
438
439
        return null;
440
    }
441
442
    /**
443
     * Trigger stupid errors who should be exceptions
444
     *
445
     * @param int    $errno   error number
446
     * @param string $errstr  error message
447
     * @param mixed  $errfile error file
448
     * @param int    $errline error line
449
     *
450
     * @return null
451
     */
452
    public function errorHandler($errno, $errstr, $errfile, $errline)
453
    {
454
        unset($errno, $errfile, $errline);
455
        preg_match('/(ORA-)(\d+)/', $errstr, $ora_error);
456
        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...
457
            $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...
458
        } else {
459
            $this->setError($this->_con);
460
        }
461
462
        return null;
463
    }
464
465
    /**
466
     * Return available drivers
467
     * Will insert the OCI driver on the list, if not exist
468
     *
469
     * @return array with drivers
470
     */
471
    public static function getAvailableDrivers()
472
    {
473
        $drivers = \PDO::getAvailableDrivers();
474
        if (!in_array("oci", $drivers)) {
475
            array_push($drivers, "oci");
476
        }
477
        return $drivers;
478
    }
479
480
    /**
481
     * Return if is on a transaction
482
     *
483
     * @return boolean on a transaction
484
     */
485
    public function inTransaction()
486
    {
487
        return !$this->_autocommit;
488
    }
489
490
    /**
491
     * Quote a string
492
     *
493
     * @param string $string to be quoted
494
     * @param int    $type   parameter type
495
     *
496
     * @return string quoted
497
     */
498
    public function quote($string, $type = null)
499
    {
500
        $string = preg_replace('/\'/', "''", $string);
501
        $string = "'$string'";
502
        return $string;
503
    }
504
505
    /**
506
     * Return the last inserted id
507
     * If the sequence name is not sent, throws an exception
508
     *
509
     * @param string $sequence name
510
     *
511
     * @return integer last id
512
     * @throws PDOException
513
     */
514
    public function lastInsertId($sequence = null)
515
    {
516
        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...
517
            throw new PDOException("SQLSTATE[IM001]: Driver does not support this function: driver does not support getting attributes in system_requirements");
518
        }
519
        $id = 0;
520
        try {
521
            $stmt = $this->query("select $sequence.currval from dual");
522
            $data = $stmt->fetch(\PDO::FETCH_ASSOC);
523
            $id   = intval($data["CURRVAL"]);
524
        } catch ( PDOException $e ) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
525
        }
526
        return $id;
527
    }
528
529
    /**
530
     * @param string $table
531
     * @param string $column
532
     * @return string
533
     */
534
    public function getFieldComment(string $table, string $column) : string
535
    {
536
        return '';
537
    }
538
}
539