From de33f74b4040cc3f7880269152b277d90b081cc8 Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Sun, 11 Apr 2010 17:16:45 +0000
Subject: [PATCH] - Patch #688704 by Crell, boombatower, noahb: give DB its own
 autoload function.

---
 includes/bootstrap.inc                      |  5 +-
 includes/cache-install.inc                  |  3 +-
 includes/database/database.inc              | 71 ++++++++++++++++++++-
 includes/install.core.inc                   |  3 +-
 includes/install.inc                        | 31 +--------
 includes/theme.maintenance.inc              |  1 +
 includes/update.inc                         |  3 -
 modules/simpletest/drupal_web_test_case.php |  2 +
 modules/simpletest/simpletest.pages.inc     |  4 +-
 9 files changed, 83 insertions(+), 40 deletions(-)

diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index d2c739aabb87..b64febca3916 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -1947,6 +1947,10 @@ function _drupal_bootstrap_database() {
   Database::setLoggingCallback('watchdog', WATCHDOG_NOTICE, WATCHDOG_ERROR);
 
   // Register autoload functions so that we can access classes and interfaces.
+  // 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');
 }
@@ -2381,7 +2385,6 @@ function drupal_autoload_class($class) {
  */
 function _registry_check_code($type, $name = NULL) {
   static $lookup_cache, $cache_update_needed;
-
   if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) {
     return TRUE;
   }
diff --git a/includes/cache-install.inc b/includes/cache-install.inc
index e0772bea760d..3a754e55fd6e 100644
--- a/includes/cache-install.inc
+++ b/includes/cache-install.inc
@@ -41,8 +41,7 @@ function clear($cid = NULL, $wildcard = FALSE) {
     // subtle bugs, some of which would not be fixed unless the site
     // administrator cleared the cache manually.
     try {
-      if (function_exists('drupal_install_initialize_database')) {
-        drupal_install_initialize_database();
+      if (class_exists('Database')) {
         parent::clear($cid, $wildcard);
       }
     }
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 933a52819be5..20a3128865c8 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -761,7 +761,9 @@ public function truncate($table, array $options = array()) {
   public function schema() {
     if (empty($this->schema)) {
       $class = $this->getDriverClass('DatabaseSchema');
-      $this->schema = new $class($this);
+      if (class_exists($class)) {
+        $this->schema = new $class($this);
+      }
     }
     return $this->schema;
   }
@@ -2109,6 +2111,73 @@ 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)) {
+        require_once "{$base_path}/{$driver}/{$file}";
+        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.
diff --git a/includes/install.core.inc b/includes/install.core.inc
index 8f626f677926..2ddcac495392 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -278,6 +278,7 @@ 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();
@@ -350,7 +351,6 @@ function install_run_tasks(&$install_state) {
       $install_state['tasks_performed'][] = $task_name;
       $install_state['installation_finished'] = empty($tasks_to_perform);
       if ($install_state['database_tables_exist'] && ($task['run'] == INSTALL_TASK_RUN_IF_NOT_COMPLETED || $install_state['installation_finished'])) {
-        drupal_install_initialize_database();
         variable_set('install_task', $install_state['installation_finished'] ? 'done' : $task_name);
       }
     }
@@ -1325,7 +1325,6 @@ function install_load_profile(&$install_state) {
 function install_bootstrap_full(&$install_state) {
   // Bootstrap newly installed Drupal, while preserving existing messages.
   $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : '';
-  drupal_install_initialize_database();
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
   $_SESSION['messages'] = $messages;
diff --git a/includes/install.inc b/includes/install.inc
index 9af6644e0185..c134e676a104 100644
--- a/includes/install.inc
+++ b/includes/install.inc
@@ -219,20 +219,14 @@ function drupal_detect_baseurl($file = 'install.php') {
 function drupal_detect_database_types() {
   $databases = array();
 
-  // Initialize the database system if it has not been
-  // initialized yet. We do not initialize it earlier to make
-  // requirements check during the installation.
-  require_once DRUPAL_ROOT . '/includes/database/database.inc';
-
   // We define a driver as a directory in /includes/database that in turn
   // contains a database.inc file. That allows us to drop in additional drivers
   // without modifying the installer.
   // 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) {
-    include_once "{$file->uri}/install.inc";
-    include_once "{$file->uri}/database.inc";
     $drivers[$file->filename] = $file->uri;
   }
 
@@ -546,28 +540,6 @@ function drupal_verify_profile($install_state) {
   return $requirements;
 }
 
-/**
- * Manually include all files for the active database.
- *
- * Because we have no registry yet, we need to manually include the
- * necessary database include files.
- */
-function drupal_install_initialize_database() {
-  static $included = FALSE;
-
-  if (!$included) {
-    $connection_info = Database::getConnectionInfo();
-    $driver = $connection_info['default']['driver'];
-    require_once DRUPAL_ROOT . '/includes/database/query.inc';
-    require_once DRUPAL_ROOT . '/includes/database/select.inc';
-    require_once DRUPAL_ROOT . '/includes/database/schema.inc';
-    foreach (glob(DRUPAL_ROOT . '/includes/database/' . $driver . '/*.inc') as $include_file) {
-      require_once $include_file;
-    }
-    $included = TRUE;
-  }
-}
-
 /**
  * Callback to install the system module.
  *
@@ -577,7 +549,6 @@ function drupal_install_initialize_database() {
 function drupal_install_system() {
   $system_path = dirname(drupal_get_filename('module', 'system', NULL));
   require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
-  drupal_install_initialize_database();
   module_invoke('system', 'install');
 
   $system_versions = drupal_get_schema_versions('system');
diff --git a/includes/theme.maintenance.inc b/includes/theme.maintenance.inc
index 1bfffa7ad267..c19dd05db219 100644
--- a/includes/theme.maintenance.inc
+++ b/includes/theme.maintenance.inc
@@ -39,6 +39,7 @@ function _drupal_maintenance_theme() {
       // Because we are operating in a crippled environment, we need to
       // bootstrap just enough to allow hook invocations to work.
       require_once DRUPAL_ROOT . '/includes/database/database.inc';
+      spl_autoload_register('db_autoload');
       $module_list['system']['filename'] = 'modules/system/system.module';
       module_list(TRUE, FALSE, FALSE, $module_list);
       drupal_load('module', 'system');
diff --git a/includes/update.inc b/includes/update.inc
index 79471051340f..51806a3d1bda 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -102,9 +102,6 @@ function update_prepare_d7_bootstrap() {
   // Allow the database system to work even if the registry has not been
   // created yet.
   drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
-  drupal_install_initialize_database();
-  spl_autoload_unregister('drupal_autoload_class');
-  spl_autoload_unregister('drupal_autoload_interface');
 
   // The new cache_bootstrap bin is required to bootstrap to
   // DRUPAL_BOOTSTRAP_SESSION, so create it here rather than in
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index ccc2f354beb3..2c6d9c919919 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -566,6 +566,8 @@ protected function setUp() {
     $this->originalPrefix = $db_prefix;
     $this->originalFileDirectory = file_directory_path();
 
+    spl_autoload_register('db_autoload');
+
     // Reset all statics so that test is performed with a clean environment.
     drupal_static_reset();
 
diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc
index c9292613104a..61bfa82f4f2c 100644
--- a/modules/simpletest/simpletest.pages.inc
+++ b/modules/simpletest/simpletest.pages.inc
@@ -185,7 +185,9 @@ function simpletest_test_form_submit($form, &$form_state) {
   // Get list of tests.
   $tests_list = array();
   foreach ($form_state['values'] as $class_name => $value) {
-    if (class_exists($class_name) && $value === 1) {
+  	// Since class_exists() will likely trigger an autoload lookup,
+  	// we do the fast check first.
+    if ($value === 1 && class_exists($class_name)) {
       $tests_list[] = $class_name;
     }
   }
-- 
GitLab