Passed
Pull Request — master (#355)
by Def
02:05
created

ConnectionPDO   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 25
eloc 55
dl 0
loc 167
rs 10
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A open() 0 22 4
A getCacheKey() 0 3 1
A __clone() 0 4 1
A getEmulatePrepare() 0 3 1
A close() 0 10 2
A getActivePDO() 0 10 2
A __construct() 0 6 1
A __sleep() 0 11 1
A getDriver() 0 3 1
A getServerVersion() 0 9 3
A setEmulatePrepare() 0 3 1
A isActive() 0 3 1
A getName() 0 3 1
A getLastInsertID() 0 8 4
A getPDO() 0 3 1
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\QueryCache;
11
use Yiisoft\Db\Cache\SchemaCache;
12
use Yiisoft\Db\Connection\Connection;
13
use Yiisoft\Db\Exception\Exception;
14
use Yiisoft\Db\Exception\InvalidCallException;
15
use Yiisoft\Db\Exception\InvalidConfigException;
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
abstract class ConnectionPDO extends Connection implements ConnectionPDOInterface
24
{
25
    protected ?PDO $pdo = null;
26
    protected string $serverVersion = '';
27
28
    protected ?bool $emulatePrepare = null;
29
30
    protected ?QueryBuilderInterface $queryBuilder = null;
31
    protected ?QuoterInterface $quoter = null;
32
    protected ?SchemaInterface $schema = null;
33
34
    public function __construct(
35
        protected PDODriverInterface $driver,
36
        protected QueryCache $queryCache,
37
        protected SchemaCache $schemaCache
38
    ) {
39
        parent::__construct($queryCache);
40
    }
41
42
    /**
43
     * Reset the connection after cloning.
44
     */
45
    public function __clone()
46
    {
47
        $this->transaction = null;
48
        $this->pdo = null;
49
    }
50
51
    /**
52
     * Close the connection before serializing.
53
     *
54
     * @return array
55
     */
56
    public function __sleep(): array
57
    {
58
        $fields = (array) $this;
59
60
        unset(
61
            $fields["\000*\000" . 'pdo'],
62
            $fields["\000*\000" . 'transaction'],
63
            $fields["\000*\000" . 'schema']
64
        );
65
66
        return array_keys($fields);
67
    }
68
69
    public function open(): void
70
    {
71
        if (!empty($this->pdo)) {
72
            return;
73
        }
74
75
        if (empty($this->driver->getDsn())) {
76
            throw new InvalidConfigException('Connection::dsn cannot be empty.');
77
        }
78
79
        $token = 'Opening DB connection: ' . $this->driver->getDsn();
80
81
        try {
82
            $this->logger?->log(LogLevel::INFO, $token);
83
            $this->profiler?->begin($token, [__METHOD__]);
84
            $this->initConnection();
85
            $this->profiler?->end($token, [__METHOD__]);
86
        } catch (PDOException $e) {
87
            $this->profiler?->end($token, [__METHOD__]);
88
            $this->logger?->log(LogLevel::ERROR, $token);
89
90
            throw new Exception($e->getMessage(), (array) $e->errorInfo, $e);
91
        }
92
    }
93
94
    public function close(): void
95
    {
96
        if ($this->pdo !== null) {
97
            $this->logger?->log(
98
                LogLevel::DEBUG,
99
                'Closing DB connection: ' . $this->driver->getDsn() . ' ' . __METHOD__,
100
            );
101
102
            $this->pdo = null;
103
            $this->transaction = null;
104
        }
105
    }
106
107
    public function getCacheKey(): array
108
    {
109
        return [$this->driver->getDsn(), $this->driver->getUsername()];
110
    }
111
112
    public function getDriver(): PDODriverInterface
113
    {
114
        return $this->driver;
115
    }
116
117
    public function getEmulatePrepare(): ?bool
118
    {
119
        return $this->emulatePrepare;
120
    }
121
122
    public function getPDO(): ?PDO
123
    {
124
        return $this->pdo;
125
    }
126
127
    /**
128
     * Input variables $sql and $forRead needs for future implementation of Connection + Pool
129
     *
130
     * @param string|null $sql
131
     * @param bool|null $forRead
132
     *
133
     * @throws Exception
134
     * @throws InvalidConfigException
135
     *
136
     * @return PDO
137
     */
138
    public function getActivePDO(?string $sql = '', ?bool $forRead = null): PDO
139
    {
140
        $this->open();
141
        $pdo = $this->getPDO();
142
143
        if ($pdo === null) {
144
            throw new Exception('PDO cannot be initialized.');
145
        }
146
147
        return $pdo;
148
    }
149
150
    public function getLastInsertID(?string $sequenceName = null): string
151
    {
152
        if ($this->isActive() && $this->pdo) {
153
            return $this->pdo->lastInsertID($sequenceName === null
154
                ? null : $this->getQuoter()->quoteTableName($sequenceName));
155
        }
156
157
        throw new InvalidCallException('DB Connection is not active.');
158
    }
159
160
    public function getName(): string
161
    {
162
        return $this->driver->getDriverName();
163
    }
164
165
    /**
166
     * @throws Exception
167
     */
168
    public function getServerVersion(): string
169
    {
170
        if ($this->serverVersion === '') {
171
            /** @var mixed */
172
            $version = $this->getActivePDO()->getAttribute(PDO::ATTR_SERVER_VERSION);
173
            $this->serverVersion = is_string($version) ? $version : 'Version could not be determined.';
174
        }
175
176
        return $this->serverVersion;
177
    }
178
179
    public function isActive(): bool
180
    {
181
        return $this->pdo !== null;
182
    }
183
184
    public function setEmulatePrepare(bool $value): void
185
    {
186
        $this->emulatePrepare = $value;
187
    }
188
189
    abstract protected function initConnection(): void;
190
}
191