Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Database/Database.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\Database; | |
4 | |
5 /** | |
6 * Primary front-controller for the database system. | |
7 * | |
8 * This class is uninstantiatable and un-extendable. It acts to encapsulate | |
9 * all control and shepherding of database connections into a single location | |
10 * without the use of globals. | |
11 */ | |
12 abstract class Database { | |
13 | |
14 /** | |
15 * Flag to indicate a query call should simply return NULL. | |
16 * | |
17 * This is used for queries that have no reasonable return value anyway, such | |
18 * as INSERT statements to a table without a serial primary key. | |
19 */ | |
20 const RETURN_NULL = 0; | |
21 | |
22 /** | |
23 * Flag to indicate a query call should return the prepared statement. | |
24 */ | |
25 const RETURN_STATEMENT = 1; | |
26 | |
27 /** | |
28 * Flag to indicate a query call should return the number of affected rows. | |
29 */ | |
30 const RETURN_AFFECTED = 2; | |
31 | |
32 /** | |
33 * Flag to indicate a query call should return the "last insert id". | |
34 */ | |
35 const RETURN_INSERT_ID = 3; | |
36 | |
37 /** | |
38 * An nested array of all active connections. It is keyed by database name | |
39 * and target. | |
40 * | |
41 * @var array | |
42 */ | |
43 static protected $connections = []; | |
44 | |
45 /** | |
46 * A processed copy of the database connection information from settings.php. | |
47 * | |
48 * @var array | |
49 */ | |
50 static protected $databaseInfo = []; | |
51 | |
52 /** | |
53 * A list of key/target credentials to simply ignore. | |
54 * | |
55 * @var array | |
56 */ | |
57 static protected $ignoreTargets = []; | |
58 | |
59 /** | |
60 * The key of the currently active database connection. | |
61 * | |
62 * @var string | |
63 */ | |
64 static protected $activeKey = 'default'; | |
65 | |
66 /** | |
67 * An array of active query log objects. | |
68 * | |
69 * Every connection has one and only one logger object for all targets and | |
70 * logging keys. | |
71 * | |
72 * array( | |
73 * '$db_key' => DatabaseLog object. | |
74 * ); | |
75 * | |
76 * @var array | |
77 */ | |
78 static protected $logs = []; | |
79 | |
80 /** | |
81 * Starts logging a given logging key on the specified connection. | |
82 * | |
83 * @param string $logging_key | |
84 * The logging key to log. | |
85 * @param string $key | |
86 * The database connection key for which we want to log. | |
87 * | |
88 * @return \Drupal\Core\Database\Log | |
89 * The query log object. Note that the log object does support richer | |
90 * methods than the few exposed through the Database class, so in some | |
91 * cases it may be desirable to access it directly. | |
92 * | |
93 * @see \Drupal\Core\Database\Log | |
94 */ | |
95 final public static function startLog($logging_key, $key = 'default') { | |
96 if (empty(self::$logs[$key])) { | |
97 self::$logs[$key] = new Log($key); | |
98 | |
99 // Every target already active for this connection key needs to have the | |
100 // logging object associated with it. | |
101 if (!empty(self::$connections[$key])) { | |
102 foreach (self::$connections[$key] as $connection) { | |
103 $connection->setLogger(self::$logs[$key]); | |
104 } | |
105 } | |
106 } | |
107 | |
108 self::$logs[$key]->start($logging_key); | |
109 return self::$logs[$key]; | |
110 } | |
111 | |
112 /** | |
113 * Retrieves the queries logged on for given logging key. | |
114 * | |
115 * This method also ends logging for the specified key. To get the query log | |
116 * to date without ending the logger request the logging object by starting | |
117 * it again (which does nothing to an open log key) and call methods on it as | |
118 * desired. | |
119 * | |
120 * @param string $logging_key | |
121 * The logging key to log. | |
122 * @param string $key | |
123 * The database connection key for which we want to log. | |
124 * | |
125 * @return array | |
126 * The query log for the specified logging key and connection. | |
127 * | |
128 * @see \Drupal\Core\Database\Log | |
129 */ | |
130 final public static function getLog($logging_key, $key = 'default') { | |
131 if (empty(self::$logs[$key])) { | |
132 return []; | |
133 } | |
134 $queries = self::$logs[$key]->get($logging_key); | |
135 self::$logs[$key]->end($logging_key); | |
136 return $queries; | |
137 } | |
138 | |
139 /** | |
140 * Gets the connection object for the specified database key and target. | |
141 * | |
142 * @param string $target | |
143 * The database target name. | |
144 * @param string $key | |
145 * The database connection key. Defaults to NULL which means the active key. | |
146 * | |
147 * @return \Drupal\Core\Database\Connection | |
148 * The corresponding connection object. | |
149 */ | |
150 final public static function getConnection($target = 'default', $key = NULL) { | |
151 if (!isset($key)) { | |
152 // By default, we want the active connection, set in setActiveConnection. | |
153 $key = self::$activeKey; | |
154 } | |
155 // If the requested target does not exist, or if it is ignored, we fall back | |
156 // to the default target. The target is typically either "default" or | |
157 // "replica", indicating to use a replica SQL server if one is available. If | |
158 // it's not available, then the default/primary server is the correct server | |
159 // to use. | |
160 if (!empty(self::$ignoreTargets[$key][$target]) || !isset(self::$databaseInfo[$key][$target])) { | |
161 $target = 'default'; | |
162 } | |
163 | |
164 if (!isset(self::$connections[$key][$target])) { | |
165 // If necessary, a new connection is opened. | |
166 self::$connections[$key][$target] = self::openConnection($key, $target); | |
167 } | |
168 return self::$connections[$key][$target]; | |
169 } | |
170 | |
171 /** | |
172 * Determines if there is an active connection. | |
173 * | |
174 * Note that this method will return FALSE if no connection has been | |
175 * established yet, even if one could be. | |
176 * | |
177 * @return bool | |
178 * TRUE if there is at least one database connection established, FALSE | |
179 * otherwise. | |
180 */ | |
181 final public static function isActiveConnection() { | |
182 return !empty(self::$activeKey) && !empty(self::$connections) && !empty(self::$connections[self::$activeKey]); | |
183 } | |
184 | |
185 /** | |
186 * Sets the active connection to the specified key. | |
187 * | |
188 * @return string|null | |
189 * The previous database connection key. | |
190 */ | |
191 final public static function setActiveConnection($key = 'default') { | |
192 if (!empty(self::$databaseInfo[$key])) { | |
193 $old_key = self::$activeKey; | |
194 self::$activeKey = $key; | |
195 return $old_key; | |
196 } | |
197 } | |
198 | |
199 /** | |
200 * Process the configuration file for database information. | |
201 * | |
202 * @param array $info | |
203 * The database connection information, as defined in settings.php. The | |
204 * structure of this array depends on the database driver it is connecting | |
205 * to. | |
206 */ | |
207 final public static function parseConnectionInfo(array $info) { | |
208 // If there is no "driver" property, then we assume it's an array of | |
209 // possible connections for this target. Pick one at random. That allows | |
210 // us to have, for example, multiple replica servers. | |
211 if (empty($info['driver'])) { | |
212 $info = $info[mt_rand(0, count($info) - 1)]; | |
213 } | |
214 // Parse the prefix information. | |
215 if (!isset($info['prefix'])) { | |
216 // Default to an empty prefix. | |
217 $info['prefix'] = [ | |
218 'default' => '', | |
219 ]; | |
220 } | |
221 elseif (!is_array($info['prefix'])) { | |
222 // Transform the flat form into an array form. | |
223 $info['prefix'] = [ | |
224 'default' => $info['prefix'], | |
225 ]; | |
226 } | |
227 return $info; | |
228 } | |
229 | |
230 /** | |
231 * Adds database connection information for a given key/target. | |
232 * | |
233 * This method allows to add new connections at runtime. | |
234 * | |
235 * Under normal circumstances the preferred way to specify database | |
236 * credentials is via settings.php. However, this method allows them to be | |
237 * added at arbitrary times, such as during unit tests, when connecting to | |
238 * admin-defined third party databases, etc. | |
239 * | |
240 * If the given key/target pair already exists, this method will be ignored. | |
241 * | |
242 * @param string $key | |
243 * The database key. | |
244 * @param string $target | |
245 * The database target name. | |
246 * @param array $info | |
247 * The database connection information, as defined in settings.php. The | |
248 * structure of this array depends on the database driver it is connecting | |
249 * to. | |
250 */ | |
251 final public static function addConnectionInfo($key, $target, array $info) { | |
252 if (empty(self::$databaseInfo[$key][$target])) { | |
253 self::$databaseInfo[$key][$target] = self::parseConnectionInfo($info); | |
254 } | |
255 } | |
256 | |
257 /** | |
258 * Gets information on the specified database connection. | |
259 * | |
260 * @param string $key | |
261 * (optional) The connection key for which to return information. | |
262 * | |
263 * @return array|null | |
264 */ | |
265 final public static function getConnectionInfo($key = 'default') { | |
266 if (!empty(self::$databaseInfo[$key])) { | |
267 return self::$databaseInfo[$key]; | |
268 } | |
269 } | |
270 | |
271 /** | |
272 * Gets connection information for all available databases. | |
273 * | |
274 * @return array | |
275 */ | |
276 final public static function getAllConnectionInfo() { | |
277 return self::$databaseInfo; | |
278 } | |
279 | |
280 /** | |
281 * Sets connection information for multiple databases. | |
282 * | |
283 * @param array $databases | |
284 * A multi-dimensional array specifying database connection parameters, as | |
285 * defined in settings.php. | |
286 */ | |
287 final public static function setMultipleConnectionInfo(array $databases) { | |
288 foreach ($databases as $key => $targets) { | |
289 foreach ($targets as $target => $info) { | |
290 self::addConnectionInfo($key, $target, $info); | |
291 } | |
292 } | |
293 } | |
294 | |
295 /** | |
296 * Rename a connection and its corresponding connection information. | |
297 * | |
298 * @param string $old_key | |
299 * The old connection key. | |
300 * @param string $new_key | |
301 * The new connection key. | |
302 * | |
303 * @return bool | |
304 * TRUE in case of success, FALSE otherwise. | |
305 */ | |
306 final public static function renameConnection($old_key, $new_key) { | |
307 if (!empty(self::$databaseInfo[$old_key]) && empty(self::$databaseInfo[$new_key])) { | |
308 // Migrate the database connection information. | |
309 self::$databaseInfo[$new_key] = self::$databaseInfo[$old_key]; | |
310 unset(self::$databaseInfo[$old_key]); | |
311 | |
312 // Migrate over the DatabaseConnection object if it exists. | |
313 if (isset(self::$connections[$old_key])) { | |
314 self::$connections[$new_key] = self::$connections[$old_key]; | |
315 unset(self::$connections[$old_key]); | |
316 } | |
317 | |
318 return TRUE; | |
319 } | |
320 else { | |
321 return FALSE; | |
322 } | |
323 } | |
324 | |
325 /** | |
326 * Remove a connection and its corresponding connection information. | |
327 * | |
328 * @param string $key | |
329 * The connection key. | |
330 * | |
331 * @return bool | |
332 * TRUE in case of success, FALSE otherwise. | |
333 */ | |
334 final public static function removeConnection($key) { | |
335 if (isset(self::$databaseInfo[$key])) { | |
336 self::closeConnection(NULL, $key); | |
337 unset(self::$databaseInfo[$key]); | |
338 return TRUE; | |
339 } | |
340 else { | |
341 return FALSE; | |
342 } | |
343 } | |
344 | |
345 /** | |
346 * Opens a connection to the server specified by the given key and target. | |
347 * | |
348 * @param string $key | |
349 * The database connection key, as specified in settings.php. The default is | |
350 * "default". | |
351 * @param string $target | |
352 * The database target to open. | |
353 * | |
354 * @throws \Drupal\Core\Database\ConnectionNotDefinedException | |
355 * @throws \Drupal\Core\Database\DriverNotSpecifiedException | |
356 */ | |
357 final protected static function openConnection($key, $target) { | |
358 // If the requested database does not exist then it is an unrecoverable | |
359 // error. | |
360 if (!isset(self::$databaseInfo[$key])) { | |
361 throw new ConnectionNotDefinedException('The specified database connection is not defined: ' . $key); | |
362 } | |
363 | |
364 if (!$driver = self::$databaseInfo[$key][$target]['driver']) { | |
365 throw new DriverNotSpecifiedException('Driver not specified for this database connection: ' . $key); | |
366 } | |
367 | |
368 if (!empty(self::$databaseInfo[$key][$target]['namespace'])) { | |
369 $driver_class = self::$databaseInfo[$key][$target]['namespace'] . '\\Connection'; | |
370 } | |
371 else { | |
372 // Fallback for Drupal 7 settings.php. | |
373 $driver_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\Connection"; | |
374 } | |
375 | |
376 $pdo_connection = $driver_class::open(self::$databaseInfo[$key][$target]); | |
377 $new_connection = new $driver_class($pdo_connection, self::$databaseInfo[$key][$target]); | |
378 $new_connection->setTarget($target); | |
379 $new_connection->setKey($key); | |
380 | |
381 // If we have any active logging objects for this connection key, we need | |
382 // to associate them with the connection we just opened. | |
383 if (!empty(self::$logs[$key])) { | |
384 $new_connection->setLogger(self::$logs[$key]); | |
385 } | |
386 | |
387 return $new_connection; | |
388 } | |
389 | |
390 /** | |
391 * Closes a connection to the server specified by the given key and target. | |
392 * | |
393 * @param string $target | |
394 * The database target name. Defaults to NULL meaning that all target | |
395 * connections will be closed. | |
396 * @param string $key | |
397 * The database connection key. Defaults to NULL which means the active key. | |
398 */ | |
399 public static function closeConnection($target = NULL, $key = NULL) { | |
400 // Gets the active connection by default. | |
401 if (!isset($key)) { | |
402 $key = self::$activeKey; | |
403 } | |
404 // To close a connection, it needs to be set to NULL and removed from the | |
405 // static variable. In all cases, closeConnection() might be called for a | |
406 // connection that was not opened yet, in which case the key is not defined | |
407 // yet and we just ensure that the connection key is undefined. | |
408 if (isset($target)) { | |
409 if (isset(self::$connections[$key][$target])) { | |
410 self::$connections[$key][$target]->destroy(); | |
411 self::$connections[$key][$target] = NULL; | |
412 } | |
413 unset(self::$connections[$key][$target]); | |
414 } | |
415 else { | |
416 if (isset(self::$connections[$key])) { | |
417 foreach (self::$connections[$key] as $target => $connection) { | |
418 self::$connections[$key][$target]->destroy(); | |
419 self::$connections[$key][$target] = NULL; | |
420 } | |
421 } | |
422 unset(self::$connections[$key]); | |
423 } | |
424 } | |
425 | |
426 /** | |
427 * Instructs the system to temporarily ignore a given key/target. | |
428 * | |
429 * At times we need to temporarily disable replica queries. To do so, call this | |
430 * method with the database key and the target to disable. That database key | |
431 * will then always fall back to 'default' for that key, even if it's defined. | |
432 * | |
433 * @param string $key | |
434 * The database connection key. | |
435 * @param string $target | |
436 * The target of the specified key to ignore. | |
437 */ | |
438 public static function ignoreTarget($key, $target) { | |
439 self::$ignoreTargets[$key][$target] = TRUE; | |
440 } | |
441 | |
442 /** | |
443 * Converts a URL to a database connection info array. | |
444 * | |
445 * @param string $url | |
446 * The URL. | |
447 * @param string $root | |
448 * The root directory of the Drupal installation. | |
449 * | |
450 * @return array | |
451 * The database connection info. | |
452 * | |
453 * @throws \InvalidArgumentException | |
454 * Exception thrown when the provided URL does not meet the minimum | |
455 * requirements. | |
456 */ | |
457 public static function convertDbUrlToConnectionInfo($url, $root) { | |
458 $info = parse_url($url); | |
459 if (!isset($info['scheme'], $info['host'], $info['path'])) { | |
460 throw new \InvalidArgumentException('Minimum requirement: driver://host/database'); | |
461 } | |
462 $info += [ | |
463 'user' => '', | |
464 'pass' => '', | |
465 'fragment' => '', | |
466 ]; | |
467 | |
468 // A SQLite database path with two leading slashes indicates a system path. | |
469 // Otherwise the path is relative to the Drupal root. | |
470 if ($info['path'][0] === '/') { | |
471 $info['path'] = substr($info['path'], 1); | |
472 } | |
473 if ($info['scheme'] === 'sqlite' && $info['path'][0] !== '/') { | |
474 $info['path'] = $root . '/' . $info['path']; | |
475 } | |
476 | |
477 $database = [ | |
478 'driver' => $info['scheme'], | |
479 'username' => $info['user'], | |
480 'password' => $info['pass'], | |
481 'host' => $info['host'], | |
482 'database' => $info['path'], | |
483 ]; | |
484 if (isset($info['port'])) { | |
485 $database['port'] = $info['port']; | |
486 } | |
487 return $database; | |
488 } | |
489 | |
490 /** | |
491 * Gets database connection info as a URL. | |
492 * | |
493 * @param string $key | |
494 * (Optional) The database connection key. | |
495 * | |
496 * @return string | |
497 * The connection info as a URL. | |
498 */ | |
499 public static function getConnectionInfoAsUrl($key = 'default') { | |
500 $db_info = static::getConnectionInfo($key); | |
501 if ($db_info['default']['driver'] == 'sqlite') { | |
502 $db_url = 'sqlite://localhost/' . $db_info['default']['database']; | |
503 } | |
504 else { | |
505 $user = ''; | |
506 if ($db_info['default']['username']) { | |
507 $user = $db_info['default']['username']; | |
508 if ($db_info['default']['password']) { | |
509 $user .= ':' . $db_info['default']['password']; | |
510 } | |
511 $user .= '@'; | |
512 } | |
513 | |
514 $db_url = $db_info['default']['driver'] . '://' . $user . $db_info['default']['host']; | |
515 if (isset($db_info['default']['port'])) { | |
516 $db_url .= ':' . $db_info['default']['port']; | |
517 } | |
518 $db_url .= '/' . $db_info['default']['database']; | |
519 } | |
520 if ($db_info['default']['prefix']['default']) { | |
521 $db_url .= '#' . $db_info['default']['prefix']['default']; | |
522 } | |
523 return $db_url; | |
524 } | |
525 | |
526 } |