Commit d7a63ff9 authored by Dries's avatar Dries

- Patch #851136 by Damien Tournoud, Crell, chx: make the database autoloading more robust.

parent 84c72d06
......@@ -2244,7 +2244,6 @@ function _drupal_bootstrap_database() {
// The database autoload routine comes first so that we can load the database
// system without hitting the database. That is especially important during
// the install or upgrade process.
spl_autoload_register('db_autoload');
spl_autoload_register('drupal_autoload_class');
spl_autoload_register('drupal_autoload_interface');
}
......
......@@ -648,13 +648,20 @@ protected function expandArguments(&$query, &$args) {
*
* @param string $class
* The class for which we want the potentially driver-specific class.
* @param array $files
* The name of the files in which the driver-specific class can be.
* @param $use_autoload
* If TRUE, attempt to load classes using PHP's autoload capability
* as well as the manual approach here.
* @return string
* The name of the class that should be used for this driver.
*/
public function getDriverClass($class) {
public function getDriverClass($class, array $files = array(), $use_autoload = FALSE) {
if (empty($this->driverClasses[$class])) {
$this->driverClasses[$class] = $class . '_' . $this->driver();
if (!class_exists($this->driverClasses[$class])) {
$driver = $this->driver();
$this->driverClasses[$class] = $class . '_' . $driver;
Database::loadDriverFile($driver, $files);
if (!class_exists($this->driverClasses[$class], $use_autoload)) {
$this->driverClasses[$class] = $class;
}
}
......@@ -681,7 +688,7 @@ public function getDriverClass($class) {
* @see SelectQuery
*/
public function select($table, $alias = NULL, array $options = array()) {
$class = $this->getDriverClass('SelectQuery');
$class = $this->getDriverClass('SelectQuery', array('query.inc', 'select.inc'));
return new $class($table, $alias, $this, $options);
}
......@@ -697,7 +704,7 @@ public function select($table, $alias = NULL, array $options = array()) {
* @see InsertQuery
*/
public function insert($table, array $options = array()) {
$class = $this->getDriverClass('InsertQuery');
$class = $this->getDriverClass('InsertQuery', array('query.inc'));
return new $class($this, $table, $options);
}
......@@ -713,7 +720,7 @@ public function insert($table, array $options = array()) {
* @see MergeQuery
*/
public function merge($table, array $options = array()) {
$class = $this->getDriverClass('MergeQuery');
$class = $this->getDriverClass('MergeQuery', array('query.inc'));
return new $class($this, $table, $options);
}
......@@ -730,7 +737,7 @@ public function merge($table, array $options = array()) {
* @see UpdateQuery
*/
public function update($table, array $options = array()) {
$class = $this->getDriverClass('UpdateQuery');
$class = $this->getDriverClass('UpdateQuery', array('query.inc'));
return new $class($this, $table, $options);
}
......@@ -746,7 +753,7 @@ public function update($table, array $options = array()) {
* @see DeleteQuery
*/
public function delete($table, array $options = array()) {
$class = $this->getDriverClass('DeleteQuery');
$class = $this->getDriverClass('DeleteQuery', array('query.inc'));
return new $class($this, $table, $options);
}
......@@ -762,7 +769,7 @@ public function delete($table, array $options = array()) {
* @see TruncateQuery
*/
public function truncate($table, array $options = array()) {
$class = $this->getDriverClass('TruncateQuery');
$class = $this->getDriverClass('TruncateQuery', array('query.inc'));
return new $class($this, $table, $options);
}
......@@ -776,7 +783,7 @@ public function truncate($table, array $options = array()) {
*/
public function schema() {
if (empty($this->schema)) {
$class = $this->getDriverClass('DatabaseSchema');
$class = $this->getDriverClass('DatabaseSchema', array('schema.inc'));
if (class_exists($class)) {
$this->schema = new $class($this);
}
......@@ -1581,6 +1588,34 @@ 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;
}
}
}
}
}
/**
......@@ -2108,82 +2143,6 @@ public function valid() {
}
}
/**
* Autoload callback for the database system.
*/
function db_autoload($class) {
static $base_path = '';
static $checked = array();
static $files = array(
'query.inc' => array(
'QueryPlaceholderInterface',
'QueryConditionInterface', 'DatabaseCondition',
'Query', 'DeleteQuery', 'InsertQuery', 'UpdateQuery', 'MergeQuery', 'TruncateQuery',
'QueryAlterableInterface',
),
'select.inc' => array('QueryAlterableInterface', 'SelectQueryInterface', 'SelectQuery', 'SelectQueryExtender'),
'database.inc' => array('DatabaseConnection'),
'log.inc' => array('DatabaseLog'),
'prefetch.inc' => array('DatabaseStatementPrefetch'),
'schema.inc' => array('DatabaseSchema'),
);
// If a class doesn't exist, it may get checked a second time
// by class_exists(). If so, just bail out now.
if (isset($checked[$class])) {
return;
}
$checked[$class] = TRUE;
if (empty($base_path)) {
$base_path = dirname(realpath(__FILE__));
}
// If there is an underscore in the class name, we know it's a
// driver-specific file so check for those. If not, it's a generic.
// Note that we use require_once here instead of require because of a
// quirk in class_exists(). By default, class_exists() will try to
// autoload a class if it's not found. However, we cannot tell
// at this point whether or not the class is going to exist, only
// the file that it would be in if it does exist. That means we may
// try to include a file that was already included by another
// autoload call, which would break. Using require_once() neatly
// avoids that issue.
if (strpos($class, '_') !== FALSE) {
list($base, $driver) = explode('_', $class);
// Drivers have an extra file, and may put their SelectQuery implementation
// in the main query file since it's so small.
$driver_files = $files;
$driver_files['query.inc'][] = 'SelectQuery';
$driver_files['install.inc'] = array('DatabaseTasks');
foreach ($driver_files as $file => $classes) {
if (in_array($base, $classes)) {
$filename = "{$base_path}/{$driver}/{$file}";
// We might end up looking in a file that doesn't exist, so check that.
if (file_exists($filename)) {
require_once $filename;
// If the class now exists, we're done. Otherwise keep searching in
// additional files.
if (class_exists($class, FALSE) || interface_exists($class, FALSE)) {
return;
}
}
}
}
}
else {
foreach ($files as $file => $classes) {
if (in_array($class, $classes)) {
require_once $base_path . '/' . $file;
return;
}
}
}
}
/**
* The following utility functions are simply convenience wrappers.
*
......@@ -2915,4 +2874,3 @@ function db_ignore_slave() {
$_SESSION['ignore_slave_server'] = REQUEST_TIME + $duration;
}
}
......@@ -6,6 +6,8 @@
* Generic Database schema code.
*/
require_once dirname(__FILE__) . '/query.inc';
/**
* @defgroup schemaapi Schema API
* @{
......
......@@ -6,6 +6,8 @@
* @{
*/
require_once dirname(__FILE__) . '/query.inc';
/**
* Interface for extendable query objects.
*
......@@ -642,7 +644,9 @@ public function havingCompile(DatabaseConnection $connection) {
/* Implementations of QueryExtendableInterface. */
public function extend($extender_name) {
$class = $this->connection->getDriverClass($extender_name);
// The extender can be anywhere so this needs to go to the registry, which
// is surely loaded by now.
$class = $this->connection->getDriverClass($extender_name, array(), TRUE);
return new $class($this, $this->connection);
}
......
......@@ -289,7 +289,6 @@ function install_begin_request(&$install_state) {
// Initialize the database system. Note that the connection
// won't be initialized until it is actually requested.
require_once DRUPAL_ROOT . '/includes/database/database.inc';
spl_autoload_register('db_autoload');
// Verify the last completed task in the database, if there is one.
$task = install_verify_completed_task();
......
......@@ -238,7 +238,6 @@ function drupal_detect_database_types() {
// Because we have no registry yet, we need to also include the install.inc
// file for the driver explicitly.
require_once DRUPAL_ROOT . '/includes/database/database.inc';
spl_autoload_register('db_autoload');
foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
if (file_exists($file->uri . '/database.inc') && file_exists($file->uri . '/install.inc')) {
$drivers[$file->filename] = $file->uri;
......@@ -246,8 +245,7 @@ function drupal_detect_database_types() {
}
foreach ($drivers as $driver => $file) {
$class = 'DatabaseTasks_' . $driver;
$installer = new $class();
$installer = db_installer_object($driver);
if ($installer->installable()) {
$databases[$driver] = $installer->name();
}
......@@ -1123,8 +1121,18 @@ function install_profile_info($profile, $locale = 'en') {
* encoding.
*/
function db_run_tasks($driver) {
$task_class = 'DatabaseTasks_' . $driver;
$DatabaseTasks = new $task_class();
$DatabaseTasks->runTasks();
db_installer_object($driver)->runTasks();
return TRUE;
}
/**
* Returns a database installer object.
*
* @param $driver
* The name of the driver.
*/
function db_installer_object($driver) {
Database::loadDriverFile($driver, array('install.inc'));
$task_class = 'DatabaseTasks_' . $driver;
return new $task_class();
}
......@@ -41,7 +41,6 @@ function _drupal_maintenance_theme() {
// to work. See _drupal_log_error().
if (!class_exists('Database', FALSE)) {
require_once DRUPAL_ROOT . '/includes/database/database.inc';
spl_autoload_register('db_autoload');
}
// We use the default theme as the maintenance theme. If a default theme
......
......@@ -611,8 +611,6 @@ protected function setUp() {
// Store necessary current values before switching to the test environment.
$this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
spl_autoload_register('db_autoload');
// Reset all statics so that test is performed with a clean environment.
drupal_static_reset();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment