Passed
Pull Request — master (#656)
by Wilmer
11:05 queued 08:56
created

AbstractConnection::close()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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