Passed
Pull Request — main (#116)
by Andreas
06:34
created

PDO::getAttribute()   D

Complexity

Conditions 21
Paths 21

Size

Total Lines 67
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 67
rs 4.1666
ccs 38
cts 41
cp 0.9268
crap 21.1728

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