Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 17 |
CRAP | |
15.97% |
19 / 119 |
Database | |
0.00% |
0 / 1 |
|
0.00% |
0 / 17 |
2054.25 | |
15.97% |
19 / 119 |
startLog | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 7 |
|||
getLog | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
getConnection | |
0.00% |
0 / 1 |
5.07 | |
85.71% |
6 / 7 |
|||
isActiveConnection | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 1 |
|||
setActiveConnection | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
parseConnectionInfo | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
addConnectionInfo | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
getConnectionInfo | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
|||
getAllConnectionInfo | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setMultipleConnectionInfo | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 4 |
|||
renameConnection | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 8 |
|||
removeConnection | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
openConnection | |
0.00% |
0 / 1 |
20.74 | |
14.29% |
2 / 14 |
|||
closeConnection | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 13 |
|||
ignoreTarget | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
convertDbUrlToConnectionInfo | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 16 |
|||
getConnectionInfoAsUrl | |
0.00% |
0 / 1 |
9.01 | |
56.25% |
9 / 16 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Database\Database. | |
*/ | |
namespace Drupal\Core\Database; | |
/** | |
* Primary front-controller for the database system. | |
* | |
* This class is uninstantiatable and un-extendable. It acts to encapsulate | |
* all control and shepherding of database connections into a single location | |
* without the use of globals. | |
*/ | |
abstract class Database { | |
/** | |
* Flag to indicate a query call should simply return NULL. | |
* | |
* This is used for queries that have no reasonable return value anyway, such | |
* as INSERT statements to a table without a serial primary key. | |
*/ | |
const RETURN_NULL = 0; | |
/** | |
* Flag to indicate a query call should return the prepared statement. | |
*/ | |
const RETURN_STATEMENT = 1; | |
/** | |
* Flag to indicate a query call should return the number of affected rows. | |
*/ | |
const RETURN_AFFECTED = 2; | |
/** | |
* Flag to indicate a query call should return the "last insert id". | |
*/ | |
const RETURN_INSERT_ID = 3; | |
/** | |
* An nested array of all active connections. It is keyed by database name | |
* and target. | |
* | |
* @var array | |
*/ | |
static protected $connections = array(); | |
/** | |
* A processed copy of the database connection information from settings.php. | |
* | |
* @var array | |
*/ | |
static protected $databaseInfo = array(); | |
/** | |
* A list of key/target credentials to simply ignore. | |
* | |
* @var array | |
*/ | |
static protected $ignoreTargets = array(); | |
/** | |
* The key of the currently active database connection. | |
* | |
* @var string | |
*/ | |
static protected $activeKey = 'default'; | |
/** | |
* An array of active query log objects. | |
* | |
* Every connection has one and only one logger object for all targets and | |
* logging keys. | |
* | |
* array( | |
* '$db_key' => DatabaseLog object. | |
* ); | |
* | |
* @var array | |
*/ | |
static protected $logs = array(); | |
/** | |
* Starts logging a given logging key on the specified connection. | |
* | |
* @param string $logging_key | |
* The logging key to log. | |
* @param string $key | |
* The database connection key for which we want to log. | |
* | |
* @return \Drupal\Core\Database\Log | |
* The query log object. Note that the log object does support richer | |
* methods than the few exposed through the Database class, so in some | |
* cases it may be desirable to access it directly. | |
* | |
* @see \Drupal\Core\Database\Log | |
*/ | |
final public static function startLog($logging_key, $key = 'default') { | |
if (empty(self::$logs[$key])) { | |
self::$logs[$key] = new Log($key); | |
// Every target already active for this connection key needs to have the | |
// logging object associated with it. | |
if (!empty(self::$connections[$key])) { | |
foreach (self::$connections[$key] as $connection) { | |
$connection->setLogger(self::$logs[$key]); | |
} | |
} | |
} | |
self::$logs[$key]->start($logging_key); | |
return self::$logs[$key]; | |
} | |
/** | |
* Retrieves the queries logged on for given logging key. | |
* | |
* This method also ends logging for the specified key. To get the query log | |
* to date without ending the logger request the logging object by starting | |
* it again (which does nothing to an open log key) and call methods on it as | |
* desired. | |
* | |
* @param string $logging_key | |
* The logging key to log. | |
* @param string $key | |
* The database connection key for which we want to log. | |
* | |
* @return array | |
* The query log for the specified logging key and connection. | |
* | |
* @see \Drupal\Core\Database\Log | |
*/ | |
final public static function getLog($logging_key, $key = 'default') { | |
if (empty(self::$logs[$key])) { | |
return NULL; | |
} | |
$queries = self::$logs[$key]->get($logging_key); | |
self::$logs[$key]->end($logging_key); | |
return $queries; | |
} | |
/** | |
* Gets the connection object for the specified database key and target. | |
* | |
* @param string $target | |
* The database target name. | |
* @param string $key | |
* The database connection key. Defaults to NULL which means the active key. | |
* | |
* @return \Drupal\Core\Database\Connection | |
* The corresponding connection object. | |
*/ | |
final public static function getConnection($target = 'default', $key = NULL) { | |
if (!isset($key)) { | |
// By default, we want the active connection, set in setActiveConnection. | |
$key = self::$activeKey; | |
} | |
// If the requested target does not exist, or if it is ignored, we fall back | |
// to the default target. The target is typically either "default" or | |
// "replica", indicating to use a replica SQL server if one is available. If | |
// it's not available, then the default/primary server is the correct server | |
// to use. | |
if (!empty(self::$ignoreTargets[$key][$target]) || !isset(self::$databaseInfo[$key][$target])) { | |
$target = 'default'; | |
} | |
if (!isset(self::$connections[$key][$target])) { | |
// If necessary, a new connection is opened. | |
self::$connections[$key][$target] = self::openConnection($key, $target); | |
} | |
return self::$connections[$key][$target]; | |
} | |
/** | |
* Determines if there is an active connection. | |
* | |
* Note that this method will return FALSE if no connection has been | |
* established yet, even if one could be. | |
* | |
* @return bool | |
* TRUE if there is at least one database connection established, FALSE | |
* otherwise. | |
*/ | |
final public static function isActiveConnection() { | |
return !empty(self::$activeKey) && !empty(self::$connections) && !empty(self::$connections[self::$activeKey]); | |
} | |
/** | |
* Sets the active connection to the specified key. | |
* | |
* @return string|null | |
* The previous database connection key. | |
*/ | |
final public static function setActiveConnection($key = 'default') { | |
if (!empty(self::$databaseInfo[$key])) { | |
$old_key = self::$activeKey; | |
self::$activeKey = $key; | |
return $old_key; | |
} | |
} | |
/** | |
* Process the configuration file for database information. | |
* | |
* @param array $info | |
* The database connection information, as defined in settings.php. The | |
* structure of this array depends on the database driver it is connecting | |
* to. | |
*/ | |
final public static function parseConnectionInfo(array $info) { | |
// If there is no "driver" property, then we assume it's an array of | |
// possible connections for this target. Pick one at random. That allows | |
// us to have, for example, multiple replica servers. | |
if (empty($info['driver'])) { | |
$info = $info[mt_rand(0, count($info) - 1)]; | |
} | |
// Parse the prefix information. | |
if (!isset($info['prefix'])) { | |
// Default to an empty prefix. | |
$info['prefix'] = array( | |
'default' => '', | |
); | |
} | |
elseif (!is_array($info['prefix'])) { | |
// Transform the flat form into an array form. | |
$info['prefix'] = array( | |
'default' => $info['prefix'], | |
); | |
} | |
return $info; | |
} | |
/** | |
* Adds database connection information for a given key/target. | |
* | |
* This method allows to add new connections at runtime. | |
* | |
* Under normal circumstances the preferred way to specify database | |
* credentials is via settings.php. However, this method allows them to be | |
* added at arbitrary times, such as during unit tests, when connecting to | |
* admin-defined third party databases, etc. | |
* | |
* If the given key/target pair already exists, this method will be ignored. | |
* | |
* @param string $key | |
* The database key. | |
* @param string $target | |
* The database target name. | |
* @param array $info | |
* The database connection information, as defined in settings.php. The | |
* structure of this array depends on the database driver it is connecting | |
* to. | |
*/ | |
final public static function addConnectionInfo($key, $target, array $info) { | |
if (empty(self::$databaseInfo[$key][$target])) { | |
self::$databaseInfo[$key][$target] = self::parseConnectionInfo($info); | |
} | |
} | |
/** | |
* Gets information on the specified database connection. | |
* | |
* @param string $key | |
* (optional) The connection key for which to return information. | |
* | |
* @return array|null | |
*/ | |
final public static function getConnectionInfo($key = 'default') { | |
if (!empty(self::$databaseInfo[$key])) { | |
return self::$databaseInfo[$key]; | |
} | |
} | |
/** | |
* Gets connection information for all available databases. | |
* | |
* @return array | |
*/ | |
final public static function getAllConnectionInfo() { | |
return self::$databaseInfo; | |
} | |
/** | |
* Sets connection information for multiple databases. | |
* | |
* @param array $databases | |
* A multi-dimensional array specifying database connection parameters, as | |
* defined in settings.php. | |
*/ | |
final public static function setMultipleConnectionInfo(array $databases) { | |
foreach ($databases as $key => $targets) { | |
foreach ($targets as $target => $info) { | |
self::addConnectionInfo($key, $target, $info); | |
} | |
} | |
} | |
/** | |
* Rename a connection and its corresponding connection information. | |
* | |
* @param string $old_key | |
* The old connection key. | |
* @param string $new_key | |
* The new connection key. | |
* | |
* @return bool | |
* TRUE in case of success, FALSE otherwise. | |
*/ | |
final public static function renameConnection($old_key, $new_key) { | |
if (!empty(self::$databaseInfo[$old_key]) && empty(self::$databaseInfo[$new_key])) { | |
// Migrate the database connection information. | |
self::$databaseInfo[$new_key] = self::$databaseInfo[$old_key]; | |
unset(self::$databaseInfo[$old_key]); | |
// Migrate over the DatabaseConnection object if it exists. | |
if (isset(self::$connections[$old_key])) { | |
self::$connections[$new_key] = self::$connections[$old_key]; | |
unset(self::$connections[$old_key]); | |
} | |
return TRUE; | |
} | |
else { | |
return FALSE; | |
} | |
} | |
/** | |
* Remove a connection and its corresponding connection information. | |
* | |
* @param string $key | |
* The connection key. | |
* | |
* @return bool | |
* TRUE in case of success, FALSE otherwise. | |
*/ | |
final public static function removeConnection($key) { | |
if (isset(self::$databaseInfo[$key])) { | |
self::closeConnection(NULL, $key); | |
unset(self::$databaseInfo[$key]); | |
return TRUE; | |
} | |
else { | |
return FALSE; | |
} | |
} | |
/** | |
* Opens a connection to the server specified by the given key and target. | |
* | |
* @param string $key | |
* The database connection key, as specified in settings.php. The default is | |
* "default". | |
* @param string $target | |
* The database target to open. | |
* | |
* @throws \Drupal\Core\Database\ConnectionNotDefinedException | |
* @throws \Drupal\Core\Database\DriverNotSpecifiedException | |
*/ | |
final protected static function openConnection($key, $target) { | |
// If the requested database does not exist then it is an unrecoverable | |
// error. | |
if (!isset(self::$databaseInfo[$key])) { | |
throw new ConnectionNotDefinedException('The specified database connection is not defined: ' . $key); | |
} | |
if (!$driver = self::$databaseInfo[$key][$target]['driver']) { | |
throw new DriverNotSpecifiedException('Driver not specified for this database connection: ' . $key); | |
} | |
if (!empty(self::$databaseInfo[$key][$target]['namespace'])) { | |
$driver_class = self::$databaseInfo[$key][$target]['namespace'] . '\\Connection'; | |
} | |
else { | |
// Fallback for Drupal 7 settings.php. | |
$driver_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\Connection"; | |
} | |
$pdo_connection = $driver_class::open(self::$databaseInfo[$key][$target]); | |
$new_connection = new $driver_class($pdo_connection, self::$databaseInfo[$key][$target]); | |
$new_connection->setTarget($target); | |
$new_connection->setKey($key); | |
// If we have any active logging objects for this connection key, we need | |
// to associate them with the connection we just opened. | |
if (!empty(self::$logs[$key])) { | |
$new_connection->setLogger(self::$logs[$key]); | |
} | |
return $new_connection; | |
} | |
/** | |
* Closes a connection to the server specified by the given key and target. | |
* | |
* @param string $target | |
* The database target name. Defaults to NULL meaning that all target | |
* connections will be closed. | |
* @param string $key | |
* The database connection key. Defaults to NULL which means the active key. | |
*/ | |
public static function closeConnection($target = NULL, $key = NULL) { | |
// Gets the active connection by default. | |
if (!isset($key)) { | |
$key = self::$activeKey; | |
} | |
// To close a connection, it needs to be set to NULL and removed from the | |
// static variable. In all cases, closeConnection() might be called for a | |
// connection that was not opened yet, in which case the key is not defined | |
// yet and we just ensure that the connection key is undefined. | |
if (isset($target)) { | |
if (isset(self::$connections[$key][$target])) { | |
self::$connections[$key][$target]->destroy(); | |
self::$connections[$key][$target] = NULL; | |
} | |
unset(self::$connections[$key][$target]); | |
} | |
else { | |
if (isset(self::$connections[$key])) { | |
foreach (self::$connections[$key] as $target => $connection) { | |
self::$connections[$key][$target]->destroy(); | |
self::$connections[$key][$target] = NULL; | |
} | |
} | |
unset(self::$connections[$key]); | |
} | |
} | |
/** | |
* Instructs the system to temporarily ignore a given key/target. | |
* | |
* At times we need to temporarily disable replica queries. To do so, call this | |
* method with the database key and the target to disable. That database key | |
* will then always fall back to 'default' for that key, even if it's defined. | |
* | |
* @param string $key | |
* The database connection key. | |
* @param string $target | |
* The target of the specified key to ignore. | |
*/ | |
public static function ignoreTarget($key, $target) { | |
self::$ignoreTargets[$key][$target] = TRUE; | |
} | |
/** | |
* Converts a URL to a database connection info array. | |
* | |
* @param string $url | |
* The URL. | |
* @param string $root | |
* The root directory of the Drupal installation. | |
* | |
* @return array | |
* The database connection info. | |
* | |
* @throws \InvalidArgumentException | |
* Exception thrown when the provided URL does not meet the minimum | |
* requirements. | |
*/ | |
public static function convertDbUrlToConnectionInfo($url, $root) { | |
$info = parse_url($url); | |
if (!isset($info['scheme'], $info['host'], $info['path'])) { | |
throw new \InvalidArgumentException('Minimum requirement: driver://host/database'); | |
} | |
$info += array( | |
'user' => '', | |
'pass' => '', | |
'fragment' => '', | |
); | |
// A SQLite database path with two leading slashes indicates a system path. | |
// Otherwise the path is relative to the Drupal root. | |
if ($info['path'][0] === '/') { | |
$info['path'] = substr($info['path'], 1); | |
} | |
if ($info['scheme'] === 'sqlite' && $info['path'][0] !== '/') { | |
$info['path'] = $root . '/' . $info['path']; | |
} | |
$database = array( | |
'driver' => $info['scheme'], | |
'username' => $info['user'], | |
'password' => $info['pass'], | |
'host' => $info['host'], | |
'database' => $info['path'], | |
); | |
if (isset($info['port'])) { | |
$database['port'] = $info['port']; | |
} | |
return $database; | |
} | |
/** | |
* Gets database connection info as a URL. | |
* | |
* @param string $key | |
* (Optional) The database connection key. | |
* | |
* @return string | |
* The connection info as a URL. | |
*/ | |
public static function getConnectionInfoAsUrl($key = 'default') { | |
$db_info = static::getConnectionInfo($key); | |
if ($db_info['default']['driver'] == 'sqlite') { | |
$db_url = 'sqlite://localhost/' . $db_info['default']['database']; | |
} | |
else { | |
$user = ''; | |
if ($db_info['default']['username']) { | |
$user = $db_info['default']['username']; | |
if ($db_info['default']['password']) { | |
$user .= ':' . $db_info['default']['password']; | |
} | |
$user .= '@'; | |
} | |
$db_url = $db_info['default']['driver'] . '://' . $user . $db_info['default']['host']; | |
if (isset($db_info['default']['port'])) { | |
$db_url .= ':' . $db_info['default']['port']; | |
} | |
$db_url .= '/' . $db_info['default']['database']; | |
} | |
if ($db_info['default']['prefix']['default']) { | |
$db_url .= '#' . $db_info['default']['prefix']['default']; | |
} | |
return $db_url; | |
} | |
} |