• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 12673986434

08 Jan 2025 03:42PM UTC coverage: 84.455% (+0.001%) from 84.454%
12673986434

Pull #9385

github

web-flow
Merge 06e47f0ee into e475fd8fa
Pull Request #9385: refactor: Fix phpstan expr.resultUnused

20699 of 24509 relevant lines covered (84.45%)

190.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.04
/system/Database/OCI8/Connection.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Database\OCI8;
15

16
use CodeIgniter\Database\BaseConnection;
17
use CodeIgniter\Database\Exceptions\DatabaseException;
18
use CodeIgniter\Database\Query;
19
use CodeIgniter\Database\TableName;
20
use ErrorException;
21
use stdClass;
22

23
/**
24
 * Connection for OCI8
25
 *
26
 * @extends BaseConnection<resource, resource>
27
 */
28
class Connection extends BaseConnection
29
{
30
    /**
31
     * Database driver
32
     *
33
     * @var string
34
     */
35
    protected $DBDriver = 'OCI8';
36

37
    /**
38
     * Identifier escape character
39
     *
40
     * @var string
41
     */
42
    public $escapeChar = '"';
43

44
    /**
45
     * List of reserved identifiers
46
     *
47
     * Identifiers that must NOT be escaped.
48
     *
49
     * @var array
50
     */
51
    protected $reservedIdentifiers = [
52
        '*',
53
        'rownum',
54
    ];
55

56
    protected $validDSNs = [
57
        // TNS
58
        'tns' => '/^\(DESCRIPTION=(\(.+\)){2,}\)$/',
59
        // Easy Connect string (Oracle 10g+).
60
        // https://docs.oracle.com/en/database/oracle/oracle-database/23/netag/configuring-naming-methods.html#GUID-36F3A17D-843C-490A-8A23-FB0FE005F8E8
61
        // [//]host[:port][/[service_name][:server_type][/instance_name]]
62
        'ec' => '/^
63
            (\/\/)?
64
            (\[)?[a-z0-9.:_-]+(\])? # Host or IP address
65
            (:[1-9][0-9]{0,4})?     # Port
66
            (
67
                (\/)
68
                ([a-z0-9.$_]+)?     # Service name
69
                (:[a-z]+)?          # Server type
70
                (\/[a-z0-9$_]+)?    # Instance name
71
            )?
72
        $/ix',
73
        // Instance name (defined in tnsnames.ora)
74
        'in' => '/^[a-z0-9$_]+$/i',
75
    ];
76

77
    /**
78
     * Reset $stmtId flag
79
     *
80
     * Used by storedProcedure() to prevent execute() from
81
     * re-setting the statement ID.
82
     */
83
    protected $resetStmtId = true;
84

85
    /**
86
     * Statement ID
87
     *
88
     * @var resource
89
     */
90
    protected $stmtId;
91

92
    /**
93
     * Commit mode flag
94
     *
95
     * @used-by PreparedQuery::_execute()
96
     *
97
     * @var int
98
     */
99
    public $commitMode = OCI_COMMIT_ON_SUCCESS;
100

101
    /**
102
     * Cursor ID
103
     *
104
     * @var resource
105
     */
106
    protected $cursorId;
107

108
    /**
109
     * Latest inserted table name.
110
     *
111
     * @used-by PreparedQuery::_execute()
112
     *
113
     * @var string|null
114
     */
115
    public $lastInsertedTableName;
116

117
    /**
118
     * confirm DSN format.
119
     */
120
    private function isValidDSN(): bool
121
    {
122
        if ($this->DSN === null || $this->DSN === '') {
28✔
123
            return false;
×
124
        }
125

126
        foreach ($this->validDSNs as $regexp) {
28✔
127
            if (preg_match($regexp, $this->DSN)) {
28✔
128
                return true;
28✔
129
            }
130
        }
131

132
        return false;
×
133
    }
134

135
    /**
136
     * Connect to the database.
137
     *
138
     * @return false|resource
139
     */
140
    public function connect(bool $persistent = false)
141
    {
142
        if (! $this->isValidDSN()) {
19✔
143
            $this->buildDSN();
×
144
        }
145

146
        $func = $persistent ? 'oci_pconnect' : 'oci_connect';
19✔
147

148
        return ($this->charset === '')
19✔
149
            ? $func($this->username, $this->password, $this->DSN)
×
150
            : $func($this->username, $this->password, $this->DSN, $this->charset);
19✔
151
    }
152

153
    /**
154
     * Keep or establish the connection if no queries have been sent for
155
     * a length of time exceeding the server's idle timeout.
156
     *
157
     * @return void
158
     */
159
    public function reconnect()
160
    {
161
    }
×
162

163
    /**
164
     * Close the database connection.
165
     *
166
     * @return void
167
     */
168
    protected function _close()
169
    {
170
        if (is_resource($this->cursorId)) {
×
171
            oci_free_statement($this->cursorId);
×
172
        }
173
        if (is_resource($this->stmtId)) {
×
174
            oci_free_statement($this->stmtId);
×
175
        }
176
        oci_close($this->connID);
×
177
    }
178

179
    /**
180
     * Select a specific database table to use.
181
     */
182
    public function setDatabase(string $databaseName): bool
183
    {
184
        return false;
×
185
    }
186

187
    /**
188
     * Returns a string containing the version of the database being used.
189
     */
190
    public function getVersion(): string
191
    {
192
        if (isset($this->dataCache['version'])) {
602✔
193
            return $this->dataCache['version'];
602✔
194
        }
195

196
        if (! $this->connID || ($versionString = oci_server_version($this->connID)) === false) {
29✔
197
            return '';
×
198
        }
199
        if (preg_match('#Release\s(\d+(?:\.\d+)+)#', $versionString, $match)) {
29✔
200
            return $this->dataCache['version'] = $match[1];
29✔
201
        }
202

203
        return '';
×
204
    }
205

206
    /**
207
     * Executes the query against the database.
208
     *
209
     * @return false|resource
210
     */
211
    protected function execute(string $sql)
212
    {
213
        try {
214
            if ($this->resetStmtId === true) {
667✔
215
                $this->stmtId = oci_parse($this->connID, $sql);
667✔
216
            }
217

218
            oci_set_prefetch($this->stmtId, 1000);
667✔
219

220
            $result          = oci_execute($this->stmtId, $this->commitMode) ? $this->stmtId : false;
667✔
221
            $insertTableName = $this->parseInsertTableName($sql);
667✔
222

223
            if ($result && $insertTableName !== '') {
667✔
224
                $this->lastInsertedTableName = $insertTableName;
632✔
225
            }
226

227
            return $result;
667✔
228
        } catch (ErrorException $e) {
29✔
229
            log_message('error', (string) $e);
29✔
230

231
            if ($this->DBDebug) {
29✔
232
                throw new DatabaseException($e->getMessage(), $e->getCode(), $e);
12✔
233
            }
234
        }
235

236
        return false;
17✔
237
    }
238

239
    /**
240
     * Get the table name for the insert statement from sql.
241
     */
242
    public function parseInsertTableName(string $sql): string
243
    {
244
        $commentStrippedSql = preg_replace(['/\/\*(.|\n)*?\*\//m', '/--.+/'], '', $sql);
667✔
245
        $isInsertQuery      = str_starts_with(strtoupper(ltrim($commentStrippedSql)), 'INSERT');
667✔
246

247
        if (! $isInsertQuery) {
667✔
248
            return '';
667✔
249
        }
250

251
        preg_match('/(?is)\b(?:into)\s+("?\w+"?)/', $commentStrippedSql, $match);
632✔
252
        $tableName = $match[1] ?? '';
632✔
253

254
        return str_starts_with($tableName, '"') ? trim($tableName, '"') : strtoupper($tableName);
632✔
255
    }
256

257
    /**
258
     * Returns the total number of rows affected by this query.
259
     */
260
    public function affectedRows(): int
261
    {
262
        return oci_num_rows($this->stmtId);
48✔
263
    }
264

265
    /**
266
     * Generates the SQL for listing tables in a platform-dependent manner.
267
     *
268
     * @param string|null $tableName If $tableName is provided will return only this table if exists.
269
     */
270
    protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
271
    {
272
        $sql = 'SELECT "TABLE_NAME" FROM "USER_TABLES"';
590✔
273

274
        if ($tableName !== null) {
590✔
275
            return $sql . ' WHERE "TABLE_NAME" LIKE ' . $this->escape($tableName);
590✔
276
        }
277

278
        if ($prefixLimit && $this->DBPrefix !== '') {
30✔
279
            return $sql . ' WHERE "TABLE_NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . "%' "
×
280
                    . sprintf($this->likeEscapeStr, $this->likeEscapeChar);
×
281
        }
282

283
        return $sql;
30✔
284
    }
285

286
    /**
287
     * Generates a platform-specific query string so that the column names can be fetched.
288
     *
289
     * @param string|TableName $table
290
     */
291
    protected function _listColumns($table = ''): string
292
    {
293
        if ($table instanceof TableName) {
8✔
294
            $tableName = $this->escape(strtoupper($table->getActualTableName()));
2✔
295
            $owner     = $this->username;
2✔
296
        } elseif (str_contains($table, '.')) {
6✔
297
            sscanf($table, '%[^.].%s', $owner, $tableName);
×
298
            $tableName = $this->escape(strtoupper($this->DBPrefix . $tableName));
×
299
        } else {
300
            $owner     = $this->username;
6✔
301
            $tableName = $this->escape(strtoupper($this->DBPrefix . $table));
6✔
302
        }
303

304
        return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
8✔
305
                        WHERE UPPER(OWNER) = ' . $this->escape(strtoupper($owner)) . '
8✔
306
                                AND UPPER(TABLE_NAME) = ' . $tableName;
8✔
307
    }
308

309
    /**
310
     * Returns an array of objects with field data
311
     *
312
     * @return list<stdClass>
313
     *
314
     * @throws DatabaseException
315
     */
316
    protected function _fieldData(string $table): array
317
    {
318
        if (str_contains($table, '.')) {
108✔
319
            sscanf($table, '%[^.].%s', $owner, $table);
×
320
        } else {
321
            $owner = $this->username;
108✔
322
        }
323

324
        $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE
108✔
325
                        FROM ALL_TAB_COLUMNS
326
                        WHERE UPPER(OWNER) = ' . $this->escape(strtoupper($owner)) . '
108✔
327
                                AND UPPER(TABLE_NAME) = ' . $this->escape(strtoupper($table));
108✔
328

329
        if (($query = $this->query($sql)) === false) {
108✔
330
            throw new DatabaseException(lang('Database.failGetFieldData'));
×
331
        }
332
        $query = $query->getResultObject();
108✔
333

334
        $retval = [];
108✔
335

336
        for ($i = 0, $c = count($query); $i < $c; $i++) {
108✔
337
            $retval[$i]       = new stdClass();
108✔
338
            $retval[$i]->name = $query[$i]->COLUMN_NAME;
108✔
339
            $retval[$i]->type = $query[$i]->DATA_TYPE;
108✔
340

341
            $length = $query[$i]->CHAR_LENGTH > 0 ? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION;
108✔
342
            $length ??= $query[$i]->DATA_LENGTH;
108✔
343

344
            $retval[$i]->max_length = $length;
108✔
345

346
            $retval[$i]->nullable = $query[$i]->NULLABLE === 'Y';
108✔
347
            $retval[$i]->default  = $query[$i]->DATA_DEFAULT;
108✔
348
        }
349

350
        return $retval;
108✔
351
    }
352

353
    /**
354
     * Returns an array of objects with index data
355
     *
356
     * @return array<string, stdClass>
357
     *
358
     * @throws DatabaseException
359
     */
360
    protected function _indexData(string $table): array
361
    {
362
        if (str_contains($table, '.')) {
126✔
363
            sscanf($table, '%[^.].%s', $owner, $table);
×
364
        } else {
365
            $owner = $this->username;
126✔
366
        }
367

368
        $sql = 'SELECT AIC.INDEX_NAME, UC.CONSTRAINT_TYPE, AIC.COLUMN_NAME '
126✔
369
            . ' FROM ALL_IND_COLUMNS AIC '
126✔
370
            . ' LEFT JOIN USER_CONSTRAINTS UC ON AIC.INDEX_NAME = UC.CONSTRAINT_NAME AND AIC.TABLE_NAME = UC.TABLE_NAME '
126✔
371
            . 'WHERE AIC.TABLE_NAME = ' . $this->escape(strtolower($table)) . ' '
126✔
372
            . 'AND AIC.TABLE_OWNER = ' . $this->escape(strtoupper($owner)) . ' '
126✔
373
            . ' ORDER BY UC.CONSTRAINT_TYPE, AIC.COLUMN_POSITION';
126✔
374

375
        if (($query = $this->query($sql)) === false) {
126✔
376
            throw new DatabaseException(lang('Database.failGetIndexData'));
×
377
        }
378
        $query = $query->getResultObject();
126✔
379

380
        $retVal          = [];
126✔
381
        $constraintTypes = [
126✔
382
            'P' => 'PRIMARY',
126✔
383
            'U' => 'UNIQUE',
126✔
384
        ];
126✔
385

386
        foreach ($query as $row) {
126✔
387
            if (isset($retVal[$row->INDEX_NAME])) {
124✔
388
                $retVal[$row->INDEX_NAME]->fields[] = $row->COLUMN_NAME;
4✔
389

390
                continue;
4✔
391
            }
392

393
            $retVal[$row->INDEX_NAME]         = new stdClass();
124✔
394
            $retVal[$row->INDEX_NAME]->name   = $row->INDEX_NAME;
124✔
395
            $retVal[$row->INDEX_NAME]->fields = [$row->COLUMN_NAME];
124✔
396
            $retVal[$row->INDEX_NAME]->type   = $constraintTypes[$row->CONSTRAINT_TYPE] ?? 'INDEX';
124✔
397
        }
398

399
        return $retVal;
126✔
400
    }
401

402
    /**
403
     * Returns an array of objects with Foreign key data
404
     *
405
     * @return array<string, stdClass>
406
     *
407
     * @throws DatabaseException
408
     */
409
    protected function _foreignKeyData(string $table): array
410
    {
411
        $sql = 'SELECT
5✔
412
                acc.constraint_name,
413
                acc.table_name,
414
                acc.column_name,
415
                ccu.table_name foreign_table_name,
416
                accu.column_name foreign_column_name,
417
                ac.delete_rule
418
                FROM all_cons_columns acc
419
                JOIN all_constraints ac ON acc.owner = ac.owner
420
                AND acc.constraint_name = ac.constraint_name
421
                JOIN all_constraints ccu ON ac.r_owner = ccu.owner
422
                AND ac.r_constraint_name = ccu.constraint_name
423
                JOIN all_cons_columns accu ON accu.constraint_name = ccu.constraint_name
424
                AND accu.position = acc.position
425
                AND accu.table_name = ccu.table_name
426
                WHERE ac.constraint_type = ' . $this->escape('R') . '
5✔
427
                AND acc.table_name = ' . $this->escape($table);
5✔
428

429
        $query = $this->query($sql);
5✔
430

431
        if ($query === false) {
5✔
432
            throw new DatabaseException(lang('Database.failGetForeignKeyData'));
×
433
        }
434

435
        $query   = $query->getResultObject();
5✔
436
        $indexes = [];
5✔
437

438
        foreach ($query as $row) {
5✔
439
            $indexes[$row->CONSTRAINT_NAME]['constraint_name']       = $row->CONSTRAINT_NAME;
4✔
440
            $indexes[$row->CONSTRAINT_NAME]['table_name']            = $row->TABLE_NAME;
4✔
441
            $indexes[$row->CONSTRAINT_NAME]['column_name'][]         = $row->COLUMN_NAME;
4✔
442
            $indexes[$row->CONSTRAINT_NAME]['foreign_table_name']    = $row->FOREIGN_TABLE_NAME;
4✔
443
            $indexes[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->FOREIGN_COLUMN_NAME;
4✔
444
            $indexes[$row->CONSTRAINT_NAME]['on_delete']             = $row->DELETE_RULE;
4✔
445
            $indexes[$row->CONSTRAINT_NAME]['on_update']             = null;
4✔
446
            $indexes[$row->CONSTRAINT_NAME]['match']                 = null;
4✔
447
        }
448

449
        return $this->foreignKeyDataToObjects($indexes);
5✔
450
    }
451

452
    /**
453
     * Returns platform-specific SQL to disable foreign key checks.
454
     *
455
     * @return string
456
     */
457
    protected function _disableForeignKeyChecks()
458
    {
459
        return <<<'SQL'
460
            BEGIN
461
              FOR c IN
462
              (SELECT c.owner, c.table_name, c.constraint_name
463
               FROM user_constraints c, user_tables t
464
               WHERE c.table_name = t.table_name
465
               AND c.status = 'ENABLED'
466
               AND c.constraint_type = 'R'
467
               AND t.iot_type IS NULL
468
               ORDER BY c.constraint_type DESC)
469
              LOOP
470
                dbms_utility.exec_ddl_statement('alter table "' || c.owner || '"."' || c.table_name || '" disable constraint "' || c.constraint_name || '"');
471
              END LOOP;
472
            END;
473
            SQL;
474
    }
475

476
    /**
477
     * Returns platform-specific SQL to enable foreign key checks.
478
     *
479
     * @return string
480
     */
481
    protected function _enableForeignKeyChecks()
482
    {
483
        return <<<'SQL'
484
            BEGIN
485
              FOR c IN
486
              (SELECT c.owner, c.table_name, c.constraint_name
487
               FROM user_constraints c, user_tables t
488
               WHERE c.table_name = t.table_name
489
               AND c.status = 'DISABLED'
490
               AND c.constraint_type = 'R'
491
               AND t.iot_type IS NULL
492
               ORDER BY c.constraint_type DESC)
493
              LOOP
494
                dbms_utility.exec_ddl_statement('alter table "' || c.owner || '"."' || c.table_name || '" enable constraint "' || c.constraint_name || '"');
495
              END LOOP;
496
            END;
497
            SQL;
498
    }
499

500
    /**
501
     * Get cursor. Returns a cursor from the database
502
     *
503
     * @return resource
504
     */
505
    public function getCursor()
506
    {
507
        return $this->cursorId = oci_new_cursor($this->connID);
1✔
508
    }
509

510
    /**
511
     * Executes a stored procedure
512
     *
513
     * @param string $procedureName procedure name to execute
514
     * @param array  $params        params array keys
515
     *                              KEY      OPTIONAL  NOTES
516
     *                              name     no        the name of the parameter should be in :<param_name> format
517
     *                              value    no        the value of the parameter.  If this is an OUT or IN OUT parameter,
518
     *                              this should be a reference to a variable
519
     *                              type     yes       the type of the parameter
520
     *                              length   yes       the max size of the parameter
521
     *
522
     * @return bool|Query|Result
523
     */
524
    public function storedProcedure(string $procedureName, array $params)
525
    {
526
        if ($procedureName === '') {
3✔
527
            throw new DatabaseException(lang('Database.invalidArgument', [$procedureName]));
×
528
        }
529

530
        // Build the query string
531
        $sql = sprintf(
3✔
532
            'BEGIN %s (' . substr(str_repeat(',%s', count($params)), 1) . '); END;',
3✔
533
            $procedureName,
3✔
534
            ...array_map(static fn ($row) => $row['name'], $params)
3✔
535
        );
3✔
536

537
        $this->resetStmtId = false;
3✔
538
        $this->stmtId      = oci_parse($this->connID, $sql);
3✔
539
        $this->bindParams($params);
3✔
540
        $result            = $this->query($sql);
3✔
541
        $this->resetStmtId = true;
3✔
542

543
        return $result;
3✔
544
    }
545

546
    /**
547
     * Bind parameters
548
     *
549
     * @param array $params
550
     *
551
     * @return void
552
     */
553
    protected function bindParams($params)
554
    {
555
        if (! is_array($params) || ! is_resource($this->stmtId)) {
3✔
556
            return;
×
557
        }
558

559
        foreach ($params as $param) {
3✔
560
            oci_bind_by_name(
3✔
561
                $this->stmtId,
3✔
562
                $param['name'],
3✔
563
                $param['value'],
3✔
564
                $param['length'] ?? -1,
3✔
565
                $param['type'] ?? SQLT_CHR
3✔
566
            );
3✔
567
        }
568
    }
569

570
    /**
571
     * Returns the last error code and message.
572
     *
573
     * Must return an array with keys 'code' and 'message':
574
     *
575
     *  return ['code' => null, 'message' => null);
576
     */
577
    public function error(): array
578
    {
579
        // oci_error() returns an array that already contains
580
        // 'code' and 'message' keys, but it can return false
581
        // if there was no error ....
582
        $error     = oci_error();
2✔
583
        $resources = [$this->cursorId, $this->stmtId, $this->connID];
2✔
584

585
        foreach ($resources as $resource) {
2✔
586
            if (is_resource($resource)) {
2✔
587
                $error = oci_error($resource);
2✔
588
                break;
2✔
589
            }
590
        }
591

592
        return is_array($error)
2✔
593
            ? $error
×
594
            : [
2✔
595
                'code'    => '',
2✔
596
                'message' => '',
2✔
597
            ];
2✔
598
    }
599

600
    public function insertID(): int
601
    {
602
        if (empty($this->lastInsertedTableName)) {
83✔
603
            return 0;
×
604
        }
605

606
        $indexs     = $this->getIndexData($this->lastInsertedTableName);
83✔
607
        $fieldDatas = $this->getFieldData($this->lastInsertedTableName);
83✔
608

609
        if ($indexs === [] || $fieldDatas === []) {
83✔
610
            return 0;
×
611
        }
612

613
        $columnTypeList    = array_column($fieldDatas, 'type', 'name');
83✔
614
        $primaryColumnName = '';
83✔
615

616
        foreach ($indexs as $index) {
83✔
617
            if ($index->type !== 'PRIMARY' || count($index->fields) !== 1) {
83✔
618
                continue;
57✔
619
            }
620

621
            $primaryColumnName = $this->protectIdentifiers($index->fields[0], false, false);
83✔
622
            $primaryColumnType = $columnTypeList[$primaryColumnName];
83✔
623

624
            if ($primaryColumnType !== 'NUMBER') {
83✔
625
                $primaryColumnName = '';
×
626
            }
627
        }
628

629
        if ($primaryColumnName === '') {
83✔
630
            return 0;
×
631
        }
632

633
        $query           = $this->query('SELECT DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = ?', [$this->lastInsertedTableName, $primaryColumnName])->getRow();
83✔
634
        $lastInsertValue = str_replace('nextval', 'currval', $query->DATA_DEFAULT ?? '0');
83✔
635
        $query           = $this->query(sprintf('SELECT %s SEQ FROM DUAL', $lastInsertValue))->getRow();
83✔
636

637
        return (int) ($query->SEQ ?? 0);
83✔
638
    }
639

640
    /**
641
     * Build a DSN from the provided parameters
642
     *
643
     * @return void
644
     */
645
    protected function buildDSN()
646
    {
647
        if ($this->DSN !== '') {
×
648
            $this->DSN = '';
×
649
        }
650

651
        // Legacy support for TNS in the hostname configuration field
652
        $this->hostname = str_replace(["\n", "\r", "\t", ' '], '', $this->hostname);
×
653

654
        if (preg_match($this->validDSNs['tns'], $this->hostname)) {
×
655
            $this->DSN = $this->hostname;
×
656

657
            return;
×
658
        }
659

660
        $isEasyConnectableHostName = $this->hostname !== '' && ! str_contains($this->hostname, '/') && ! str_contains($this->hostname, ':');
×
661
        $easyConnectablePort       = ($this->port !== '') && ctype_digit((string) $this->port) ? ':' . $this->port : '';
×
662
        $easyConnectableDatabase   = $this->database !== '' ? '/' . ltrim($this->database, '/') : '';
×
663

664
        if ($isEasyConnectableHostName && ($easyConnectablePort !== '' || $easyConnectableDatabase !== '')) {
×
665
            /* If the hostname field isn't empty, doesn't contain
666
             * ':' and/or '/' and if port and/or database aren't
667
             * empty, then the hostname field is most likely indeed
668
             * just a hostname. Therefore we'll try and build an
669
             * Easy Connect string from these 3 settings, assuming
670
             * that the database field is a service name.
671
             */
672
            $this->DSN = $this->hostname . $easyConnectablePort . $easyConnectableDatabase;
×
673

674
            if (preg_match($this->validDSNs['ec'], $this->DSN)) {
×
675
                return;
×
676
            }
677
        }
678

679
        /* At this point, we can only try and validate the hostname and
680
         * database fields separately as DSNs.
681
         */
682
        if (preg_match($this->validDSNs['ec'], $this->hostname) || preg_match($this->validDSNs['in'], $this->hostname)) {
×
683
            $this->DSN = $this->hostname;
×
684

685
            return;
×
686
        }
687

688
        $this->database = str_replace(["\n", "\r", "\t", ' '], '', $this->database);
×
689

690
        foreach ($this->validDSNs as $regexp) {
×
691
            if (preg_match($regexp, $this->database)) {
×
692
                return;
×
693
            }
694
        }
695

696
        /* Well - OK, an empty string should work as well.
697
         * PHP will try to use environment variables to
698
         * determine which Oracle instance to connect to.
699
         */
700
        $this->DSN = '';
×
701
    }
702

703
    /**
704
     * Begin Transaction
705
     */
706
    protected function _transBegin(): bool
707
    {
708
        $this->commitMode = OCI_NO_AUTO_COMMIT;
17✔
709

710
        return true;
17✔
711
    }
712

713
    /**
714
     * Commit Transaction
715
     */
716
    protected function _transCommit(): bool
717
    {
718
        $this->commitMode = OCI_COMMIT_ON_SUCCESS;
5✔
719

720
        return oci_commit($this->connID);
5✔
721
    }
722

723
    /**
724
     * Rollback Transaction
725
     */
726
    protected function _transRollback(): bool
727
    {
728
        $this->commitMode = OCI_COMMIT_ON_SUCCESS;
17✔
729

730
        return oci_rollback($this->connID);
17✔
731
    }
732

733
    /**
734
     * Returns the name of the current database being used.
735
     */
736
    public function getDatabase(): string
737
    {
738
        if (! empty($this->database)) {
11✔
739
            return $this->database;
×
740
        }
741

742
        return $this->query('SELECT DEFAULT_TABLESPACE FROM USER_USERS')->getRow()->DEFAULT_TABLESPACE ?? '';
11✔
743
    }
744

745
    /**
746
     * Get the prefix of the function to access the DB.
747
     */
748
    protected function getDriverFunctionPrefix(): string
749
    {
750
        return 'oci_';
×
751
    }
752
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc