Passed
Pull Request — main (#146)
by Andreas
01:38
created

PDO::getAttribute()   D

Complexity

Conditions 21
Paths 21

Size

Total Lines 66
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 21.1728

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 21
eloc 43
c 2
b 0
f 0
nc 21
nop 1
dl 0
loc 66
ccs 38
cts 41
cp 0.9268
crap 21.1728
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Licensed to CRATE Technology GmbH("Crate") under one or more contributor
4
 * license agreements.  See the NOTICE file distributed with this work for
5
 * additional information regarding copyright ownership.  Crate licenses
6
 * this file to you under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.  You may
8
 * obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
15
 * License for the specific language governing permissions and limitations
16
 * under the License.
17
 *
18
 * However, if you have executed another commercial license agreement
19
 * with Crate these terms will supersede the license and you may use the
20
 * software solely pursuant to the terms of the relevant commercial agreement.
21
 */
22
23
declare(strict_types=1);
24
25
namespace Crate\PDO;
26
27
use Crate\PDO\Exception\InvalidArgumentException;
28
use Crate\PDO\Exception\PDOException;
29
use Crate\PDO\Http\ServerInterface;
30
use Crate\PDO\Http\ServerPool;
31
use Crate\Stdlib\ArrayUtils;
32
use PDO as BasePDO;
33
use ReturnTypeWillChange;
0 ignored issues
show
Bug introduced by
The type ReturnTypeWillChange 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...
34
35
use const PHP_VERSION_ID;
36
37
class PDO extends BasePDO implements PDOInterface
38
{
39
    use PDOImplementation;
40
41
    public const VERSION     = '2.1.4';
42
    public const DRIVER_NAME = 'crate';
43
44
    public const DSN_REGEX = '/^(?:crate:)(?:((?:[\w\.-]+:\d+\,?)+))\/?([\w]+)?$/';
45
46
    public const CRATE_ATTR_HTTP_BASIC_AUTH = 1000;
47
    public const CRATE_ATTR_DEFAULT_SCHEMA  = 1001;
48
49
    public const CRATE_ATTR_SSL_MODE                                       = 1008;
50
    public const CRATE_ATTR_SSL_MODE_DISABLED                              = 1;
51
    public const CRATE_ATTR_SSL_MODE_ENABLED_BUT_WITHOUT_HOST_VERIFICATION = 2;
52
    public const CRATE_ATTR_SSL_MODE_REQUIRED                              = 3;
53
54
    public const CRATE_ATTR_SSL_KEY_PATH      = 1002;
55
    public const CRATE_ATTR_SSL_KEY_PASSWORD  = 1003;
56
    public const CRATE_ATTR_SSL_CERT_PATH     = 1004;
57
    public const CRATE_ATTR_SSL_CERT_PASSWORD = 1005;
58
    public const CRATE_ATTR_SSL_CA_PATH       = 1006;
59
    public const CRATE_ATTR_SSL_CA_PASSWORD   = 1007;
60
61
    public const PARAM_FLOAT     = 6;
62
    public const PARAM_DOUBLE    = 7;
63
    public const PARAM_LONG      = 8;
64
    public const PARAM_ARRAY     = 9;
65
    public const PARAM_OBJECT    = 10;
66
    public const PARAM_TIMESTAMP = 11;
67
    public const PARAM_IP        = 12;
68
69
    /**
70
     * @var array
71
     */
72
    private $attributes = [
73
        'defaultFetchMode' => self::FETCH_BOTH,
74
        'errorMode'        => self::ERRMODE_SILENT,
75
        'sslMode'          => self::CRATE_ATTR_SSL_MODE_DISABLED,
76
        'statementClass'   => PDOStatement::class,
77
        'timeout'          => 0.0,
78
        'auth'             => [],
79
        'defaultSchema'    => 'doc',
80
        'bulkMode'         => false,
81
    ];
82
83
    /**
84
     * @var Http\ServerInterface
85
     */
86
    private $server;
87
88
    /**
89
     * @var PDOStatement|null
90
     */
91
    private $lastStatement;
92
93
    /**
94
     * @var callable
95
     */
96
    private $request;
97
98
    /**
99
     * {@inheritDoc}
100
     *
101
     * @param string     $dsn      The HTTP endpoint to call
102
     * @param null       $username Username for basic auth
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $username is correct as it would always require null to be passed?
Loading history...
103
     * @param null       $passwd   Password for basic auth
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $passwd is correct as it would always require null to be passed?
Loading history...
104
     * @param null|array $options  Attributes to set on the PDO
105
     */
106 36
    public function __construct($dsn, $username = null, $passwd = null, $options = [])
107
    {
108
109 36
        if (PHP_VERSION_ID < 80000) {
110
            trigger_error(
111
                "`crate/crate-pdo` will stop supporting PHP7 on one of the upcoming " .
112
                "releases. Please upgrade to PHP8.",
113
                E_USER_DEPRECATED
114
            );
115
        }
116
117 36
        $dsnParts = self::parseDSN($dsn);
118 36
        $servers  = self::serversFromDsnParts($dsnParts);
119
120 36
        $this->setServer(new ServerPool($servers));
121
122 36
        foreach ((array)$options as $attribute => $value) {
123 2
            $this->setAttribute($attribute, $value);
124
        }
125
126 36
        if (!empty($username)) {
127 4
            $this->setAttribute(self::CRATE_ATTR_HTTP_BASIC_AUTH, [$username, $passwd]);
128
        }
129
130 36
        if (!empty($dsnParts[1])) {
131
            $this->setAttribute(self::CRATE_ATTR_DEFAULT_SCHEMA, $dsnParts[1]);
132
        }
133
134
        // Define a callback that will be used in the PDOStatements
135
        // This way we don't expose this as a public api to the end users.
136 36
        $this->request = function (PDOStatement $statement, $sql, array $parameters) {
137
138 2
            $this->lastStatement = $statement;
139
140
            try {
141 2
                if ($statement->isBulkMode()) {
142
                    return $this->server->executeBulk($sql, $parameters);
143
                } else {
144 2
                    return $this->server->execute($sql, $parameters);
145
                }
146
            } catch (Exception\RuntimeException $e) {
147
                if ($this->getAttribute(self::ATTR_ERRMODE) === self::ERRMODE_EXCEPTION) {
148
                    throw new Exception\PDOException($e->getMessage(), $e->getCode());
149
                }
150
151
                if ($this->getAttribute(self::ATTR_ERRMODE) === self::ERRMODE_WARNING) {
152
                    trigger_error(sprintf('[%d] %s', $e->getCode(), $e->getMessage()), E_USER_WARNING);
153
                }
154
155
                // should probably wrap this in a error object ?
156
                return [
157
                    'code'    => $e->getCode(),
158
                    'message' => $e->getMessage(),
159
                ];
160
            }
161 36
        };
162
    }
163
164
    /**
165
     * Change the server implementation
166
     *
167
     * @param ServerInterface $server
168
     */
169 28
    public function setServer(ServerInterface $server): void
170
    {
171 28
        $this->server = $server;
172 28
        $this->server->configure($this);
173
    }
174
175
    /**
176
     * Extract servers and optional custom schema from DSN string
177
     *
178
     * @param string $dsn The DSN string
179
     *
180
     * @throws \Crate\PDO\Exception\PDOException on an invalid DSN string
181
     *
182
     * @return array An array of ['host:post,host:port,...', 'schema']
183
     */
184 110
    private static function parseDSN($dsn)
185
    {
186 110
        $matches = [];
187
188 110
        if (!preg_match(static::DSN_REGEX, $dsn, $matches)) {
189 8
            throw new PDOException(sprintf('Invalid DSN %s', $dsn));
190
        }
191
192 102
        return array_slice($matches, 1);
193
    }
194
195
    /**
196
     * Extract host:port pairs out of the DSN parts
197
     *
198
     * @param array $dsnParts The parts of the parsed DSN string
199
     *
200
     * @return array An array of host:port strings
201
     */
202 100
    private static function serversFromDsnParts($dsnParts)
203
    {
204 100
        return explode(',', trim($dsnParts[0], ','));
205
    }
206
207
    /**
208
     * {@inheritDoc}
209
     */
210 4
    #[ReturnTypeWillChange]
211
    public function prepare(string $statement, array $options = [])
212
    {
213 4
        $options = ArrayUtils::toArray($options);
214
215 4
        if (isset($options[self::ATTR_CURSOR])) {
216
            trigger_error(sprintf('%s not supported', __METHOD__), E_USER_WARNING);
217
218
            return true;
219
        }
220
221 4
        $className = $this->attributes['statementClass'];
222
223 4
        return new $className($this, $this->request, $statement, $options);
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 2
    public function beginTransaction(): bool
230
    {
231 2
        return true;
232
    }
233
234
    /**
235
     * {@inheritDoc}
236
     */
237 2
    public function commit(): bool
238
    {
239 2
        return true;
240
    }
241
242
    /**
243
     * {@inheritDoc}
244
     */
245 2
    public function rollBack(): bool
246
    {
247 2
        throw new Exception\UnsupportedException;
248
    }
249
250
    /**
251
     * {@inheritDoc}
252
     */
253 2
    public function inTransaction(): bool
254
    {
255 2
        return false;
256
    }
257
258
    /**
259
     * {@inheritDoc}
260
     */
261
    public function exec($statement): int
262
    {
263
        $statement = $this->prepare($statement);
264
        $result    = $statement->execute();
265
266
        return $result === false ? false : $statement->rowCount();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result === false... $statement->rowCount() could return the type false which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272 2
    public function doQuery($statement)
273
    {
274 2
        $statement = $this->prepare($statement);
275 2
        $result    = $statement->execute();
276
277 2
        return $result === false ? false : $statement;
278
    }
279
280
    /**
281
     * {@inheritDoc}
282
     */
283 2
    public function lastInsertId(?string $name = null): string
284
    {
285 2
        throw new Exception\UnsupportedException;
286
    }
287
288
    /**
289
     * {@inheritDoc}
290
     */
291
    public function errorCode(): ?string
292
    {
293
        return $this->lastStatement === null ? null : $this->lastStatement->errorCode();
294
    }
295
296
    /**
297
     * {@inheritDoc}
298
     */
299
    public function errorInfo(): array
300
    {
301
        return $this->lastStatement === null ? null : $this->lastStatement->errorInfo();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->lastStatem...tStatement->errorInfo() could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
302
    }
303
304
    /**
305
     * {@inheritDoc}
306
     *
307
     * @throws \Crate\PDO\Exception\PDOException
308
     * @throws \Crate\PDO\Exception\InvalidArgumentException
309
     */
310 30
    public function setAttribute(int $attribute, $value): bool
311
    {
312
        switch ($attribute) {
313 30
            case self::ATTR_DEFAULT_FETCH_MODE:
314 2
                $this->attributes['defaultFetchMode'] = $value;
315 2
                break;
316
317 28
            case self::ATTR_ERRMODE:
318 2
                $this->attributes['errorMode'] = $value;
319 2
                break;
320
321 26
            case self::ATTR_STATEMENT_CLASS:
322 2
                $this->attributes['statementClass'] = $value;
323 2
                break;
324
325 24
            case self::ATTR_TIMEOUT:
326 4
                $this->attributes['timeout'] = (int)$value;
327 4
                break;
328
329 22
            case self::CRATE_ATTR_HTTP_BASIC_AUTH:
330 4
                if (!is_array($value) && $value !== null) {
331
                    throw new InvalidArgumentException(
332
                        'Value probided to CRATE_ATTR_HTTP_BASIC_AUTH must be null or an array'
333
                    );
334
                }
335
336 4
                $this->attributes['auth'] = $value;
337 4
                break;
338
339 22
            case self::CRATE_ATTR_DEFAULT_SCHEMA:
340 4
                $this->attributes['defaultSchema'] = $value;
341 4
                break;
342
343 18
            case self::CRATE_ATTR_SSL_MODE:
344 18
                $this->attributes['sslMode'] = $value;
345 18
                break;
346
347 12
            case self::CRATE_ATTR_SSL_CA_PATH:
348 4
                $this->attributes['sslCa'] = $value;
349 4
                break;
350
351 10
            case self::CRATE_ATTR_SSL_CA_PASSWORD:
352 2
                $this->attributes['sslCaPassword'] = $value;
353 2
                break;
354
355 8
            case self::CRATE_ATTR_SSL_CERT_PATH:
356 4
                $this->attributes['sslCert'] = $value;
357 4
                break;
358
359 6
            case self::CRATE_ATTR_SSL_CERT_PASSWORD:
360 2
                $this->attributes['sslCertPassword'] = $value;
361 2
                break;
362
363 4
            case self::CRATE_ATTR_SSL_KEY_PATH:
364 4
                $this->attributes['sslKey'] = $value;
365 4
                break;
366
367 2
            case self::CRATE_ATTR_SSL_KEY_PASSWORD:
368 2
                $this->attributes['sslKeyPassword'] = $value;
369 2
                break;
370
371
            default:
372
                throw new Exception\PDOException('Unsupported driver attribute');
373
        }
374
375
        // A setting changed so we need to reconfigure the server pool
376 30
        $this->server->configure($this);
377 30
        return true;
378
    }
379
380
    /**
381
     * {@inheritDoc}
382
     *
383
     * @throws \Crate\PDO\Exception\PDOException
384
     */
385 50
    #[\ReturnTypeWillChange]
386
    public function getAttribute(int $attribute)
387
    {
388
        switch ($attribute) {
389 50
            case self::ATTR_PREFETCH:
390 50
            case self::ATTR_PERSISTENT:
391 4
                return false;
392
393 50
            case self::ATTR_CLIENT_VERSION:
394 2
                return self::VERSION;
395
396 50
            case self::ATTR_SERVER_VERSION:
397
                return $this->server->getServerVersion();
398
399 50
            case self::ATTR_SERVER_INFO:
400
                return $this->server->getServerInfo();
401
402 50
            case self::ATTR_TIMEOUT:
403 50
                return $this->attributes['timeout'];
404
405 50
            case self::CRATE_ATTR_HTTP_BASIC_AUTH:
406 50
                return $this->attributes['auth'];
407
408 50
            case self::ATTR_DEFAULT_FETCH_MODE:
409 4
                return $this->attributes['defaultFetchMode'];
410
411 50
            case self::ATTR_ERRMODE:
412 2
                return $this->attributes['errorMode'];
413
414 50
            case self::ATTR_DRIVER_NAME:
415 2
                return static::DRIVER_NAME;
416
417 50
            case self::ATTR_STATEMENT_CLASS:
418 2
                return [$this->attributes['statementClass']];
419
420 50
            case self::CRATE_ATTR_DEFAULT_SCHEMA:
421 50
                return $this->attributes['defaultSchema'];
422
423 50
            case self::CRATE_ATTR_SSL_MODE:
424 50
                return $this->attributes['sslMode'];
425
426 50
            case self::CRATE_ATTR_SSL_CA_PATH:
427 50
                return $this->attributes['sslCa'] ?? null;
428
429 50
            case self::CRATE_ATTR_SSL_CA_PASSWORD:
430 50
                return $this->attributes['sslCaPassword'] ?? null;
431
432 50
            case self::CRATE_ATTR_SSL_CERT_PATH:
433 50
                return $this->attributes['sslCert'] ?? null;
434
435 50
            case self::CRATE_ATTR_SSL_CERT_PASSWORD:
436 50
                return $this->attributes['sslCertPassword'] ?? null;
437
438 50
            case self::CRATE_ATTR_SSL_KEY_PATH:
439 50
                return $this->attributes['sslKey'] ?? null;
440
441 50
            case self::CRATE_ATTR_SSL_KEY_PASSWORD:
442 50
                return $this->attributes['sslKeyPassword'] ?? null;
443
444
            default:
445
                // PHP Switch is a lose comparison
446 2
                if ($attribute === self::ATTR_AUTOCOMMIT) {
447 2
                    return true;
448
                }
449
450
                throw new Exception\PDOException(sprintf('Unsupported driver attribute: %s', $attribute));
451
        }
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457 8
    #[ReturnTypeWillChange]
458
    public function quote(string $string, int $parameter_type = self::PARAM_STR)
459
    {
460
        switch ($parameter_type) {
461 8
            case self::PARAM_INT:
462 2
                return (int)$string;
463
464 8
            case self::PARAM_BOOL:
465 2
                return (bool)$string;
466
467 8
            case self::PARAM_NULL:
468 2
                return null;
469
470 6
            case self::PARAM_LOB:
471 2
                throw new Exception\UnsupportedException('This is not supported by crate.io');
472
473 4
            case self::PARAM_STR:
474 2
                throw new Exception\UnsupportedException('This is not supported, please use prepared statements.');
475
476
            default:
477 2
                throw new Exception\InvalidArgumentException('Unknown param type');
478
        }
479
    }
480
481
    /**
482
     * {@inheritDoc}
483
     */
484 2
    public static function getAvailableDrivers(): array
485
    {
486 2
        return array_merge(parent::getAvailableDrivers(), [static::DRIVER_NAME]);
487
    }
488
489
    public function getServerVersion(): string
490
    {
491
        return $this->server->getServerVersion();
492
    }
493
494
    public function getServerInfo(): string
495
    {
496
        return $this->getServerVersion();
497
    }
498
}
499