Passed
Push — develop ( fe1a17...a3aa5d )
by nguereza
03:43
created

Connection::connect()   A

Complexity

Conditions 3
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 22
rs 9.7998
cc 3
nc 5
nop 0
1
<?php
2
3
/**
4
 * Platine Database
5
 *
6
 * Platine Database is the abstraction layer using PDO with support of query and schema builder
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Database
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
/**
32
 *  @file Connection.php
33
 *
34
 *  The Database Connection class
35
 *
36
 *  @package    Platine\Database
37
 *  @author Platine Developers Team
38
 *  @copyright  Copyright (c) 2020
39
 *  @license    http://opensource.org/licenses/MIT  MIT License
40
 *  @link   http://www.iacademy.cf
41
 *  @version 1.0.0
42
 *  @filesource
43
 */
44
declare(strict_types=1);
45
46
namespace Platine\Database;
47
48
use InvalidArgumentException;
49
use PDO;
50
use PDOException;
51
use PDOStatement;
52
use Platine\Database\Driver\Driver;
53
use Platine\Database\Driver\MySQL;
54
use Platine\Database\Driver\Oracle;
55
use Platine\Database\Driver\PostgreSQL;
56
use Platine\Database\Driver\SQLite;
57
use Platine\Database\Driver\SQLServer;
58
use Platine\Database\Exception\ConnectionException;
59
use Platine\Database\Exception\QueryException;
60
use Platine\Logger\Logger;
61
use Platine\Logger\NullLogger;
62
63
/**
64
 * Class Connection
65
 * @package Platine\Database
66
 */
67
class Connection
68
{
69
70
    /**
71
     * The PDO instance
72
     * @var PDO|null
73
     */
74
    protected ?PDO $pdo = null;
75
76
    /**
77
     * The database driver name to use
78
     * @var string
79
     */
80
    protected string $driverName = '';
81
82
    /**
83
     * The PDO dsn
84
     * @var string
85
     */
86
    protected string $dsn = '';
87
88
    /**
89
     * The PDO connection options
90
     * @var array
91
     */
92
    protected array $options = [
93
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
94
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
95
        PDO::ATTR_STRINGIFY_FETCHES => false,
96
        PDO::ATTR_EMULATE_PREPARES => false,
97
    ];
98
99
    /**
100
     * The list of SQL command to execute after connection
101
     * @var array
102
     */
103
    protected array $commands = [];
104
105
    /**
106
     * The driver to use
107
     * @var Driver|null
108
     */
109
    protected ?Driver $driver = null;
110
111
    /**
112
     * The Schema instance to use
113
     * @var Schema|null
114
     */
115
    protected ?Schema $schema = null;
116
117
    /**
118
     * The driver options
119
     * @var array
120
     */
121
    protected array $driverOptions = [];
122
123
    /**
124
     * The connection configurations
125
     * @var array
126
     */
127
    protected array $config = [];
128
129
    /**
130
     * The connection parameters
131
     * @var array
132
     */
133
    protected array $params = [];
134
135
    /**
136
     * @var Logger|null
137
     */
138
    protected ?Logger $logger = null;
139
140
    /**
141
     * Connection constructor.
142
     * @param array $config
143
     * @param bool $autoConnect whether to connect to database automatically
144
     * @param Logger $logger
145
     * @throws ConnectionException
146
     */
147
    public function __construct(
148
        array $config = [],
149
        ?Logger $logger = null
150
    ) {
151
        $this->logger = $logger ? $logger : new Logger(new NullLogger());
152
153
        if (!empty($config)) {
154
            $this->setConfig($config);
155
        }
156
    }
157
158
    /**
159
     * Connect to the database
160
     * @return bool
161
     */
162
    public function connect(): bool
163
    {
164
        try {
165
            $this->pdo = new PDO(
166
                $this->dsn,
167
                $this->config['username'],
168
                $this->config['password'],
169
                $this->options
170
            );
171
172
            foreach ($this->commands as $command) {
173
                $this->pdo->exec($command);
174
            }
175
        } catch (PDOException $exception) {
176
            $this->logger->emergency('Can not connect to database. Error message: {error}', [
0 ignored issues
show
Bug introduced by
The method emergency() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
            $this->logger->/** @scrutinizer ignore-call */ 
177
                           emergency('Can not connect to database. Error message: {error}', [

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
177
                'exception' => $exception,
178
                'error' => $exception->getMessage()
179
            ]);
180
            throw new ConnectionException($exception->getMessage());
181
        }
182
183
        return $this->pdo !== null;
184
    }
185
186
    /**
187
     * Set the connection configuration
188
     * @param array $config
189
     * @return void
190
     */
191
    public function setConfig(array $config): void
192
    {
193
        $defaultConfig = [
194
            'driver' => 'mysql',
195
            'charset' => 'UTF8', //only for some drivers,
196
            'appname' => '', //only for MSSQL, DBLIB,
197
            'hostname' => 'localhost',
198
            'username' => '',
199
            'password' => '',
200
            'port' => null,
201
            'database' => '',
202
            'auto_connect' => false,
203
            'collation' => 'utf8_general_ci', //only for MySQL
204
            'socket' => '', //only for MySQL
205
            'options' => [],
206
            'commands' => [],
207
        ];
208
209
        $dbConfig = array_merge($defaultConfig, $config);
210
        $this->config = $dbConfig;
211
212
        $this->driverName = strtolower((string) $dbConfig['driver']);
213
214
        $options = array_replace($this->options, (array) $dbConfig['options']);
215
216
        $commands = array_merge($this->commands, $dbConfig['commands']);
217
218
        $port = null;
219
        $attr = [];
220
221
        if (is_int($dbConfig['port'])) {
222
            $port = $dbConfig['port'];
223
        }
224
225
        $driverName = $this->driverName;
226
        switch ($driverName) {
227
            case 'mysql':
228
            case 'pgsql':
229
                $attr = [
230
                    'driver' => $driverName,
231
                    'dbname' => $dbConfig['database'],
232
                    'host' => $dbConfig['hostname'],
233
                ];
234
235
                if ($port > 0) {
236
                    $attr['port'] = $port;
237
                }
238
239
                if ($driverName === 'mysql') {
240
                    //Make MySQL using standard quoted identifier
241
                    $commands[] = 'SET SQL_MODE=ANSI_QUOTES';
242
243
                    if (!empty($dbConfig['socket'])) {
244
                        $attr['unix_socket'] = $dbConfig['socket'];
245
246
                        unset($attr['host']);
247
                        unset($attr['port']);
248
                    }
249
                }
250
                break;
251
            case 'sqlsrv':
252
                //Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting
253
                $commands[] = 'SET QUOTED_IDENTIFIER ON';
254
255
                //Make ANSI_NULLS is ON for NULL value
256
                $commands[] = 'SET ANSI_NULLS ON';
257
258
                $attr = [
259
                    'driver' => 'sqlsrv',
260
                    'Server' => $dbConfig['hostname']
261
                        . ($port > 0 ? ':' . $port : ''),
262
                    'Database' => $dbConfig['database']
263
                ];
264
265
                if (!empty($dbConfig['appname'])) {
266
                    $attr['APP'] = $dbConfig['appname'];
267
                }
268
269
                $attributes = [
270
                    'ApplicationIntent',
271
                    'AttachDBFileName',
272
                    'Authentication',
273
                    'ColumnEncryption',
274
                    'ConnectionPooling',
275
                    'Encrypt',
276
                    'Failover_Partner',
277
                    'KeyStoreAuthentication',
278
                    'KeyStorePrincipalId',
279
                    'KeyStoreSecret',
280
                    'LoginTimeout',
281
                    'MultipleActiveResultSets',
282
                    'MultiSubnetFailover',
283
                    'Scrollable',
284
                    'TraceFile',
285
                    'TraceOn',
286
                    'TransactionIsolation',
287
                    'TransparentNetworkIPResolution',
288
                    'TrustServerCertificate',
289
                    'WSID',
290
                ];
291
292
                foreach ($attributes as $attribute) {
293
                    $keyname = strtolower(preg_replace(
294
                        ['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'],
295
                        '$1_$2',
296
                        $attribute
297
                    ));
298
299
                    if (isset($dbConfig[$keyname])) {
300
                        $attr[$attribute] = $dbConfig[$keyname];
301
                    }
302
                }
303
                break;
304
            case 'oci':
305
            case 'oracle':
306
                $database = $dbConfig['database'];
307
                $attr = [
308
                    'driver' => 'oci',
309
                    'dbname' => '//' . $dbConfig['hostname']
310
                    . ($port > 0 ? ':' . $port : ':1521') . '/' . $database
311
                ];
312
313
                $attr['charset'] = $dbConfig['charset'];
314
                break;
315
            case 'sqlite':
316
                $attr = [
317
                    'driver' => 'sqlite',
318
                    $dbConfig['database']
319
                ];
320
                break;
321
        }
322
323
        if (empty($attr)) {
324
            throw new InvalidArgumentException('Invalid database options supplied');
325
        }
326
327
        $driver = $attr['driver'];
328
        if (!in_array($driver, PDO::getAvailableDrivers())) {
329
            throw new InvalidArgumentException(sprintf(
330
                'Invalid database driver [%s], must be one of [%s]',
331
                $driver,
332
                implode(', ', PDO::getAvailableDrivers())
333
            ));
334
        }
335
        $this->params = $attr;
336
337
        unset($attr['driver']);
338
339
        $params = [];
340
        foreach ($attr as $key => $value) {
341
            $params[] = is_int($key) ? $value : $key . '=' . $value;
342
        }
343
344
        $dsn = $driver . ':' . implode(';', $params);
345
        if (in_array($driver, ['mysql', 'pgsql', 'sqlsrv'])) {
346
            $commands[] = 'SET NAMES "' . $dbConfig['charset'] . '"' . (
347
                    $this->driverName === 'mysql'
348
                            ? ' COLLATE "' . $dbConfig['collation'] . '"'
349
                            : ''
350
                    );
351
        }
352
353
        $this->dsn = $dsn;
354
        $this->commands = $commands;
355
        $this->options = $options;
356
357
        if (is_bool($dbConfig['auto_connect']) && $dbConfig['auto_connect']) {
358
            $this->connect();
359
        }
360
    }
361
362
    /**
363
     * Return the current connection parameters
364
     * @return array
365
     */
366
    public function getParams(): array
367
    {
368
        return $this->params;
369
    }
370
371
    /**
372
     * Return the current connection configuration
373
     * @return array
374
     */
375
    public function getConfig(): array
376
    {
377
        return $this->config;
378
    }
379
380
    /**
381
     * @param Logger $logger
382
     * @return self
383
     */
384
    public function setLogger(Logger $logger): self
385
    {
386
        $this->logger = $logger;
387
388
        return $this;
389
    }
390
391
    /**
392
     * Return the current driver instance
393
     * @return Driver
394
     */
395
    public function getDriver(): Driver
396
    {
397
        if ($this->driver === null) {
398
            $this->setDefaultDriver();
399
        }
400
        return $this->driver;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->driver could return the type null which is incompatible with the type-hinted return Platine\Database\Driver\Driver. Consider adding an additional type-check to rule them out.
Loading history...
401
    }
402
403
    /**
404
     * Set the custom driver to use
405
     * @param Driver $driver
406
     * @return self
407
     */
408
    public function setDriver(Driver $driver): self
409
    {
410
        $this->driver = $driver;
411
412
        return $this;
413
    }
414
415
    /**
416
     * Return the current Schema instance
417
     * @return Schema
418
     */
419
    public function getSchema(): Schema
420
    {
421
        if ($this->schema === null) {
422
            $this->schema = new Schema($this);
423
        }
424
        return $this->schema;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->schema could return the type null which is incompatible with the type-hinted return Platine\Database\Schema. Consider adding an additional type-check to rule them out.
Loading history...
425
    }
426
427
    /**
428
     * Set the custom schema to use
429
     * @param Schema $schema
430
     * @return self
431
     */
432
    public function setSchema(Schema $schema): self
433
    {
434
        $this->schema = $schema;
435
436
        return $this;
437
    }
438
439
    /**
440
     * Set the PDO connection option
441
     * @param string $name
442
     * @param mixed $value
443
     * @return self
444
     */
445
    public function setOption(string $name, $value): self
446
    {
447
        $this->options[$name] = $value;
448
449
        return $this;
450
    }
451
452
    /**
453
     * Set connection to be persistent
454
     * @param bool $value
455
     * @return self
456
     */
457
    public function persistent(bool $value = true): self
458
    {
459
        $this->setOption(PDO::ATTR_PERSISTENT, $value);
460
461
        return $this;
462
    }
463
464
    /**
465
     * Set the date format to use for the current driver
466
     * @param string $format
467
     * @return self
468
     */
469
    public function setDateFormat(string $format): self
470
    {
471
        $this->driverOptions['dateFormat'] = $format;
472
473
        return $this;
474
    }
475
476
    /**
477
     * Set the quote identifier to use for the current driver
478
     * @param string $identifier
479
     * @return self
480
     */
481
    public function setQuoteIdentifier(string $identifier): self
482
    {
483
        $this->driverOptions['identifier'] = $identifier;
484
485
        return $this;
486
    }
487
488
    /**
489
     * @return string
490
     */
491
    public function getDsn(): string
492
    {
493
        return $this->dsn;
494
    }
495
496
    /**
497
     * Return the name of the connection driver
498
     * @return string
499
     */
500
    public function getDriverName(): string
501
    {
502
        return $this->driverName;
503
    }
504
505
    /**
506
     * Return the instance of the PDO
507
     * @return PDO
508
     */
509
    public function getPDO(): PDO
510
    {
511
        if ($this->pdo === null) {
512
            $this->connect();
513
        }
514
        return $this->pdo;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->pdo could return the type null which is incompatible with the type-hinted return PDO. Consider adding an additional type-check to rule them out.
Loading history...
515
    }
516
517
    /**
518
     * CLose the connection
519
     */
520
    public function disconnect(): void
521
    {
522
        $this->pdo = null;
523
    }
524
525
    /**
526
     * Execute the SQL query and return the result
527
     * @param string $sql
528
     * @param array $params the query parameters
529
     * @return ResultSet
530
     * @throws QueryException
531
     */
532
    public function query(string $sql, array $params = []): ResultSet
533
    {
534
        $prepared = $this->prepare($sql, $params);
535
        $this->execute($prepared);
536
537
        return new ResultSet($prepared['statement']);
538
    }
539
540
    /**
541
     * Direct execute the SQL query
542
     * @param string $sql
543
     * @param array $params the query parameters
544
     * @return mixed
545
     * @throws QueryException
546
     */
547
    public function exec(string $sql, array $params = [])
548
    {
549
        return $this->execute($this->prepare($sql, $params));
550
    }
551
552
    /**
553
     *  Execute the SQL query and return the number
554
     * of affected rows
555
     * @param string $sql
556
     * @param array $params the query parameters
557
     * @return int
558
     * @throws QueryException
559
     */
560
    public function count(string $sql, array $params = []): int
561
    {
562
        $prepared = $this->prepare($sql, $params);
563
        $this->execute($prepared);
564
565
        $result = $prepared['statement']->rowCount();
566
        $prepared['statement']->closeCursor();
567
568
        return $result;
569
    }
570
571
    /**
572
     *  Execute the SQL query and return the first column result
573
     * @param string $sql
574
     * @param array $params the query parameters
575
     * @return mixed
576
     * @throws QueryException
577
     */
578
    public function column(string $sql, array $params = [])
579
    {
580
        $prepared = $this->prepare($sql, $params);
581
        $this->execute($prepared);
582
583
        $result = $prepared['statement']->fetchColumn();
584
        $prepared['statement']->closeCursor();
585
586
        return $result;
587
    }
588
589
    /**
590
     * @param callable $callback
591
     * @param mixed|null $that
592
     *
593
     * @return mixed
594
     *
595
     * @throws ConnectionException
596
     */
597
    public function transaction(
598
        callable $callback,
599
        $that = null
600
    ) {
601
        if ($that === null) {
602
            $that = $this;
603
        }
604
605
        $this->checkConnectionStatus();
606
607
        if ($this->pdo->inTransaction()) {
0 ignored issues
show
Bug introduced by
The method inTransaction() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

607
        if ($this->pdo->/** @scrutinizer ignore-call */ inTransaction()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
608
            return $callback($that);
609
        }
610
611
        try {
612
            $this->pdo->beginTransaction();
613
            $result = $callback($that);
614
            $this->pdo->commit();
615
        } catch (PDOException $exception) {
616
            $this->pdo->rollBack();
617
            $this->logger->error('Database transaction error. Error message: {error}', [
618
                'exception' => $exception,
619
                'error' => $exception->getMessage()
620
            ]);
621
            throw new ConnectionException($exception->getMessage());
622
        }
623
624
        return $result;
625
    }
626
627
    /**
628
     * Change the query parameters placeholder with the value
629
     * @param string $query
630
     * @param array $params
631
     * @return string
632
     */
633
    protected function replaceParameters(string $query, array $params): string
634
    {
635
        $driver = $this->getDriver();
636
637
        return preg_replace_callback(
638
            '/\?/',
639
            function () use ($driver, &$params) {
640
                $param = array_shift($params);
641
                $param = is_object($param) ? get_class($param) : $param;
642
                if (is_int($param) || is_float($param)) {
643
                    return $param;
644
                }
645
                if ($param === null) {
646
                    return 'NULL';
647
                }
648
                if (is_bool($param)) {
649
                    return $param ? 'TRUE' : 'FALSE';
650
                }
651
                return $driver->quote($param);
652
            },
653
            $query
654
        );
655
    }
656
657
    /**
658
     * Prepare the query
659
     * @param string $query
660
     * @param array $params
661
     * @return array
662
     * @throws QueryException
663
     */
664
    protected function prepare(string $query, array $params): array
665
    {
666
        $this->checkConnectionStatus();
667
        try {
668
            $statement = $this->pdo->prepare($query);
669
        } catch (PDOException $exception) {
670
            $sql = $this->replaceParameters($query, $params);
671
            $this->logger->error('Error when prepare query [{sql}]. Error message: {error}', [
672
                'exception' => $exception,
673
                'error' => $exception->getMessage(),
674
                'sql' => $sql
675
            ]);
676
            throw new QueryException(
677
                $exception->getMessage() . ' [' . $sql . ']',
678
                (int) $exception->getCode(),
679
                $exception->getPrevious()
680
            );
681
        }
682
683
        return [
684
            'statement' => $statement,
685
            'query' => $query,
686
            'params' => $params
687
        ];
688
    }
689
690
    /**
691
     * Execute the prepared query
692
     * @param array $prepared
693
     * @return bool the status of the execution
694
     * @throws QueryException
695
     */
696
    protected function execute(array $prepared): bool
697
    {
698
        $sql = $this->replaceParameters($prepared['query'], $prepared['params']);
699
        $sqlLog = [
700
            'query' => $prepared['query'],
701
            'parameters' => implode(', ', $prepared['params'])
702
        ];
703
704
        try {
705
            if ($prepared['params']) {
706
                $this->bindValues($prepared['statement'], $prepared['params']);
707
            }
708
            $start = microtime(true);
709
            $result = $prepared['statement']->execute();
710
            $sqlLog['time'] = number_format(microtime(true) - $start, 6);
711
712
            $this->logger->info(
713
                'Execute Query: [{query}], parameters: [{parameters}], time: [{time}]',
714
                $sqlLog
715
            );
716
        } catch (PDOException $exception) {
717
            $this->logger->error('Error when execute query [{sql}]. Error message: {error}', [
718
                'exception' => $exception,
719
                'error' => $exception->getMessage(),
720
                'sql' => $sql
721
            ]);
722
            throw new QueryException(
723
                $exception->getMessage() . ' [' . $sql . ']',
724
                (int) $exception->getCode(),
725
                $exception->getPrevious()
726
            );
727
        }
728
729
        return $result;
730
    }
731
732
    /**
733
     * Bind the parameters values
734
     * @param PDOStatement $statement
735
     * @param array $values
736
     */
737
    protected function bindValues(PDOStatement $statement, array $values): void
738
    {
739
        foreach ($values as $key => $value) {
740
            $param = PDO::PARAM_STR;
741
            if (is_null($value)) {
742
                $param = PDO::PARAM_NULL;
743
            } elseif (is_int($value) || is_float($value)) {
744
                $param = PDO::PARAM_INT;
745
            } elseif (is_bool($value)) {
746
                $param = PDO::PARAM_BOOL;
747
            }
748
749
            $statement->bindValue($key + 1, $value, $param);
750
        }
751
    }
752
753
    /**
754
     * Set the default driver instance using current driver name
755
     * @return void
756
     */
757
    protected function setDefaultDriver(): void
758
    {
759
        switch ($this->driverName) {
760
            case 'mysql':
761
                $this->driver = new MySQL($this);
762
                break;
763
            case 'pgsql':
764
                $this->driver = new PostgreSQL($this);
765
                break;
766
            case 'dblib':
767
            case 'mssql':
768
            case 'sqlsrv':
769
            case 'sybase':
770
                $this->driver = new SQLServer($this);
771
                break;
772
            case 'oci':
773
            case 'oracle':
774
                $this->driver = new Oracle($this);
775
                break;
776
            case 'sqlite':
777
                $this->driver = new SQLite($this);
778
                break;
779
            default:
780
                $this->driver = new Driver($this);
781
        }
782
        $this->driver->setOptions($this->driverOptions);
0 ignored issues
show
Bug introduced by
The method setOptions() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

782
        $this->driver->/** @scrutinizer ignore-call */ 
783
                       setOptions($this->driverOptions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
783
    }
784
785
    /**
786
     * Check the status of the connection
787
     * @return void
788
     * @throws ConnectionException
789
     */
790
    protected function checkConnectionStatus(): void
791
    {
792
        if ($this->pdo === null) {
793
            throw ConnectionException::connectionNotEstablished();
794
        }
795
    }
796
}
797