Completed
Push — ezp24624-query_controller_take... ( f346e1...647162 )
by
unknown
23:39
created

ConnectionHandler::parseDSN()   F

Complexity

Conditions 24
Paths 18436

Size

Total Lines 136
Code Lines 85

Duplication

Lines 8
Ratio 5.88 %

Importance

Changes 0
Metric Value
cc 24
eloc 85
nc 18436
nop 1
dl 8
loc 136
rs 2
c 0
b 0
f 0

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
/**
4
 * File containing an interface for the Doctrine database abstractions.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Persistence\Doctrine;
12
13
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
14
use eZ\Publish\Core\Persistence\Database\QueryException;
15
use Doctrine\DBAL\Connection;
16
use Doctrine\DBAL\DriverManager;
17
use Doctrine\DBAL\DBALException;
18
19
class ConnectionHandler implements DatabaseHandler
20
{
21
    /**
22
     * @var \Doctrine\DBAL\Connection
23
     */
24
    protected $connection;
25
26
    /**
27
     * @param string|array $dsn
28
     *
29
     * @return \Doctrine\DBAL\Connection
30
     */
31
    public static function createConnectionFromDSN($dsn)
32
    {
33
        if (is_string($dsn)) {
34
            $parsed = self::parseDSN($dsn);
35
        } else {
36
            $parsed = $dsn;
37
        }
38
39
        return DriverManager::getConnection($parsed);
40
    }
41
42
    /**
43
     * Create a Connection Handler from given Doctrine $connection.
44
     *
45
     * @param \Doctrine\DBAL\Connection $connection
46
     *
47
     * @return \eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler
48
     */
49
    public static function createFromConnection(Connection $connection)
50
    {
51
        $driver = $connection->getDriver()->getName();
52
53
        if ($driver === 'pdo_sqlite') {
54
            return new ConnectionHandler\SqliteConnectionHandler($connection);
55
        }
56
57
        if ($driver === 'pdo_pgsql') {
58
            return new ConnectionHandler\PostgresConnectionHandler($connection);
59
        }
60
61
        return new self($connection);
62
    }
63
64
    /**
65
     * Create a Connection Handler with corresponding Doctrine connection from DSN.
66
     *
67
     * @param string|array $dsn
68
     *
69
     * @return ConnectionHandler
70
     */
71
    public static function createFromDSN($dsn)
72
    {
73
        if (is_string($dsn)) {
74
            $parsed = self::parseDSN($dsn);
75
        } else {
76
            $parsed = $dsn;
77
        }
78
79
        $connection = DriverManager::getConnection($parsed);
80
81
        if ($parsed['driver'] === 'pdo_sqlite') {
82
            return new ConnectionHandler\SqliteConnectionHandler($connection);
83
        }
84
85
        if ($parsed['driver'] === 'pdo_pgsql') {
86
            return new ConnectionHandler\PostgresConnectionHandler($connection);
87
        }
88
89
        return new self($connection);
90
    }
91
92
    /**
93
     * Returns the Data Source Name as a structure containing the various parts of the DSN.
94
     *
95
     * Additional keys can be added by appending a URI query string to the
96
     * end of the DSN.
97
     *
98
     * The format of the supplied DSN is in its fullest form:
99
     * <code>
100
     *  driver://user:password@protocol+host/database?option=8&another=true
101
     * </code>
102
     *
103
     * Most variations are allowed:
104
     * <code>
105
     *  driver://user:password@protocol+host:110//usr/db_file.db?mode=0644
106
     *  driver://user:password@host/dbname
107
     *  driver://user:password@host
108
     *  driver://user@host
109
     *  driver://host/dbname
110
     *  driver://host
111
     *  driver
112
     * </code>
113
     *
114
     * This function is 'borrowed' from PEAR /DB.php .
115
     *
116
     * @license Apache2 from Zeta Components Database project
117
     *
118
     * @param string $dsn Data Source Name to be parsed
119
     *
120
     * @return array an associative array with the following keys:
121
     *  + driver:  Database backend used in PHP (mysql, odbc etc.)
122
     *  + host: Host specification (hostname[:port])
123
     *  + dbname: Database to use on the DBMS server
124
     *  + username: User name for login
125
     *  + password: Password for login
126
     */
127
    public static function parseDSN($dsn)
128
    {
129
        $parsed = array(
130
            'driver' => false,
131
            'user' => false,
132
            'password' => false,
133
            'host' => false,
134
            'port' => false,
135
            'unix_socket' => false,
136
            'dbname' => false,
137
            'memory' => false,
138
            'path' => false,
139
        );
140
141
        // Find driver and dbsyntax
142
        if (($pos = strpos($dsn, '://')) !== false) {
143
            $str = substr($dsn, 0, $pos);
144
            $dsn = substr($dsn, $pos + 3);
145
        } else {
146
            $str = $dsn;
147
            $dsn = null;
148
        }
149
150
        // Get driver and dbsyntax
151
        // $str => driver(dbsyntax)
152
        if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
153
            $parsed['driver'] = $arr[1];
154
        } else {
155
            $parsed['driver'] = $str;
156
        }
157
158
        if (!count($dsn)) {
159
            return $parsed;
160
        }
161
162
        // Get (if found): username and password
163
        // $dsn => username:password@protocol+host/database
164
        if (($at = strrpos((string)$dsn, '@')) !== false) {
165
            $str = substr($dsn, 0, $at);
166
            $dsn = substr($dsn, $at + 1);
167
            if (($pos = strpos($str, ':')) !== false) {
168
                $parsed['user'] = rawurldecode(substr($str, 0, $pos));
169
                $parsed['password'] = rawurldecode(substr($str, $pos + 1));
170
            } else {
171
                $parsed['user'] = rawurldecode($str);
172
            }
173
        }
174
175
        // Find protocol and host
176
177
        if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
178
            // $dsn => proto(proto_opts)/database
179
            $proto = $match[1];
180
            $proto_opts = $match[2] ? $match[2] : false;
181
            $dsn = $match[3];
182
        } else {
183
            // $dsn => protocol+host/database (old format)
184 View Code Duplication
            if (strpos($dsn, '+') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
                list($proto, $dsn) = explode('+', $dsn, 2);
186
            }
187
            if (strpos($dsn, '/') !== false) {
188
                list($proto_opts, $dsn) = explode('/', $dsn, 2);
189
            } else {
190
                $proto_opts = $dsn;
191
                $dsn = null;
192
            }
193
        }
194
195
        // process the different protocol options
196
        $protocol = (!empty($proto)) ? $proto : 'tcp';
197
        $proto_opts = rawurldecode($proto_opts);
198
        if ($protocol === 'tcp') {
199
            if (strpos($proto_opts, ':') !== false) {
200
                list($parsed['host'], $parsed['port']) = explode(':', $proto_opts);
201
            } else {
202
                $parsed['host'] = $proto_opts;
203
            }
204
        } elseif ($protocol === 'unix') {
205
            $parsed['unix_socket'] = $proto_opts;
206
        }
207
208
        // Get dabase if any
209
        // $dsn => database
210
        if ($dsn) {
211
            if (($pos = strpos($dsn, '?')) === false) {
212
                // /database
213
                $parsed['dbname'] = rawurldecode($dsn);
214
            } else {
215
                // /database?param1=value1&param2=value2
216
                $parsed['dbname'] = rawurldecode(substr($dsn, 0, $pos));
217
                $dsn = substr($dsn, $pos + 1);
218 View Code Duplication
                if (strpos($dsn, '&') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
                    $opts = explode('&', $dsn);
220
                } else {
221
                    $opts = array($dsn);
222
                }
223
224
                foreach ($opts as $opt) {
225
                    list($key, $value) = explode('=', $opt);
226
                    if (!isset($parsed[$key])) {
227
                        // don't allow params overwrite
228
                        $parsed[$key] = rawurldecode($value);
229
                    }
230
                }
231
            }
232
        }
233
234
        if ($parsed['driver'] === 'sqlite') {
235
            if (isset($parsed['port']) && $parsed['port'] === 'memory') {
236
                $parsed['memory'] = true;
237
                unset($parsed['port']);
238
                unset($parsed['host']);
239
            } elseif (isset($parsed['dbname'])) {
240
                $parsed['path'] = $parsed['dbname'];
241
                unset($parsed['dbname']);
242
                unset($parsed['host']);
243
            }
244
        }
245
246
        $driverMap = array(
247
            'mysql' => 'pdo_mysql',
248
            'pgsql' => 'pdo_pgsql',
249
            'sqlite' => 'pdo_sqlite',
250
        );
251
252
        if (isset($driverMap[$parsed['driver']])) {
253
            $parsed['driver'] = $driverMap[$parsed['driver']];
254
        }
255
256
        return array_filter(
257
            $parsed,
258
            function ($element) {
259
                return $element !== false;
260
            }
261
        );
262
    }
263
264
    /**
265
     * @param \Doctrine\DBAL\Connection $connection
266
     */
267
    public function __construct(Connection $connection)
268
    {
269
        $this->connection = $connection;
270
    }
271
272
    /**
273
     * @return \Doctrine\DBAL\Connection
274
     */
275
    public function getConnection()
276
    {
277
        return $this->connection;
278
    }
279
280
    /**
281
     * @return string
282
     */
283
    public function getName()
284
    {
285
        return $this->connection->getDatabasePlatform()->getName();
286
    }
287
288
    /**
289
     * Begin a transaction.
290
     */
291
    public function beginTransaction()
292
    {
293
        try {
294
            $this->connection->beginTransaction();
295
        } catch (DBALException $e) {
296
            throw new QueryException($e->getMessage(), $e->getCode(), $e);
297
        }
298
    }
299
300
    /**
301
     * Commit a transaction.
302
     */
303
    public function commit()
304
    {
305
        try {
306
            $this->connection->commit();
307
        } catch (DBALException $e) {
308
            throw new QueryException($e->getMessage(), $e->getCode(), $e);
309
        }
310
    }
311
312
    /**
313
     * Rollback a transaction.
314
     */
315
    public function rollBack()
316
    {
317
        try {
318
            $this->connection->rollBack();
319
        } catch (DBALException $e) {
320
            throw new QueryException($e->getMessage(), $e->getCode(), $e);
321
        }
322
    }
323
324
    public function prepare($query)
325
    {
326
        return $this->connection->prepare($query);
327
    }
328
329
    /**
330
     * Retrieve the last auto incremet or sequence id.
331
     *
332
     * @param string $sequenceName
333
     *
334
     * @return string
335
     */
336
    public function lastInsertId($sequenceName = null)
337
    {
338
        return $this->connection->lastInsertId($sequenceName);
339
    }
340
341
    /**
342
     * @return bool
343
     */
344
    public function useSequences()
345
    {
346
        return $this->connection->getDatabasePlatform()->supportsSequences();
347
    }
348
349
    /**
350
     * Execute a query against the database.
351
     *
352
     * @param string $query
353
     */
354
    public function exec($query)
355
    {
356
        try {
357
            $this->connection->exec($query);
358
        } catch (DBALException $e) {
359
            throw new QueryException($e->getMessage(), $e->getCode(), $e);
360
        }
361
    }
362
363
    /**
364
     * Create Select Query object.
365
     *
366
     * @return \eZ\Publish\Core\Persistence\Database\SelectQuery
367
     */
368
    public function createSelectQuery()
369
    {
370
        return new SelectDoctrineQuery($this->connection);
371
    }
372
373
    /**
374
     * Create Insert Query object.
375
     *
376
     * @return \eZ\Publish\Core\Persistence\Database\InsertQuery
377
     */
378
    public function createInsertQuery()
379
    {
380
        return new InsertDoctrineQuery($this->connection);
381
    }
382
383
    /**
384
     * Create update Query object.
385
     *
386
     * @return \eZ\Publish\Core\Persistence\Database\UpdateQuery
387
     */
388
    public function createUpdateQuery()
389
    {
390
        return new UpdateDoctrineQuery($this->connection);
391
    }
392
393
    /**
394
     * Create a Delete Query object.
395
     *
396
     * @return \eZ\Publish\Core\Persistence\Database\DeleteQuery
397
     */
398
    public function createDeleteQuery()
399
    {
400
        return new DeleteDoctrineQuery($this->connection);
401
    }
402
403
    /**
404
     * Creates an alias for $tableName, $columnName in $query.
405
     *
406
     * @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query
407
     * @param string $columnName
408
     * @param string|null $tableName
409
     *
410
     * @return string
411
     */
412
    public function aliasedColumn($query, $columnName, $tableName = null)
413
    {
414
        return $this->alias(
415
            $this->quoteColumn($columnName, $tableName),
416
            $this->quoteIdentifier(
417
                ($tableName ? $tableName . '_' : '') .
418
                $columnName
419
            )
420
        );
421
    }
422
423
    /**
424
     * Returns a qualified identifier for $columnName in $tableName.
425
     *
426
     * @param string $columnName
427
     * @param string $tableName
428
     *
429
     * @return string
430
     */
431
    public function quoteColumn($columnName, $tableName = null)
432
    {
433
        return
434
            ($tableName ? $this->quoteTable($tableName) . '.' : '') .
435
            $this->quoteIdentifier($columnName);
436
    }
437
438
    /**
439
     * Returns a qualified identifier for $tableName.
440
     *
441
     * @param string $tableName
442
     *
443
     * @return string
444
     */
445
    public function quoteTable($tableName)
446
    {
447
        return $this->quoteIdentifier($tableName);
448
    }
449
450
    /**
451
     * Custom alias method.
452
     *
453
     * Ignores some properties of identifier quoting, but since we use somehow
454
     * sane table and column names, ourselves, this is fine.
455
     *
456
     * This is an optimization and works around the ezcDB implementation.
457
     *
458
     * @param string $identifier
0 ignored issues
show
Bug introduced by
There is no parameter named $identifier. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
459
     *
460
     * @return string
461
     */
462
    public function alias($name, $alias)
463
    {
464
        return $name . ' ' . $alias;
465
    }
466
467
    /**
468
     * Custom quote identifier method.
469
     *
470
     * Ignores some properties of identifier quoting, but since we use somehow
471
     * sane table and column names, ourselves, this is fine.
472
     *
473
     * This is an optimization and works around the ezcDB implementation.
474
     *
475
     * @param string $identifier
476
     *
477
     * @return string
478
     */
479
    public function quoteIdentifier($identifier)
480
    {
481
        return '`' . $identifier . '`';
482
    }
483
484
    /**
485
     * Get auto increment value.
486
     *
487
     * Returns the value used for autoincrement tables. Usually this will just
488
     * be null. In case for sequence based RDBMS this method can return a
489
     * proper value for the given column.
490
     *
491
     * @param string $table
492
     * @param string $column
493
     *
494
     * @return mixed
495
     */
496
    public function getAutoIncrementValue($table, $column)
497
    {
498
        return 'null';
499
    }
500
501
    /**
502
     * Returns the name of the affected sequence.
503
     *
504
     * @param string $table
505
     * @param string $column
506
     *
507
     * @return string
508
     */
509
    public function getSequenceName($table, $column)
510
    {
511
        return null;
512
    }
513
}
514