Failed Conditions
Push — master ( 7bbed5...cf98cc )
by Sergei
84:08 queued 81:00
created

DriverManager::getConnection()   F

Complexity

Conditions 14
Paths 448

Size

Total Lines 61
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 14

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 61
ccs 30
cts 30
cp 1
rs 2.8666
c 0
b 0
f 0
cc 14
nc 448
nop 3
crap 14

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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL;
21
22
use Doctrine\Common\EventManager;
23
use function array_keys;
24
use function array_map;
25
use function array_merge;
26
use function class_implements;
27
use function in_array;
28
use function is_subclass_of;
29
use function parse_str;
30
use function parse_url;
31
use function preg_replace;
32
use function str_replace;
33
use function strpos;
34
use function substr;
35
36
/**
37
 * Factory for creating Doctrine\DBAL\Connection instances.
38
 *
39
 * @author Roman Borschel <[email protected]>
40
 * @since 2.0
41
 */
42
final class DriverManager
43
{
44
    /**
45
     * List of supported drivers and their mappings to the driver classes.
46
     *
47
     * To add your own driver use the 'driverClass' parameter to
48
     * {@link DriverManager::getConnection()}.
49
     *
50
     * @var array
51
     */
52
     private static $_driverMap = [
53
         'pdo_mysql'          => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
54
         'pdo_sqlite'         => 'Doctrine\DBAL\Driver\PDOSqlite\Driver',
55
         'pdo_pgsql'          => 'Doctrine\DBAL\Driver\PDOPgSql\Driver',
56
         'pdo_oci'            => 'Doctrine\DBAL\Driver\PDOOracle\Driver',
57
         'oci8'               => 'Doctrine\DBAL\Driver\OCI8\Driver',
58
         'ibm_db2'            => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver',
59
         'pdo_sqlsrv'         => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver',
60
         'mysqli'             => 'Doctrine\DBAL\Driver\Mysqli\Driver',
61
         'drizzle_pdo_mysql'  => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver',
62
         'sqlanywhere'        => 'Doctrine\DBAL\Driver\SQLAnywhere\Driver',
63
         'sqlsrv'             => 'Doctrine\DBAL\Driver\SQLSrv\Driver',
64
    ];
65
66
    /**
67
     * List of URL schemes from a database URL and their mappings to driver.
68
     */
69
    private static $driverSchemeAliases = [
70
        'db2'        => 'ibm_db2',
71
        'mssql'      => 'pdo_sqlsrv',
72
        'mysql'      => 'pdo_mysql',
73
        'mysql2'     => 'pdo_mysql', // Amazon RDS, for some weird reason
74
        'postgres'   => 'pdo_pgsql',
75
        'postgresql' => 'pdo_pgsql',
76
        'pgsql'      => 'pdo_pgsql',
77
        'sqlite'     => 'pdo_sqlite',
78
        'sqlite3'    => 'pdo_sqlite',
79
    ];
80
81
    /**
82
     * Private constructor. This class cannot be instantiated.
83
     */
84
    private function __construct()
85
    {
86
    }
87
88
    /**
89
     * Creates a connection object based on the specified parameters.
90
     * This method returns a Doctrine\DBAL\Connection which wraps the underlying
91
     * driver connection.
92
     *
93
     * $params must contain at least one of the following.
94
     *
95
     * Either 'driver' with one of the following values:
96
     *
97
     *     pdo_mysql
98
     *     pdo_sqlite
99
     *     pdo_pgsql
100
     *     pdo_oci (unstable)
101
     *     pdo_sqlsrv
102
     *     pdo_sqlsrv
103
     *     mysqli
104
     *     sqlanywhere
105
     *     sqlsrv
106
     *     ibm_db2 (unstable)
107
     *     drizzle_pdo_mysql
108
     *
109
     * OR 'driverClass' that contains the full class name (with namespace) of the
110
     * driver class to instantiate.
111
     *
112
     * Other (optional) parameters:
113
     *
114
     * <b>user (string)</b>:
115
     * The username to use when connecting.
116
     *
117
     * <b>password (string)</b>:
118
     * The password to use when connecting.
119
     *
120
     * <b>driverOptions (array)</b>:
121
     * Any additional driver-specific options for the driver. These are just passed
122
     * through to the driver.
123
     *
124
     * <b>pdo</b>:
125
     * You can pass an existing PDO instance through this parameter. The PDO
126
     * instance will be wrapped in a Doctrine\DBAL\Connection.
127
     *
128
     * <b>wrapperClass</b>:
129
     * You may specify a custom wrapper class through the 'wrapperClass'
130
     * parameter but this class MUST inherit from Doctrine\DBAL\Connection.
131
     *
132
     * <b>driverClass</b>:
133
     * The driver class to use.
134
     *
135
     * @param array                              $params       The parameters.
136
     * @param \Doctrine\DBAL\Configuration|null  $config       The configuration to use.
137
     * @param \Doctrine\Common\EventManager|null $eventManager The event manager to use.
138
     *
139
     * @return \Doctrine\DBAL\Connection
140
     *
141
     * @throws \Doctrine\DBAL\DBALException
142
     */
143 2745
    public static function getConnection(
144
            array $params,
145
            Configuration $config = null,
146
            EventManager $eventManager = null): Connection
147
    {
148
        // create default config and event manager, if not set
149 2745
        if ( ! $config) {
150 2602
            $config = new Configuration();
151
        }
152 2745
        if ( ! $eventManager) {
153 2602
            $eventManager = new EventManager();
154
        }
155
156 2745
        $params = self::parseDatabaseUrl($params);
157
158
        // URL support for MasterSlaveConnection
159 2688
        if (isset($params['master'])) {
160 75
            $params['master'] = self::parseDatabaseUrl($params['master']);
161
        }
162
163 2688
        if (isset($params['slaves'])) {
164 75
            foreach ($params['slaves'] as $key => $slaveParams) {
165 75
                $params['slaves'][$key] = self::parseDatabaseUrl($slaveParams);
166
            }
167
        }
168
169
        // URL support for PoolingShardConnection
170 2688
        if (isset($params['global'])) {
171 285
            $params['global'] = self::parseDatabaseUrl($params['global']);
172
        }
173
174 2688
        if (isset($params['shards'])) {
175 285
            foreach ($params['shards'] as $key => $shardParams) {
176 285
                $params['shards'][$key] = self::parseDatabaseUrl($shardParams);
177
            }
178
        }
179
180
        // check for existing pdo object
181 2688
        if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) {
182 19
            throw DBALException::invalidPdoInstance();
183 2669
        } elseif (isset($params['pdo'])) {
184 95
            $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
185 95
            $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME);
186
        } else {
187 2574
            self::_checkParams($params);
188
        }
189
190 2612
        $className = $params['driverClass'] ?? self::$_driverMap[$params['driver']];
191
192 2612
        $driver = new $className();
193
194 2612
        $wrapperClass = 'Doctrine\DBAL\Connection';
195 2612
        if (isset($params['wrapperClass'])) {
196 512
            if (is_subclass_of($params['wrapperClass'], $wrapperClass)) {
197 493
               $wrapperClass = $params['wrapperClass'];
198
            } else {
199 19
                throw DBALException::invalidWrapperClass($params['wrapperClass']);
200
            }
201
        }
202
203 2593
        return new $wrapperClass($params, $driver, $config, $eventManager);
204
    }
205
206
    /**
207
     * Returns the list of supported drivers.
208
     *
209
     * @return array
210
     */
211
    public static function getAvailableDrivers(): array
212
    {
213
        return array_keys(self::$_driverMap);
214
    }
215
216
    /**
217
     * Checks the list of parameters.
218
     *
219
     * @param array $params The list of parameters.
220
     *
221
     * @return void
222
     *
223
     * @throws \Doctrine\DBAL\DBALException
224
     */
225 2574
    private static function _checkParams(array $params): void
226
    {
227
        // check existence of mandatory parameters
228
229
        // driver
230 2574
        if ( ! isset($params['driver']) && ! isset($params['driverClass'])) {
231 19
            throw DBALException::driverRequired();
232
        }
233
234
        // check validity of parameters
235
236
        // driver
237 2555
        if (isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) {
238 19
            throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap));
239
        }
240
241 2536
        if (isset($params['driverClass']) && ! in_array('Doctrine\DBAL\Driver', class_implements($params['driverClass'], true))) {
242 19
            throw DBALException::invalidDriverClass($params['driverClass']);
243
        }
244 2517
    }
245
246
    /**
247
     * Normalizes the given connection URL path.
248
     *
249
     * @param string $urlPath
250
     *
251
     * @return string The normalized connection URL path
252
     */
253 513
    private static function normalizeDatabaseUrlPath(string $urlPath): string
254
    {
255
        // Trim leading slash from URL path.
256 513
        return substr($urlPath, 1);
257
    }
258
259
    /**
260
     * Extracts parts from a database URL, if present, and returns an
261
     * updated list of parameters.
262
     *
263
     * @param array $params The list of parameters.
264
     *
265
     * @return array A modified list of parameters with info from a database
266
     *               URL extracted into indidivual parameter parts.
267
     *
268
     * @throws DBALException
269
     */
270 2745
    private static function parseDatabaseUrl(array $params): array
271
    {
272 2745
        if (!isset($params['url'])) {
273 2213
            return $params;
274
        }
275
276
        // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
277 570
        $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']);
278 570
        $url = parse_url($url);
279
280 570
        if ($url === false) {
281
            throw new DBALException('Malformed parameter "url".');
282
        }
283
284 570
        $url = array_map('rawurldecode', $url);
285
286
        // If we have a connection URL, we have to unset the default PDO instance connection parameter (if any)
287
        // as we cannot merge connection details from the URL into the PDO instance (URL takes precedence).
288 570
        unset($params['pdo']);
289
290 570
        $params = self::parseDatabaseUrlScheme($url, $params);
291
292 513
        if (isset($url['host'])) {
293 513
            $params['host'] = $url['host'];
294
        }
295 513
        if (isset($url['port'])) {
296 57
            $params['port'] = $url['port'];
297
        }
298 513
        if (isset($url['user'])) {
299 399
            $params['user'] = $url['user'];
300
        }
301 513
        if (isset($url['pass'])) {
302 399
            $params['password'] = $url['pass'];
303
        }
304
305 513
        $params = self::parseDatabaseUrlPath($url, $params);
306 513
        $params = self::parseDatabaseUrlQuery($url, $params);
307
308 513
        return $params;
309
    }
310
311
    /**
312
     * Parses the given connection URL and resolves the given connection parameters.
313
     *
314
     * Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters
315
     * via {@link parseDatabaseUrlScheme}.
316
     *
317
     * @param array $url    The URL parts to evaluate.
318
     * @param array $params The connection parameters to resolve.
319
     *
320
     * @return array The resolved connection parameters.
321
     *
322
     * @see parseDatabaseUrlScheme
323
     */
324 513
    private static function parseDatabaseUrlPath(array $url, array $params): array
325
    {
326 513
        if (! isset($url['path'])) {
327
            return $params;
328
        }
329
330 513
        $url['path'] = self::normalizeDatabaseUrlPath($url['path']);
331
332
        // If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate
333
        // and therefore treat the path as regular DBAL connection URL path.
334 513
        if (! isset($params['driver'])) {
335 19
            return self::parseRegularDatabaseUrlPath($url, $params);
336
        }
337
338 494
        if (strpos($params['driver'], 'sqlite') !== false) {
339 114
            return self::parseSqliteDatabaseUrlPath($url, $params);
340
        }
341
342 380
        return self::parseRegularDatabaseUrlPath($url, $params);
343
    }
344
345
    /**
346
     * Parses the query part of the given connection URL and resolves the given connection parameters.
347
     *
348
     * @param array $url    The connection URL parts to evaluate.
349
     * @param array $params The connection parameters to resolve.
350
     *
351
     * @return array The resolved connection parameters.
352
     */
353 513
    private static function parseDatabaseUrlQuery(array $url, array $params): array
354
    {
355 513
        if (! isset($url['query'])) {
356 494
            return $params;
357
        }
358
359 19
        $query = [];
360
361 19
        parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode
362
363 19
        return array_merge($params, $query); // parse_str wipes existing array elements
364
    }
365
366
    /**
367
     * Parses the given regular connection URL and resolves the given connection parameters.
368
     *
369
     * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}.
370
     *
371
     * @param array $url    The regular connection URL parts to evaluate.
372
     * @param array $params The connection parameters to resolve.
373
     *
374
     * @return array The resolved connection parameters.
375
     *
376
     * @see normalizeDatabaseUrlPath
377
     */
378 399
    private static function parseRegularDatabaseUrlPath(array $url, array $params): array
379
    {
380 399
        $params['dbname'] = $url['path'];
381
382 399
        return $params;
383
    }
384
385
    /**
386
     * Parses the given SQLite connection URL and resolves the given connection parameters.
387
     *
388
     * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}.
389
     *
390
     * @param array $url    The SQLite connection URL parts to evaluate.
391
     * @param array $params The connection parameters to resolve.
392
     *
393
     * @return array The resolved connection parameters.
394
     *
395
     * @see normalizeDatabaseUrlPath
396
     */
397 114
    private static function parseSqliteDatabaseUrlPath(array $url, array $params): array
398
    {
399 114
        if ($url['path'] === ':memory:') {
400 38
            $params['memory'] = true;
401
402 38
            return $params;
403
        }
404
405 76
        $params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key
406
407 76
        return $params;
408
    }
409
410
    /**
411
     * Parses the scheme part from given connection URL and resolves the given connection parameters.
412
     *
413
     * @param array $url    The connection URL parts to evaluate.
414
     * @param array $params The connection parameters to resolve.
415
     *
416
     * @return array The resolved connection parameters.
417
     *
418
     * @throws DBALException if parsing failed or resolution is not possible.
419
     */
420 570
    private static function parseDatabaseUrlScheme(array $url, array $params): array
421
    {
422 570
        if (isset($url['scheme'])) {
423
            // The requested driver from the URL scheme takes precedence
424
            // over the default custom driver from the connection parameters (if any).
425 437
            unset($params['driverClass']);
426
427
            // URL schemes must not contain underscores, but dashes are ok
428 437
            $driver = str_replace('-', '_', $url['scheme']);
429
430
            // The requested driver from the URL scheme takes precedence over the
431
            // default driver from the connection parameters. If the driver is
432
            // an alias (e.g. "postgres"), map it to the actual name ("pdo-pgsql").
433
            // Otherwise, let checkParams decide later if the driver exists.
434 437
            $params['driver'] = self::$driverSchemeAliases[$driver] ?? $driver;
435
436 437
            return $params;
437
        }
438
439
        // If a schemeless connection URL is given, we require a default driver or default custom driver
440
        // as connection parameter.
441 133
        if (! isset($params['driverClass']) && ! isset($params['driver'])) {
442 57
            throw DBALException::driverRequired($params['url']);
443
        }
444
445 76
        return $params;
446
    }
447
}
448