| 148 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
namespace Doctrine\DBAL;
|
|
|
4 |
|
|
|
5 |
use Doctrine\Common\EventManager;
|
|
|
6 |
use Doctrine\DBAL\Driver\IBMDB2;
|
|
|
7 |
use Doctrine\DBAL\Driver\Mysqli;
|
|
|
8 |
use Doctrine\DBAL\Driver\OCI8;
|
|
|
9 |
use Doctrine\DBAL\Driver\PDO;
|
| 621 |
lars |
10 |
use Doctrine\DBAL\Driver\PgSQL;
|
| 148 |
lars |
11 |
use Doctrine\DBAL\Driver\SQLite3;
|
|
|
12 |
use Doctrine\DBAL\Driver\SQLSrv;
|
| 621 |
lars |
13 |
use Doctrine\DBAL\Exception\MalformedDsnException;
|
|
|
14 |
use Doctrine\DBAL\Tools\DsnParser;
|
| 148 |
lars |
15 |
use Doctrine\Deprecations\Deprecation;
|
| 621 |
lars |
16 |
use SensitiveParameter;
|
| 148 |
lars |
17 |
|
|
|
18 |
use function array_keys;
|
|
|
19 |
use function array_merge;
|
|
|
20 |
use function is_a;
|
|
|
21 |
|
|
|
22 |
/**
|
|
|
23 |
* Factory for creating {@see Connection} instances.
|
|
|
24 |
*
|
|
|
25 |
* @psalm-type OverrideParams = array{
|
| 1464 |
lars |
26 |
* application_name?: string,
|
| 148 |
lars |
27 |
* charset?: string,
|
|
|
28 |
* dbname?: string,
|
|
|
29 |
* default_dbname?: string,
|
|
|
30 |
* driver?: key-of<self::DRIVER_MAP>,
|
|
|
31 |
* driverClass?: class-string<Driver>,
|
|
|
32 |
* driverOptions?: array<mixed>,
|
|
|
33 |
* host?: string,
|
|
|
34 |
* password?: string,
|
|
|
35 |
* path?: string,
|
| 1464 |
lars |
36 |
* persistent?: bool,
|
| 148 |
lars |
37 |
* platform?: Platforms\AbstractPlatform,
|
|
|
38 |
* port?: int,
|
| 1464 |
lars |
39 |
* serverVersion?: string,
|
| 621 |
lars |
40 |
* url?: string,
|
| 148 |
lars |
41 |
* user?: string,
|
|
|
42 |
* unix_socket?: string,
|
|
|
43 |
* }
|
|
|
44 |
* @psalm-type Params = array{
|
| 1464 |
lars |
45 |
* application_name?: string,
|
| 148 |
lars |
46 |
* charset?: string,
|
|
|
47 |
* dbname?: string,
|
|
|
48 |
* defaultTableOptions?: array<string, mixed>,
|
|
|
49 |
* default_dbname?: string,
|
|
|
50 |
* driver?: key-of<self::DRIVER_MAP>,
|
|
|
51 |
* driverClass?: class-string<Driver>,
|
|
|
52 |
* driverOptions?: array<mixed>,
|
|
|
53 |
* host?: string,
|
|
|
54 |
* keepSlave?: bool,
|
|
|
55 |
* keepReplica?: bool,
|
|
|
56 |
* master?: OverrideParams,
|
|
|
57 |
* memory?: bool,
|
|
|
58 |
* password?: string,
|
|
|
59 |
* path?: string,
|
| 1464 |
lars |
60 |
* persistent?: bool,
|
| 148 |
lars |
61 |
* platform?: Platforms\AbstractPlatform,
|
|
|
62 |
* port?: int,
|
|
|
63 |
* primary?: OverrideParams,
|
|
|
64 |
* replica?: array<OverrideParams>,
|
|
|
65 |
* serverVersion?: string,
|
|
|
66 |
* sharding?: array<string,mixed>,
|
|
|
67 |
* slaves?: array<OverrideParams>,
|
|
|
68 |
* url?: string,
|
|
|
69 |
* user?: string,
|
|
|
70 |
* wrapperClass?: class-string<Connection>,
|
|
|
71 |
* unix_socket?: string,
|
|
|
72 |
* }
|
|
|
73 |
*/
|
|
|
74 |
final class DriverManager
|
|
|
75 |
{
|
|
|
76 |
/**
|
|
|
77 |
* List of supported drivers and their mappings to the driver classes.
|
|
|
78 |
*
|
|
|
79 |
* To add your own driver use the 'driverClass' parameter to {@see DriverManager::getConnection()}.
|
|
|
80 |
*/
|
|
|
81 |
private const DRIVER_MAP = [
|
|
|
82 |
'pdo_mysql' => PDO\MySQL\Driver::class,
|
|
|
83 |
'pdo_sqlite' => PDO\SQLite\Driver::class,
|
|
|
84 |
'pdo_pgsql' => PDO\PgSQL\Driver::class,
|
|
|
85 |
'pdo_oci' => PDO\OCI\Driver::class,
|
|
|
86 |
'oci8' => OCI8\Driver::class,
|
|
|
87 |
'ibm_db2' => IBMDB2\Driver::class,
|
|
|
88 |
'pdo_sqlsrv' => PDO\SQLSrv\Driver::class,
|
|
|
89 |
'mysqli' => Mysqli\Driver::class,
|
| 621 |
lars |
90 |
'pgsql' => PgSQL\Driver::class,
|
| 148 |
lars |
91 |
'sqlsrv' => SQLSrv\Driver::class,
|
|
|
92 |
'sqlite3' => SQLite3\Driver::class,
|
|
|
93 |
];
|
|
|
94 |
|
|
|
95 |
/**
|
|
|
96 |
* List of URL schemes from a database URL and their mappings to driver.
|
|
|
97 |
*
|
|
|
98 |
* @deprecated Use actual driver names instead.
|
|
|
99 |
*
|
|
|
100 |
* @var string[]
|
|
|
101 |
*/
|
|
|
102 |
private static array $driverSchemeAliases = [
|
|
|
103 |
'db2' => 'ibm_db2',
|
|
|
104 |
'mssql' => 'pdo_sqlsrv',
|
|
|
105 |
'mysql' => 'pdo_mysql',
|
|
|
106 |
'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason
|
|
|
107 |
'postgres' => 'pdo_pgsql',
|
|
|
108 |
'postgresql' => 'pdo_pgsql',
|
|
|
109 |
'pgsql' => 'pdo_pgsql',
|
|
|
110 |
'sqlite' => 'pdo_sqlite',
|
|
|
111 |
'sqlite3' => 'pdo_sqlite',
|
|
|
112 |
];
|
|
|
113 |
|
|
|
114 |
/**
|
|
|
115 |
* Private constructor. This class cannot be instantiated.
|
|
|
116 |
*
|
|
|
117 |
* @codeCoverageIgnore
|
|
|
118 |
*/
|
|
|
119 |
private function __construct()
|
|
|
120 |
{
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
/**
|
|
|
124 |
* Creates a connection object based on the specified parameters.
|
|
|
125 |
* This method returns a Doctrine\DBAL\Connection which wraps the underlying
|
|
|
126 |
* driver connection.
|
|
|
127 |
*
|
|
|
128 |
* $params must contain at least one of the following.
|
|
|
129 |
*
|
|
|
130 |
* Either 'driver' with one of the array keys of {@see DRIVER_MAP},
|
|
|
131 |
* OR 'driverClass' that contains the full class name (with namespace) of the
|
|
|
132 |
* driver class to instantiate.
|
|
|
133 |
*
|
|
|
134 |
* Other (optional) parameters:
|
|
|
135 |
*
|
|
|
136 |
* <b>user (string)</b>:
|
|
|
137 |
* The username to use when connecting.
|
|
|
138 |
*
|
|
|
139 |
* <b>password (string)</b>:
|
|
|
140 |
* The password to use when connecting.
|
|
|
141 |
*
|
|
|
142 |
* <b>driverOptions (array)</b>:
|
|
|
143 |
* Any additional driver-specific options for the driver. These are just passed
|
|
|
144 |
* through to the driver.
|
|
|
145 |
*
|
|
|
146 |
* <b>wrapperClass</b>:
|
|
|
147 |
* You may specify a custom wrapper class through the 'wrapperClass'
|
|
|
148 |
* parameter but this class MUST inherit from Doctrine\DBAL\Connection.
|
|
|
149 |
*
|
|
|
150 |
* <b>driverClass</b>:
|
|
|
151 |
* The driver class to use.
|
|
|
152 |
*
|
|
|
153 |
* @param Configuration|null $config The configuration to use.
|
|
|
154 |
* @param EventManager|null $eventManager The event manager to use.
|
| 621 |
lars |
155 |
* @psalm-param Params $params
|
| 148 |
lars |
156 |
*
|
| 621 |
lars |
157 |
* @psalm-return ($params is array{wrapperClass: class-string<T>} ? T : Connection)
|
| 148 |
lars |
158 |
*
|
|
|
159 |
* @throws Exception
|
|
|
160 |
*
|
|
|
161 |
* @template T of Connection
|
|
|
162 |
*/
|
|
|
163 |
public static function getConnection(
|
| 621 |
lars |
164 |
#[SensitiveParameter]
|
| 148 |
lars |
165 |
array $params,
|
|
|
166 |
?Configuration $config = null,
|
|
|
167 |
?EventManager $eventManager = null
|
|
|
168 |
): Connection {
|
|
|
169 |
// create default config and event manager, if not set
|
|
|
170 |
$config ??= new Configuration();
|
|
|
171 |
$eventManager ??= new EventManager();
|
|
|
172 |
$params = self::parseDatabaseUrl($params);
|
|
|
173 |
|
|
|
174 |
// URL support for PrimaryReplicaConnection
|
|
|
175 |
if (isset($params['primary'])) {
|
|
|
176 |
$params['primary'] = self::parseDatabaseUrl($params['primary']);
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
if (isset($params['replica'])) {
|
|
|
180 |
foreach ($params['replica'] as $key => $replicaParams) {
|
|
|
181 |
$params['replica'][$key] = self::parseDatabaseUrl($replicaParams);
|
|
|
182 |
}
|
|
|
183 |
}
|
|
|
184 |
|
| 621 |
lars |
185 |
$driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null);
|
| 148 |
lars |
186 |
|
|
|
187 |
foreach ($config->getMiddlewares() as $middleware) {
|
|
|
188 |
$driver = $middleware->wrap($driver);
|
|
|
189 |
}
|
|
|
190 |
|
|
|
191 |
$wrapperClass = $params['wrapperClass'] ?? Connection::class;
|
|
|
192 |
if (! is_a($wrapperClass, Connection::class, true)) {
|
|
|
193 |
throw Exception::invalidWrapperClass($wrapperClass);
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
return new $wrapperClass($params, $driver, $config, $eventManager);
|
|
|
197 |
}
|
|
|
198 |
|
|
|
199 |
/**
|
|
|
200 |
* Returns the list of supported drivers.
|
|
|
201 |
*
|
|
|
202 |
* @return string[]
|
| 621 |
lars |
203 |
* @psalm-return list<key-of<self::DRIVER_MAP>>
|
| 148 |
lars |
204 |
*/
|
|
|
205 |
public static function getAvailableDrivers(): array
|
|
|
206 |
{
|
|
|
207 |
return array_keys(self::DRIVER_MAP);
|
|
|
208 |
}
|
|
|
209 |
|
|
|
210 |
/**
|
| 621 |
lars |
211 |
* @param class-string<Driver>|null $driverClass
|
|
|
212 |
* @param key-of<self::DRIVER_MAP>|null $driver
|
| 148 |
lars |
213 |
*
|
|
|
214 |
* @throws Exception
|
|
|
215 |
*/
|
| 621 |
lars |
216 |
private static function createDriver(?string $driver, ?string $driverClass): Driver
|
| 148 |
lars |
217 |
{
|
| 621 |
lars |
218 |
if ($driverClass === null) {
|
|
|
219 |
if ($driver === null) {
|
|
|
220 |
throw Exception::driverRequired();
|
| 148 |
lars |
221 |
}
|
|
|
222 |
|
| 621 |
lars |
223 |
if (! isset(self::DRIVER_MAP[$driver])) {
|
|
|
224 |
throw Exception::unknownDriver($driver, array_keys(self::DRIVER_MAP));
|
| 148 |
lars |
225 |
}
|
|
|
226 |
|
| 621 |
lars |
227 |
$driverClass = self::DRIVER_MAP[$driver];
|
|
|
228 |
} elseif (! is_a($driverClass, Driver::class, true)) {
|
|
|
229 |
throw Exception::invalidDriverClass($driverClass);
|
| 148 |
lars |
230 |
}
|
|
|
231 |
|
| 621 |
lars |
232 |
return new $driverClass();
|
| 148 |
lars |
233 |
}
|
|
|
234 |
|
|
|
235 |
/**
|
|
|
236 |
* Extracts parts from a database URL, if present, and returns an
|
|
|
237 |
* updated list of parameters.
|
|
|
238 |
*
|
|
|
239 |
* @param mixed[] $params The list of parameters.
|
|
|
240 |
* @psalm-param Params $params
|
|
|
241 |
*
|
|
|
242 |
* @return mixed[] A modified list of parameters with info from a database
|
|
|
243 |
* URL extracted into indidivual parameter parts.
|
|
|
244 |
* @psalm-return Params
|
|
|
245 |
*
|
|
|
246 |
* @throws Exception
|
|
|
247 |
*/
|
| 621 |
lars |
248 |
private static function parseDatabaseUrl(
|
|
|
249 |
#[SensitiveParameter]
|
|
|
250 |
array $params
|
|
|
251 |
): array {
|
| 148 |
lars |
252 |
if (! isset($params['url'])) {
|
|
|
253 |
return $params;
|
|
|
254 |
}
|
|
|
255 |
|
| 621 |
lars |
256 |
Deprecation::trigger(
|
|
|
257 |
'doctrine/dbal',
|
|
|
258 |
'https://github.com/doctrine/dbal/pull/5843',
|
|
|
259 |
'The "url" connection parameter is deprecated. Please use %s to parse a database url before calling %s.',
|
|
|
260 |
DsnParser::class,
|
|
|
261 |
self::class,
|
|
|
262 |
);
|
| 148 |
lars |
263 |
|
| 621 |
lars |
264 |
$parser = new DsnParser(self::$driverSchemeAliases);
|
|
|
265 |
try {
|
|
|
266 |
$parsedParams = $parser->parse($params['url']);
|
|
|
267 |
} catch (MalformedDsnException $e) {
|
|
|
268 |
throw new Exception('Malformed parameter "url".', 0, $e);
|
| 148 |
lars |
269 |
}
|
|
|
270 |
|
| 621 |
lars |
271 |
if (isset($parsedParams['driver'])) {
|
| 148 |
lars |
272 |
// The requested driver from the URL scheme takes precedence
|
|
|
273 |
// over the default custom driver from the connection parameters (if any).
|
|
|
274 |
unset($params['driverClass']);
|
| 621 |
lars |
275 |
}
|
| 148 |
lars |
276 |
|
| 621 |
lars |
277 |
$params = array_merge($params, $parsedParams);
|
| 148 |
lars |
278 |
|
|
|
279 |
// If a schemeless connection URL is given, we require a default driver or default custom driver
|
|
|
280 |
// as connection parameter.
|
|
|
281 |
if (! isset($params['driverClass']) && ! isset($params['driver'])) {
|
|
|
282 |
throw Exception::driverRequired($params['url']);
|
|
|
283 |
}
|
|
|
284 |
|
|
|
285 |
return $params;
|
|
|
286 |
}
|
|
|
287 |
}
|