Commit 67429ab1 authored by Crell's avatar Crell
Browse files

Initial move and rename of database classes to PSR-0 namespaces. So far only...

Initial move and rename of database classes to PSR-0 namespaces.  So far only MySQL has been converted.
parent f78b16e7
This diff is collapsed.
<?php
namespace Drupal\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 = NULL;
/**
* 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 $logging_key
* The logging key to log.
* @param $key
* The database connection key for which we want to log.
*
* @return DatabaseLog
* 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 DatabaseLog
*/
final public static function startLog($logging_key, $key = 'default') {
if (empty(self::$logs[$key])) {
self::$logs[$key] = new DatabaseLog($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 $logging_key
* The logging key to log.
* @param $key
* The database connection key for which we want to log.
*
* @return array
* The query log for the specified logging key and connection.
*
* @see DatabaseLog
*/
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 $target
* The database target name.
* @param $key
* The database connection key. Defaults to NULL which means the active key.
*
* @return DatabaseConnection
* 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
// "slave", indicating to use a slave SQL server if one is available. If
// it's not available, then the default/master 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
* 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
* The previous database connection key.
*/
final public static function setActiveConnection($key = 'default') {
if (empty(self::$databaseInfo)) {
self::parseConnectionInfo();
}
if (!empty(self::$databaseInfo[$key])) {
$old_key = self::$activeKey;
self::$activeKey = $key;
return $old_key;
}
}
/**
* Process the configuration file for database information.
*/
final public static function parseConnectionInfo() {
global $databases;
$database_info = is_array($databases) ? $databases : array();
foreach ($database_info as $index => $info) {
foreach ($database_info[$index] as $target => $value) {
// 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 slave servers.
if (empty($value['driver'])) {
$database_info[$index][$target] = $database_info[$index][$target][mt_rand(0, count($database_info[$index][$target]) - 1)];
}
// Parse the prefix information.
if (!isset($database_info[$index][$target]['prefix'])) {
// Default to an empty prefix.
$database_info[$index][$target]['prefix'] = array(
'default' => '',
);
}
elseif (!is_array($database_info[$index][$target]['prefix'])) {
// Transform the flat form into an array form.
$database_info[$index][$target]['prefix'] = array(
'default' => $database_info[$index][$target]['prefix'],
);
}
}
}
if (!is_array(self::$databaseInfo)) {
self::$databaseInfo = $database_info;
}
// Merge the new $database_info into the existing.
// array_merge_recursive() cannot be used, as it would make multiple
// database, user, and password keys in the same database array.
else {
foreach ($database_info as $database_key => $database_values) {
foreach ($database_values as $target => $target_values) {
self::$databaseInfo[$database_key][$target] = $target_values;
}
}
}
}
/**
* Adds database connection information for a given key/target.
*
* This method allows the addition of new connection credentials 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 $key
* The database key.
* @param $target
* The database target name.
* @param $info
* The database connection information, as it would be defined in
* settings.php. Note that the structure of this array will depend on the
* database driver it is connecting to.
*/
public static function addConnectionInfo($key, $target, $info) {
if (empty(self::$databaseInfo[$key][$target])) {
self::$databaseInfo[$key][$target] = $info;
}
}
/**
* Gets information on the specified database connection.
*
* @param $connection
* The connection key for which we want information.
*/
final public static function getConnectionInfo($key = 'default') {
if (empty(self::$databaseInfo)) {
self::parseConnectionInfo();
}
if (!empty(self::$databaseInfo[$key])) {
return self::$databaseInfo[$key];
}
}
/**
* Rename a connection and its corresponding connection information.
*
* @param $old_key
* The old connection key.
* @param $new_key
* The new connection key.
* @return
* TRUE in case of success, FALSE otherwise.
*/
final public static function renameConnection($old_key, $new_key) {
if (empty(self::$databaseInfo)) {
self::parseConnectionInfo();
}
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 $key
* The connection key.
* @return
* TRUE in case of success, FALSE otherwise.
*/
final public static function removeConnection($key) {
if (isset(self::$databaseInfo[$key])) {
unset(self::$databaseInfo[$key]);
unset(self::$connections[$key]);
return TRUE;
}
else {
return FALSE;
}
}
/**
* Opens a connection to the server specified by the given key and target.
*
* @param $key
* The database connection key, as specified in settings.php. The default is
* "default".
* @param $target
* The database target to open.
*
* @throws DatabaseConnectionNotDefinedException
* @throws DatabaseDriverNotSpecifiedException
*/
final protected static function openConnection($key, $target) {
if (empty(self::$databaseInfo)) {
self::parseConnectionInfo();
}
// If the requested database does not exist then it is an unrecoverable
// error.
if (!isset(self::$databaseInfo[$key])) {
throw new DatabaseConnectionNotDefinedException('The specified database connection is not defined: ' . $key);
}
if (!$driver = self::$databaseInfo[$key][$target]['driver']) {
throw new DatabaseDriverNotSpecifiedException('Driver not specified for this database connection: ' . $key);
}
// We cannot rely on the registry yet, because the registry requires an
// open database connection.
$driver_class = "Drupal\\Database\\Driver\\{$driver}\\Connection";
$new_connection = new $driver_class(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 $target
* The database target name. Defaults to NULL meaning that all target
* connections will be closed.
* @param $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 the connection, we need to unset the static variable.
if (isset($target)) {
unset(self::$connections[$key][$target]);
}
else {
unset(self::$connections[$key]);
}
}
/**
* Instructs the system to temporarily ignore a given key/target.
*
* At times we need to temporarily disable slave 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 $key
* The database connection key.
* @param $target
* The target of the specified key to ignore.
*/
public static function ignoreTarget($key, $target) {
self::$ignoreTargets[$key][$target] = TRUE;
}
/**
* Load a file for the database that might hold a class.
*
* @param $driver
* The name of the driver.
* @param array $files
* The name of the files the driver specific class can be.
*/
public static function loadDriverFile($driver, array $files = array()) {
static $base_path;
if (empty($base_path)) {
$base_path = dirname(realpath(__FILE__));
}
$driver_base_path = "$base_path/$driver";
foreach ($files as $file) {
// Load the base file first so that classes extending base classes will
// have the base class loaded.
foreach (array("$base_path/$file", "$driver_base_path/$file") as $filename) {
// The OS caches file_exists() and PHP caches require_once(), so
// we'll let both of those take care of performance here.
if (file_exists($filename)) {
require_once $filename;
}
}
}
}
}
<?php
namespace Drupal\Database;
use Exception;
/**
* Exception thrown if an undefined database connection is requested.
*/
class DatabaseConnectionNotDefinedException extends Exception {}
<?php
namespace Drupal\Database;
use Exception;
/**
* Exception thrown if no driver is specified for a database connection.
*/
class DatabaseDriverNotSpecifiedException extends Exception {}
<?php
/**
* @file
* Logging classes for the database layer.
*/
namespace Drupal\Database;
/**
* Database query logger.
......
<?php
/**
* @file
* Generic Database schema code.
*/
namespace Drupal\Database;
require_once __DIR__ . '/query.inc';
use Drupal\Database\Query\PlaceholderInterface;
/**
* @defgroup schemaapi Schema API
......@@ -156,7 +153,7 @@
* @see drupal_install_schema()
*/
abstract class DatabaseSchema implements QueryPlaceholderInterface {
abstract class DatabaseSchema implements PlaceholderInterface {
protected $connection;
......@@ -194,14 +191,14 @@ public function __clone() {
}
/**
* Implements QueryPlaceHolderInterface::uniqueIdentifier().
* Implements PlaceHolderInterface::uniqueIdentifier().
*/
public function uniqueIdentifier() {
return $this->uniqueIdentifier;
}
/**
* Implements QueryPlaceHolderInterface::nextPlaceholder().
* Implements PlaceHolderInterface::nextPlaceholder().
*/
public function nextPlaceholder() {
return $this->placeholder++;
......@@ -698,26 +695,3 @@ public function prepareComment($comment, $length = NULL) {
return $this->connection->quote($comment);
}
}
/**
* Exception thrown if an object being created already exists.
*
* For example, this exception should be thrown whenever there is an attempt to
* create a new database table, field, or index that already exists in the
* database schema.
*/
class DatabaseSchemaObjectExistsException extends Exception {}
/**
* Exception thrown if an object being modified doesn't exist yet.
*
* For example, this exception should be thrown whenever there is an attempt to
* modify a database table, field, or index that does not currently exist in
* the database schema.
*/
class DatabaseSchemaObjectDoesNotExistException extends Exception {}
/**
* @} End of "defgroup schemaapi".
*/
<?php
namespace Drupal\Database;
use Exception;
/**
* Exception thrown if an object being modified doesn't exist yet.
*
* For example, this exception should be thrown whenever there is an attempt to
* modify a database table, field, or index that does not currently exist in
* the database schema.
*/
class DatabaseSchemaObjectDoesNotExistException extends Exception {}
<?php
namespace Drupal\Database;
use Exception;
/**
* Exception thrown if an object being created already exists.
*
* For example, this exception should be thrown whenever there is an attempt to
* create a new database table, field, or index that already exists in the
* database schema.
*/
class DatabaseSchemaObjectExistsException extends Exception {}
<?php
namespace Drupal\Database;
use PDO;
use PDOStatement;
/**
* Default implementation of DatabaseStatementInterface.
*
* PDO allows us to extend the PDOStatement class to provide additional
* functionality beyond that offered by default. We do need extra
* functionality. By default, this class is not driver-specific. If a given
* driver needs to set a custom statement class, it may do so in its
* constructor.
*
* @see http://us.php.net/pdostatement
*/
class DatabaseStatementBase extends PDOStatement implements DatabaseStatementInterface {
/**
* Reference to the database connection object for this statement.
*
* The name $dbh is inherited from PDOStatement.
*