Completed
Pull Request — dev (#53)
by Def
09:50 queued 09:50
created

ConnectionPDOOracle::open()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 6.0045

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 9
nop 0
dl 0
loc 34
ccs 19
cts 20
cp 0.95
crap 6.0045
rs 8.9777
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Oracle\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\Connection\ConnectionPDOInterface;
14
use Yiisoft\Db\Driver\PDODriver;
15
use Yiisoft\Db\Exception\Exception;
16
use Yiisoft\Db\Exception\InvalidConfigException;
17
use Yiisoft\Db\Oracle\Quoter;
18
use Yiisoft\Db\Query\Query;
19
use Yiisoft\Db\Query\QueryBuilderInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Query\QueryBuilderInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use Yiisoft\Db\Schema\QuoterInterface;
21
use Yiisoft\Db\Schema\SchemaInterface;
22
use Yiisoft\Db\Transaction\TransactionInterface;
23
24
use function array_keys;
25
use function constant;
26
use function is_string;
27
use function strncmp;
28
29
/**
30
 * The class Connection represents a connection to a database via [PDO](https://secure.php.net/manual/en/book.pdo.php).
31
 */
32
final class ConnectionPDOOracle extends Connection implements ConnectionPDOInterface
33
{
34
    private ?PDO $pdo = null;
35
    private ?Query $query = null;
36
    private ?QueryBuilderInterface $queryBuilder = null;
37
    private ?QuoterInterface $quoter = null;
38
    private ?SchemaInterface $schema = null;
39
    private string $serverVersion = '';
40
41 379
    public function __construct(
42
        private PDODriver $driver,
43
        private QueryCache $queryCache,
44
        private SchemaCache $schemaCache
45
    ) {
46 379
        parent::__construct($queryCache);
47
    }
48
49
    /**
50
     * Reset the connection after cloning.
51
     */
52 1
    public function __clone()
53
    {
54 1
        $this->master = null;
55 1
        $this->slave = null;
56 1
        $this->transaction = null;
57
58 1
        if (strncmp($this->driver->getDsn(), 'sqlite::memory:', 15) !== 0) {
59
            /** reset PDO connection, unless its sqlite in-memory, which can only have one connection */
60 1
            $this->pdo = null;
61
        }
62
    }
63
64
    /**
65
     * Close the connection before serializing.
66
     *
67
     * @return array
68
     */
69 1
    public function __sleep(): array
70
    {
71 1
        $fields = (array) $this;
72
73
        unset(
74 1
            $fields["\000" . __CLASS__ . "\000" . 'pdo'],
75 1
            $fields["\000" . __CLASS__ . "\000" . 'master'],
76 1
            $fields["\000" . __CLASS__ . "\000" . 'slave'],
77 1
            $fields["\000" . __CLASS__ . "\000" . 'transaction'],
78 1
            $fields["\000" . __CLASS__ . "\000" . 'schema']
79
        );
80
81 1
        return array_keys($fields);
82
    }
83
84 331
    public function createCommand(?string $sql = null, array $params = []): CommandPDOOracle
85
    {
86 331
        $command = new CommandPDOOracle($this, $this->queryCache);
87
88 331
        if ($sql !== null) {
89 169
            $command->setSql($sql);
90
        }
91
92 331
        if ($this->logger !== null) {
93 331
            $command->setLogger($this->logger);
94
        }
95
96 331
        if ($this->profiler !== null) {
97 331
            $command->setProfiler($this->profiler);
98
        }
99
100 331
        return $command->bindValues($params);
101
    }
102
103 8
    public function createTransaction(): TransactionInterface
104
    {
105 8
        return new TransactionPDOOracle($this);
106
    }
107
108 379
    public function close(): void
109
    {
110 379
        if (!empty($this->master)) {
111
            /** @var ConnectionPDOOracle */
112 2
            $db = $this->master;
113
114 2
            if ($this->pdo === $db->getPDO()) {
0 ignored issues
show
Bug introduced by
The method getPDO() does not exist on Yiisoft\Db\Connection\ConnectionInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Yiisoft\Db\Connection\Connection. Are you sure you never get one of those? ( Ignorable by Annotation )

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

114
            if ($this->pdo === $db->/** @scrutinizer ignore-call */ getPDO()) {
Loading history...
115 2
                $this->pdo = null;
116
            }
117
118 2
            $db->close();
119 2
            $this->master = null;
120
        }
121
122 379
        if ($this->pdo !== null) {
123 172
            $this->logger?->log(
124
                LogLevel::DEBUG,
125 170
                'Closing DB connection: ' . $this->driver->getDsn() . ' ' . __METHOD__,
126
            );
127
128 172
            $this->pdo = null;
129 172
            $this->transaction = null;
130
        }
131
132 379
        if (!empty($this->slave)) {
133 1
            $this->slave->close();
134 1
            $this->slave = null;
135
        }
136
    }
137
138 349
    public function getDriver(): PDODriver
139
    {
140 349
        return $this->driver;
141
    }
142
143 7
    public function getDriverName(): string
144
    {
145 7
        return 'oci';
146
    }
147
148 170
    public function getMasterPdo(): PDO|null
149
    {
150 170
        $this->open();
151 170
        return $this->pdo;
152
    }
153
154 41
    public function getPDO(): ?PDO
155
    {
156 41
        return $this->pdo;
157
    }
158
159 331
    public function getQuery(): Query
160
    {
161 331
        if ($this->query === null) {
162 331
            $this->query = new Query($this);
163
        }
164
165 331
        return $this->query;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Query\Query. Consider adding an additional type-check to rule them out.
Loading history...
166
    }
167
168
    /**
169
     * @throws Exception|InvalidConfigException
170
     */
171 331
    public function getQueryBuilder(): QueryBuilderInterface
172
    {
173 331
        if ($this->queryBuilder === null) {
174 331
            $this->queryBuilder = new QueryBuilderPDOOracle(
175 331
                $this->createCommand(),
176 331
                $this->getQuery(),
177 331
                $this->getQuoter(),
178 331
                $this->getSchema(),
179
            );
180
        }
181
182 331
        return $this->queryBuilder;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->queryBuilder could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Query\QueryBuilderInterface. Consider adding an additional type-check to rule them out.
Loading history...
183
    }
184
185 344
    public function getQuoter(): QuoterInterface
186
    {
187 344
        if ($this->quoter === null) {
188 344
            $this->quoter = new Quoter('"', '"', $this->getTablePrefix());
189
        }
190
191 344
        return $this->quoter;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->quoter could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Schema\QuoterInterface. Consider adding an additional type-check to rule them out.
Loading history...
192
    }
193
194
    /**
195
     * @throws Exception
196
     */
197 2
    public function getServerVersion(): string
198
    {
199 2
        if ($this->serverVersion === '') {
200
            /** @var mixed */
201 2
            $version = $this->getSlavePDO()?->getAttribute(PDO::ATTR_SERVER_VERSION);
202 2
            $this->serverVersion = is_string($version) ? $version : 'Version could not be determined.';
203
        }
204
205 2
        return $this->serverVersion;
206
    }
207
208 348
    public function getSchema(): SchemaInterface
209
    {
210 348
        if ($this->schema === null) {
211 348
            $this->schema = new SchemaPDOOracle($this, $this->schemaCache);
212
        }
213
214 348
        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 Yiisoft\Db\Schema\SchemaInterface. Consider adding an additional type-check to rule them out.
Loading history...
215
    }
216
217 167
    public function getSlavePdo(bool $fallbackToMaster = true): ?PDO
218
    {
219
        /** @var ConnectionPDOOracle|null $db */
220 167
        $db = $this->getSlave(false);
221
222 167
        if ($db === null) {
223 166
            return $fallbackToMaster ? $this->getMasterPdo() : null;
224
        }
225
226 1
        return $db->getPDO();
227
    }
228
229 12
    public function isActive(): bool
230
    {
231 12
        return $this->pdo !== null;
232
    }
233
234 175
    public function open(): void
235
    {
236 175
        if (!empty($this->pdo)) {
237 151
            return;
238
        }
239
240 175
        if (!empty($this->masters)) {
241
            /** @var ConnectionPDOOracle|null */
242 2
            $db = $this->getMaster();
243
244 2
            if ($db !== null) {
245 2
                $this->pdo = $db->getPDO();
246 2
                return;
247
            }
248
249 2
            throw new InvalidConfigException('None of the master DB servers is available.');
250
        }
251
252 175
        if (empty($this->driver->getDsn())) {
253
            throw new InvalidConfigException('Connection::dsn cannot be empty.');
254
        }
255
256 175
        $token = 'Opening DB connection: ' . $this->driver->getDsn();
257
258
        try {
259 175
            $this->logger?->log(LogLevel::INFO, $token);
260 175
            $this->profiler?->begin($token, [__METHOD__]);
261 175
            $this->initConnection();
262 175
            $this->profiler?->end($token, [__METHOD__]);
263 3
        } catch (PDOException $e) {
264 3
            $this->profiler?->end($token, [__METHOD__]);
265 3
            $this->logger?->log(LogLevel::ERROR, $token);
266
267 3
            throw new Exception($e->getMessage(), (array) $e->errorInfo, $e);
268
        }
269
    }
270
271
    /**
272
     * Initializes the DB connection.
273
     *
274
     * This method is invoked right after the DB connection is established.
275
     *
276
     * The default implementation turns on `PDO::ATTR_EMULATE_PREPARES`.
277
     *
278
     * if {@see emulatePrepare} is true, and sets the database {@see charset} if it is not empty.
279
     *
280
     * It then triggers an {@see EVENT_AFTER_OPEN} event.
281
     */
282 175
    protected function initConnection(): void
283
    {
284 175
        $this->pdo = $this->driver->createConnection();
285 175
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
286
287 175
        if ($this->getEmulatePrepare() !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
288
            $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->getEmulatePrepare());
289
        }
290
    }
291
}
292