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

codeigniter4 / CodeIgniter4 / 28995226889

09 Jul 2026 04:59AM UTC coverage: 89.607% (+0.02%) from 89.588%
28995226889

Pull #10395

github

web-flow
Merge d47c78aa8 into 0d4f36d76
Pull Request #10395: feat: add StreamResponse and SSE response factories

31 of 41 new or added lines in 3 files covered. (75.61%)

223 existing lines in 6 files now uncovered.

25330 of 28268 relevant lines covered (89.61%)

230.92 hits per line

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

71.09
/system/Database/MySQLi/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\MySQLi;
15

16
use CodeIgniter\Database\BaseConnection;
17
use CodeIgniter\Database\Exceptions\DatabaseException;
18
use CodeIgniter\Database\TableName;
19
use CodeIgniter\Exceptions\LogicException;
20
use mysqli;
21
use mysqli_result;
22
use mysqli_sql_exception;
23
use stdClass;
24
use Throwable;
25

26
/**
27
 * Connection for MySQLi
28
 *
29
 * @extends BaseConnection<mysqli, mysqli_result>
30
 */
31
class Connection extends BaseConnection
32
{
33
    /**
34
     * Database driver
35
     *
36
     * @var string
37
     */
38
    public $DBDriver = 'MySQLi';
39

40
    /**
41
     * DELETE hack flag
42
     *
43
     * Whether to use the MySQL "delete hack" which allows the number
44
     * of affected rows to be shown. Uses a preg_replace when enabled,
45
     * adding a bit more processing to all queries.
46
     *
47
     * @var bool
48
     */
49
    public $deleteHack = true;
50

51
    /**
52
     * Identifier escape character
53
     *
54
     * @var string
55
     */
56
    public $escapeChar = '`';
57

58
    /**
59
     * MySQLi object
60
     *
61
     * Has to be preserved without being assigned to $connId.
62
     *
63
     * @var false|mysqli
64
     */
65
    public $mysqli;
66

67
    /**
68
     * MySQLi constant
69
     *
70
     * For unbuffered queries use `MYSQLI_USE_RESULT`.
71
     *
72
     * Default mode for buffered queries uses `MYSQLI_STORE_RESULT`.
73
     *
74
     * @var int
75
     */
76
    public $resultMode = MYSQLI_STORE_RESULT;
77

78
    /**
79
     * Use MYSQLI_OPT_INT_AND_FLOAT_NATIVE
80
     *
81
     * @var bool
82
     */
83
    public $numberNative = false;
84

85
    /**
86
     * Use MYSQLI_CLIENT_FOUND_ROWS
87
     *
88
     * Whether affectedRows() should return number of rows found,
89
     * or number of rows changed, after an UPDATE query.
90
     *
91
     * @var bool
92
     */
93
    public $foundRows = false;
94

95
    /**
96
     * Strict SQL mode
97
     */
98
    protected bool $strictOn = false;
99

100
    /**
101
     * Checks whether the native database error represents a unique constraint violation.
102
     */
103
    protected function isUniqueConstraintViolation(int|string $code, string $message): bool
104
    {
105
        // ER_DUP_ENTRY: duplicate key value.
106
        return $code === 1062;
72✔
107
    }
108

109
    /**
110
     * Checks whether the native database error represents a foreign key constraint violation.
111
     */
112
    protected function isForeignKeyConstraintViolation(int|string $code, string $message): bool
113
    {
114
        // ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2.
115
        return in_array($code, [1216, 1217, 1451, 1452], true);
36✔
116
    }
117

118
    /**
119
     * Checks whether the native database error represents a NOT NULL constraint violation.
120
     */
121
    protected function isNotNullConstraintViolation(int|string $code, string $message): bool
122
    {
123
        // ER_BAD_NULL_ERROR, ER_BAD_NULL_ERROR_NOT_IGNORED: column cannot be null.
124
        return in_array($code, [1048, 3673], true);
31✔
125
    }
126

127
    /**
128
     * Checks whether the native database error represents a CHECK constraint violation.
129
     */
130
    protected function isCheckConstraintViolation(int|string $code, string $message): bool
131
    {
132
        if ($code === 3819) {
24✔
133
            return true;
1✔
134
        }
135

136
        // MariaDB reports CHECK failures as ER_CONSTRAINT_FAILED, while MySQL uses 4025 for other errors.
137
        return $code === 4025 && str_contains(strtolower($this->getVersion()), 'mariadb');
23✔
138
    }
139

140
    /**
141
     * Checks whether the native database code represents a retryable transaction failure.
142
     */
143
    protected function isRetryableTransactionErrorCode(int|string $code): bool
144
    {
145
        // ER_LOCK_DEADLOCK: InnoDB rolls back the full transaction.
146
        return $code === 1213;
22✔
147
    }
148

149
    /**
150
     * Connect to the database.
151
     *
152
     * @return false|mysqli
153
     *
154
     * @throws DatabaseException
155
     */
156
    public function connect(bool $persistent = false)
157
    {
158
        // Do we have a socket path?
159
        if ($this->hostname[0] === '/') {
79✔
UNCOV
160
            $hostname = null;
×
161
            $port     = null;
×
UNCOV
162
            $socket   = $this->hostname;
×
163
        } else {
164
            $hostname = $persistent ? 'p:' . $this->hostname : $this->hostname;
79✔
165
            $port     = empty($this->port) ? null : $this->port;
79✔
166
            $socket   = '';
79✔
167
        }
168

169
        $clientFlags  = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0;
79✔
170
        $this->mysqli = mysqli_init();
79✔
171

172
        mysqli_report(MYSQLI_REPORT_ALL & ~MYSQLI_REPORT_INDEX);
79✔
173

174
        $this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);
79✔
175

176
        if ($this->numberNative === true) {
79✔
177
            $this->mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);
1✔
178
        }
179

180
        $initCommands = [];
79✔
181

182
        if ($this->strictOn) {
79✔
183
            $initCommands[] = "sql_mode = CONCAT(@@sql_mode, ',', 'STRICT_ALL_TABLES')";
79✔
184
        } else {
185
            $initCommands[] = "sql_mode = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
×
186
                                @@sql_mode,
187
                                'STRICT_ALL_TABLES,', ''),
188
                            ',STRICT_ALL_TABLES', ''),
189
                        'STRICT_ALL_TABLES', ''),
190
                    'STRICT_TRANS_TABLES,', ''),
191
                ',STRICT_TRANS_TABLES', ''),
UNCOV
192
            'STRICT_TRANS_TABLES', '')";
×
193
        }
194

195
        // Set session timezone if configured
196
        $timezoneOffset = $this->getSessionTimezone();
79✔
197
        if ($timezoneOffset !== null) {
79✔
198
            $initCommands[] = "time_zone = '{$timezoneOffset}'";
3✔
199
        }
200

201
        $this->mysqli->options(
79✔
202
            MYSQLI_INIT_COMMAND,
79✔
203
            'SET SESSION ' . implode(', ', $initCommands),
79✔
204
        );
79✔
205

206
        if (is_array($this->encrypt)) {
79✔
207
            $ssl = [];
×
208

UNCOV
209
            if (! empty($this->encrypt['ssl_key'])) {
×
UNCOV
210
                $ssl['key'] = $this->encrypt['ssl_key'];
×
211
            }
212
            if (! empty($this->encrypt['ssl_cert'])) {
×
213
                $ssl['cert'] = $this->encrypt['ssl_cert'];
×
214
            }
215
            if (! empty($this->encrypt['ssl_ca'])) {
×
216
                $ssl['ca'] = $this->encrypt['ssl_ca'];
×
217
            }
218
            if (! empty($this->encrypt['ssl_capath'])) {
×
UNCOV
219
                $ssl['capath'] = $this->encrypt['ssl_capath'];
×
220
            }
221
            if (! empty($this->encrypt['ssl_cipher'])) {
×
UNCOV
222
                $ssl['cipher'] = $this->encrypt['ssl_cipher'];
×
223
            }
224

UNCOV
225
            if ($ssl !== []) {
×
UNCOV
226
                if (isset($this->encrypt['ssl_verify'])) {
×
UNCOV
227
                    if ($this->encrypt['ssl_verify']) {
×
UNCOV
228
                        if (defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) {
×
UNCOV
229
                            $this->mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, 1);
×
230
                        }
231
                    }
232
                    // Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT
233
                    // to FALSE didn't do anything, so PHP 5.6.16 introduced yet another
234
                    // constant ...
235
                    //
236
                    // https://secure.php.net/ChangeLog-5.php#5.6.16
237
                    // https://bugs.php.net/bug.php?id=68344
UNCOV
238
                    elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT') && version_compare($this->mysqli->client_info, 'mysqlnd 5.6', '>=')) {
×
239
                        $clientFlags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
×
240
                    }
241
                }
242

243
                $this->mysqli->ssl_set(
×
244
                    $ssl['key'] ?? null,
×
UNCOV
245
                    $ssl['cert'] ?? null,
×
UNCOV
246
                    $ssl['ca'] ?? null,
×
247
                    $ssl['capath'] ?? null,
×
UNCOV
248
                    $ssl['cipher'] ?? null,
×
UNCOV
249
                );
×
250
            }
251

UNCOV
252
            $clientFlags += MYSQLI_CLIENT_SSL;
×
253
        }
254

255
        if ($this->foundRows) {
79✔
256
            $clientFlags += MYSQLI_CLIENT_FOUND_ROWS;
1✔
257
        }
258

259
        try {
260
            if ($this->mysqli->real_connect(
79✔
261
                $hostname,
79✔
262
                $this->username,
79✔
263
                $this->password,
79✔
264
                $this->database,
79✔
265
                $port,
79✔
266
                $socket,
79✔
267
                $clientFlags,
79✔
268
            )) {
79✔
269
                if (! $this->mysqli->set_charset($this->charset)) {
79✔
UNCOV
270
                    log_message('error', "Database: Unable to set the configured connection charset ('{$this->charset}').");
×
271

UNCOV
272
                    $this->mysqli->close();
×
273

UNCOV
274
                    if ($this->DBDebug) {
×
UNCOV
275
                        throw new DatabaseException('Unable to set client connection character set: ' . $this->charset);
×
276
                    }
277

UNCOV
278
                    return false;
×
279
                }
280

281
                return $this->mysqli;
79✔
282
            }
283
        } catch (Throwable $e) {
1✔
284
            // Clean sensitive information from errors.
285
            $msg = $e->getMessage();
1✔
286

287
            $msg = str_replace($this->username, '****', $msg);
1✔
288
            $msg = str_replace($this->password, '****', $msg);
1✔
289

290
            throw new DatabaseException($msg, $e->getCode(), $e);
1✔
291
        }
292

UNCOV
293
        return false;
×
294
    }
295

296
    /**
297
     * Close the database connection.
298
     *
299
     * @return void
300
     */
301
    protected function _close()
302
    {
303
        $this->connID->close();
6✔
304
    }
305

306
    /**
307
     * Select a specific database table to use.
308
     */
309
    public function setDatabase(string $databaseName): bool
310
    {
UNCOV
311
        if ($databaseName === '') {
×
UNCOV
312
            $databaseName = $this->database;
×
313
        }
314

UNCOV
315
        if (empty($this->connID)) {
×
UNCOV
316
            $this->initialize();
×
317
        }
318

UNCOV
319
        if ($this->connID->select_db($databaseName)) {
×
UNCOV
320
            $this->database = $databaseName;
×
321

322
            return true;
×
323
        }
324

UNCOV
325
        return false;
×
326
    }
327

328
    /**
329
     * Returns a string containing the version of the database being used.
330
     */
331
    public function getVersion(): string
332
    {
333
        if (isset($this->dataCache['version'])) {
4✔
334
            return $this->dataCache['version'];
3✔
335
        }
336

337
        if (empty($this->mysqli)) {
2✔
338
            $this->initialize();
1✔
339
        }
340

341
        return $this->dataCache['version'] = $this->mysqli->server_info;
2✔
342
    }
343

344
    /**
345
     * Executes the query against the database.
346
     *
347
     * @return false|mysqli_result
348
     */
349
    protected function execute(string $sql)
350
    {
351
        while ($this->connID->more_results()) {
944✔
UNCOV
352
            $this->connID->next_result();
×
UNCOV
353
            if ($res = $this->connID->store_result()) {
×
UNCOV
354
                $res->free();
×
355
            }
356
        }
357

358
        try {
359
            return $this->connID->query($this->prepQuery($sql), $this->resultMode);
944✔
360
        } catch (mysqli_sql_exception $e) {
50✔
361
            log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
50✔
362
                'message' => $e->getMessage(),
50✔
363
                'exFile'  => clean_path($e->getFile()),
50✔
364
                'exLine'  => $e->getLine(),
50✔
365
                'trace'   => render_backtrace($e->getTrace()),
50✔
366
            ]);
50✔
367

368
            $exception = $this->createDatabaseException($e->getMessage(), $e->getCode(), $e);
50✔
369

370
            if ($this->DBDebug) {
50✔
371
                throw $exception;
23✔
372
            }
373

374
            $this->lastException = $exception;
27✔
375
        }
376

377
        return false;
27✔
378
    }
379

380
    /**
381
     * Prep the query. If needed, each database adapter can prep the query string
382
     */
383
    protected function prepQuery(string $sql): string
384
    {
385
        // mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
386
        // modifies the query so that it a proper number of affected rows is returned.
387
        if ($this->deleteHack === true && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) {
944✔
388
            return trim($sql) . ' WHERE 1=1';
3✔
389
        }
390

391
        return $sql;
944✔
392
    }
393

394
    /**
395
     * Returns the total number of rows affected by this query.
396
     */
397
    public function affectedRows(): int
398
    {
399
        return $this->connID->affected_rows ?? 0;
67✔
400
    }
401

402
    /**
403
     * Platform-dependant string escape
404
     */
405
    protected function _escapeString(string $str): string
406
    {
407
        if (! $this->connID) {
920✔
UNCOV
408
            $this->initialize();
×
409
        }
410

411
        return $this->connID->real_escape_string($str);
920✔
412
    }
413

414
    /**
415
     * Escape Like String Direct
416
     * There are a few instances where MySQLi queries cannot take the
417
     * additional "ESCAPE x" parameter for specifying the escape character
418
     * in "LIKE" strings, and this handles those directly with a backslash.
419
     *
420
     * @param list<string>|string $str Input string
421
     *
422
     * @return list<string>|string
423
     */
424
    public function escapeLikeStringDirect($str)
425
    {
426
        if (is_array($str)) {
1✔
UNCOV
427
            foreach ($str as $key => $val) {
×
428
                $str[$key] = $this->escapeLikeStringDirect($val);
×
429
            }
430

UNCOV
431
            return $str;
×
432
        }
433

434
        $str = $this->_escapeString($str);
1✔
435

436
        // Escape LIKE condition wildcards
437
        return str_replace(
1✔
438
            [$this->likeEscapeChar, '%', '_'],
1✔
439
            ['\\' . $this->likeEscapeChar, '\\%', '\\_'],
1✔
440
            $str,
1✔
441
        );
1✔
442
    }
443

444
    /**
445
     * Generates the SQL for listing tables in a platform-dependent manner.
446
     * Uses escapeLikeStringDirect().
447
     *
448
     * @param string|null $tableName If $tableName is provided will return only this table if exists.
449
     */
450
    protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
451
    {
452
        $sql = 'SHOW TABLES FROM ' . $this->escapeIdentifier($this->database);
863✔
453

454
        if ((string) $tableName !== '') {
863✔
455
            return $sql . ' LIKE ' . $this->escape($tableName);
862✔
456
        }
457

458
        if ($prefixLimit && $this->DBPrefix !== '') {
87✔
UNCOV
459
            return $sql . " LIKE '" . $this->escapeLikeStringDirect($this->DBPrefix) . "%'";
×
460
        }
461

462
        return $sql;
87✔
463
    }
464

465
    /**
466
     * Generates a platform-specific query string so that the column names can be fetched.
467
     *
468
     * @param string|TableName $table
469
     */
470
    protected function _listColumns($table = ''): string
471
    {
472
        $tableName = $this->protectIdentifiers(
13✔
473
            $table,
13✔
474
            true,
13✔
475
            null,
13✔
476
            false,
13✔
477
        );
13✔
478

479
        return 'SHOW COLUMNS FROM ' . $tableName;
13✔
480
    }
481

482
    /**
483
     * Returns an array of objects with field data
484
     *
485
     * @return list<stdClass>
486
     *
487
     * @throws DatabaseException
488
     */
489
    protected function _fieldData(string $table): array
490
    {
491
        $table = $this->protectIdentifiers($table, true, null, false);
16✔
492

493
        if (($query = $this->query('SHOW COLUMNS FROM ' . $table)) === false) {
16✔
UNCOV
494
            throw new DatabaseException(lang('Database.failGetFieldData'));
×
495
        }
496
        $query = $query->getResultObject();
16✔
497

498
        $retVal = [];
16✔
499

500
        for ($i = 0, $c = count($query); $i < $c; $i++) {
16✔
501
            $retVal[$i]       = new stdClass();
16✔
502
            $retVal[$i]->name = $query[$i]->Field;
16✔
503

504
            sscanf($query[$i]->Type, '%[a-z](%d)', $retVal[$i]->type, $retVal[$i]->max_length);
16✔
505

506
            $retVal[$i]->nullable    = $query[$i]->Null === 'YES';
16✔
507
            $retVal[$i]->default     = $query[$i]->Default;
16✔
508
            $retVal[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
16✔
509
        }
510

511
        return $retVal;
16✔
512
    }
513

514
    /**
515
     * Returns an array of objects with index data
516
     *
517
     * @return array<string, stdClass>
518
     *
519
     * @throws DatabaseException
520
     * @throws LogicException
521
     */
522
    protected function _indexData(string $table): array
523
    {
524
        $table = $this->protectIdentifiers($table, true, null, false);
8✔
525

526
        if (($query = $this->query('SHOW INDEX FROM ' . $table)) === false) {
8✔
UNCOV
527
            throw new DatabaseException(lang('Database.failGetIndexData'));
×
528
        }
529

530
        $indexes = $query->getResultArray();
8✔
531

532
        if ($indexes === []) {
8✔
533
            return [];
3✔
534
        }
535

536
        $keys = [];
7✔
537

538
        foreach ($indexes as $index) {
7✔
539
            if (empty($keys[$index['Key_name']])) {
7✔
540
                $keys[$index['Key_name']]       = new stdClass();
7✔
541
                $keys[$index['Key_name']]->name = $index['Key_name'];
7✔
542

543
                if ($index['Key_name'] === 'PRIMARY') {
7✔
544
                    $type = 'PRIMARY';
5✔
545
                } elseif ($index['Index_type'] === 'FULLTEXT') {
5✔
UNCOV
546
                    $type = 'FULLTEXT';
×
547
                } elseif ($index['Non_unique']) {
5✔
548
                    $type = $index['Index_type'] === 'SPATIAL' ? 'SPATIAL' : 'INDEX';
5✔
549
                } else {
550
                    $type = 'UNIQUE';
3✔
551
                }
552

553
                $keys[$index['Key_name']]->type = $type;
7✔
554
            }
555

556
            $keys[$index['Key_name']]->fields[] = $index['Column_name'];
7✔
557
        }
558

559
        return $keys;
7✔
560
    }
561

562
    /**
563
     * Returns an array of objects with Foreign key data
564
     *
565
     * @return array<string, stdClass>
566
     *
567
     * @throws DatabaseException
568
     */
569
    protected function _foreignKeyData(string $table): array
570
    {
571
        $sql = '
6✔
572
                SELECT
573
                    tc.CONSTRAINT_NAME,
574
                    tc.TABLE_NAME,
575
                    kcu.COLUMN_NAME,
576
                    rc.REFERENCED_TABLE_NAME,
577
                    kcu.REFERENCED_COLUMN_NAME,
578
                    rc.DELETE_RULE,
579
                    rc.UPDATE_RULE,
580
                    rc.MATCH_OPTION
581
                FROM information_schema.table_constraints AS tc
582
                INNER JOIN information_schema.referential_constraints AS rc
583
                    ON tc.constraint_name = rc.constraint_name
584
                    AND tc.constraint_schema = rc.constraint_schema
585
                INNER JOIN information_schema.key_column_usage AS kcu
586
                    ON tc.constraint_name = kcu.constraint_name
587
                    AND tc.constraint_schema = kcu.constraint_schema
588
                WHERE
589
                    tc.constraint_type = ' . $this->escape('FOREIGN KEY') . ' AND
6✔
590
                    tc.table_schema = ' . $this->escape($this->database) . ' AND
6✔
591
                    tc.table_name = ' . $this->escape($table);
6✔
592

593
        if (($query = $this->query($sql)) === false) {
6✔
UNCOV
594
            throw new DatabaseException(lang('Database.failGetForeignKeyData'));
×
595
        }
596

597
        $query   = $query->getResultObject();
6✔
598
        $indexes = [];
6✔
599

600
        foreach ($query as $row) {
6✔
601
            $indexes[$row->CONSTRAINT_NAME]['constraint_name']       = $row->CONSTRAINT_NAME;
5✔
602
            $indexes[$row->CONSTRAINT_NAME]['table_name']            = $row->TABLE_NAME;
5✔
603
            $indexes[$row->CONSTRAINT_NAME]['column_name'][]         = $row->COLUMN_NAME;
5✔
604
            $indexes[$row->CONSTRAINT_NAME]['foreign_table_name']    = $row->REFERENCED_TABLE_NAME;
5✔
605
            $indexes[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->REFERENCED_COLUMN_NAME;
5✔
606
            $indexes[$row->CONSTRAINT_NAME]['on_delete']             = $row->DELETE_RULE;
5✔
607
            $indexes[$row->CONSTRAINT_NAME]['on_update']             = $row->UPDATE_RULE;
5✔
608
            $indexes[$row->CONSTRAINT_NAME]['match']                 = $row->MATCH_OPTION;
5✔
609
        }
610

611
        return $this->foreignKeyDataToObjects($indexes);
6✔
612
    }
613

614
    /**
615
     * Returns platform-specific SQL to disable foreign key checks.
616
     *
617
     * @return string
618
     */
619
    protected function _disableForeignKeyChecks()
620
    {
621
        return 'SET FOREIGN_KEY_CHECKS=0';
868✔
622
    }
623

624
    /**
625
     * Returns platform-specific SQL to enable foreign key checks.
626
     *
627
     * @return string
628
     */
629
    protected function _enableForeignKeyChecks()
630
    {
631
        return 'SET FOREIGN_KEY_CHECKS=1';
868✔
632
    }
633

634
    /**
635
     * Returns the last error code and message.
636
     * Must return this format: ['code' => string|int, 'message' => string]
637
     * intval(code) === 0 means "no error".
638
     *
639
     * @return array{code: int|string|null, message: string|null}
640
     */
641
    public function error(): array
642
    {
643
        if (! empty($this->mysqli->connect_errno)) {
3✔
UNCOV
644
            return [
×
UNCOV
645
                'code'    => $this->mysqli->connect_errno,
×
UNCOV
646
                'message' => $this->mysqli->connect_error,
×
UNCOV
647
            ];
×
648
        }
649

650
        return [
3✔
651
            'code'    => $this->connID->errno,
3✔
652
            'message' => $this->connID->error,
3✔
653
        ];
3✔
654
    }
655

656
    /**
657
     * Insert ID
658
     */
659
    public function insertID(): int
660
    {
661
        return $this->connID->insert_id;
90✔
662
    }
663

664
    /**
665
     * Begin Transaction
666
     */
667
    protected function _transBegin(): bool
668
    {
669
        return $this->connID->begin_transaction();
58✔
670
    }
671

672
    /**
673
     * Commit Transaction
674
     */
675
    protected function _transCommit(): bool
676
    {
677
        return $this->connID->commit();
21✔
678
    }
679

680
    /**
681
     * Rollback Transaction
682
     */
683
    protected function _transRollback(): bool
684
    {
685
        return $this->connID->rollback();
46✔
686
    }
687
}
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