Passed
Push — master ( 9320d8...ec99d2 )
by Wilmer
20:40 queued 18:03
created

AbstractConnectionPDO::getDriver()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Driver\PDO;
6
7
use PDO;
8
use PDOException;
9
use Psr\Log\LogLevel;
10
use Yiisoft\Db\Cache\SchemaCache;
11
use Yiisoft\Db\Connection\AbstractConnection;
12
use Yiisoft\Db\Exception\Exception;
13
use Yiisoft\Db\Exception\InvalidCallException;
14
use Yiisoft\Db\Exception\InvalidConfigException;
15
use Yiisoft\Db\Profiler\Context\ConnectionContext;
16
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
17
use Yiisoft\Db\Schema\QuoterInterface;
18
use Yiisoft\Db\Schema\SchemaInterface;
19
20
use function array_keys;
21
use function is_string;
22
23
/**
24
 * Represents a connection to a database using the PDO (PHP Data Objects) extension.
25
 *
26
 * It provides a set of methods for interacting with a database using PDO, such as executing SQL statements, preparing
27
 * and executing statements, and managing transactions.
28
 *
29
 * The ConnectionPDO classes extend from this class, which is a base class for representing a connection to a database.
30
 *
31
 * It implements the ConnectionInterface, which defines the interface for interacting with a database connection.
32
 */
33
abstract class AbstractConnectionPDO extends AbstractConnection implements ConnectionPDOInterface
34
{
35
    protected PDO|null $pdo = null;
36
    protected string $serverVersion = '';
37
    protected bool|null $emulatePrepare = null;
38
    protected QueryBuilderInterface|null $queryBuilder = null;
39
    protected QuoterInterface|null $quoter = null;
40
    protected SchemaInterface|null $schema = null;
41
42
    public function __construct(protected PDODriverInterface $driver, protected SchemaCache $schemaCache)
43
    {
44
    }
45
46
    /**
47
     * Reset the connection after cloning.
48
     */
49
    public function __clone()
50
    {
51
        $this->transaction = null;
52
        $this->pdo = null;
53
    }
54
55
    /**
56
     * Close the connection before serializing.
57
     */
58
    public function __sleep(): array
59
    {
60
        $fields = (array) $this;
61
62
        unset(
63
            $fields["\000*\000" . 'pdo'],
64
            $fields["\000*\000" . 'transaction'],
65
            $fields["\000*\000" . 'schema']
66
        );
67
68
        return array_keys($fields);
69
    }
70
71
    public function open(): void
72
    {
73
        if ($this->pdo instanceof PDO) {
74
            return;
75
        }
76
77
        if ($this->driver->getDsn() === '') {
78
            throw new InvalidConfigException('Connection::dsn cannot be empty.');
79
        }
80
81
        $token = 'Opening DB connection: ' . $this->driver->getDsn();
82
        $connectionContext = new ConnectionContext(__METHOD__);
83
84
        try {
85
            $this->logger?->log(LogLevel::INFO, $token);
86
            $this->profiler?->begin($token, $connectionContext);
87
            $this->initConnection();
88
            $this->profiler?->end($token, $connectionContext);
89
        } catch (PDOException $e) {
90
            $this->profiler?->end($token, $connectionContext->setException($e));
91
            $this->logger?->log(LogLevel::ERROR, $token);
92
93
            throw new Exception($e->getMessage(), (array) $e->errorInfo, $e);
94
        }
95
    }
96
97
    public function close(): void
98
    {
99
        if ($this->pdo !== null) {
100
            $this->logger?->log(
101
                LogLevel::DEBUG,
102
                'Closing DB connection: ' . $this->driver->getDsn() . ' ' . __METHOD__,
103
            );
104
105
            $this->pdo = null;
106
            $this->transaction = null;
107
        }
108
    }
109
110
    public function getCacheKey(): array
111
    {
112
        return [$this->driver->getDsn(), $this->driver->getUsername()];
113
    }
114
115
    public function getDriver(): PDODriverInterface
116
    {
117
        return $this->driver;
118
    }
119
120
    public function getEmulatePrepare(): bool|null
121
    {
122
        return $this->emulatePrepare;
123
    }
124
125
    public function getActivePDO(string|null $sql = '', bool|null $forRead = null): PDO
126
    {
127
        $this->open();
128
        $pdo = $this->getPDO();
129
130
        if ($pdo === null) {
131
            throw new Exception('PDO cannot be initialized.');
132
        }
133
134
        return $pdo;
135
    }
136
137
    public function getPDO(): PDO|null
138
    {
139
        return $this->pdo;
140
    }
141
142
    public function getLastInsertID(string $sequenceName = null): string
143
    {
144
        if ($this->pdo !== null) {
145
            return $this->pdo->lastInsertID($sequenceName ?? null);
146
        }
147
148
        throw new InvalidCallException('DB Connection is not active.');
149
    }
150
151
    public function getDriverName(): string
152
    {
153
        return $this->driver->getDriverName();
154
    }
155
156
    public function getServerVersion(): string
157
    {
158
        if ($this->serverVersion === '') {
159
            /** @psalm-var mixed $version */
160
            $version = $this->getActivePDO()->getAttribute(PDO::ATTR_SERVER_VERSION);
161
            $this->serverVersion = is_string($version) ? $version : 'Version could not be determined.';
162
        }
163
164
        return $this->serverVersion;
165
    }
166
167
    public function isActive(): bool
168
    {
169
        return $this->pdo !== null;
170
    }
171
172
    public function quoteValue(mixed $value): mixed
173
    {
174
        if (is_string($value) === false) {
175
            return $value;
176
        }
177
178
        return $this->getActivePDO()->quote($value);
179
    }
180
181
    public function setEmulatePrepare(bool $value): void
182
    {
183
        $this->emulatePrepare = $value;
184
    }
185
186
    /**
187
     * Initializes the DB connection.
188
     *
189
     * This method is invoked right after the DB connection is established.
190
     *
191
     * The default implementation turns on `PDO::ATTR_EMULATE_PREPARES`, if {@see getEmulatePrepare()} is `true`.
192
     */
193
    protected function initConnection(): void
194
    {
195
        if ($this->getEmulatePrepare() !== null) {
196
            $this->driver->attributes([PDO::ATTR_EMULATE_PREPARES => $this->getEmulatePrepare()]);
197
        }
198
199
        $this->pdo = $this->driver->createConnection();
200
    }
201
}
202