Commit b65fd7fe authored by catch's avatar catch
Browse files

Issue #1541674 by Berdir, Tor Arne Thune, sun, plach, dixon_, tim.plunkett: Remove the registry.

parent a2215bf8
......@@ -69,7 +69,7 @@ function authorize_access_allowed() {
require_once DRUPAL_ROOT . '/core/includes/ajax.inc';
// We prepare only a minimal bootstrap. This includes the database and
// variables, however, so we have access to the class autoloader registry.
// variables, however, so we have access to the class autoloader.
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
// This must go after drupal_bootstrap(), which unsets globals!
......
......@@ -278,16 +278,6 @@
*/
const PASS_THROUGH = -1;
/**
* Signals that the registry lookup cache should be reset.
*/
const REGISTRY_RESET_LOOKUP_CACHE = 1;
/**
* Signals that the registry lookup cache should be written to storage.
*/
const REGISTRY_WRITE_LOOKUP_CACHE = 2;
/**
* Regular expression to match PHP function names.
*
......@@ -2362,13 +2352,6 @@ function _drupal_bootstrap_database() {
// Initialize the database system. Note that the connection
// won't be initialized until it is actually requested.
require_once DRUPAL_ROOT . '/core/includes/database.inc';
// 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('drupal_autoload_class');
spl_autoload_register('drupal_autoload_interface');
}
/**
......@@ -3028,11 +3011,6 @@ function ip_address() {
return $ip_address;
}
/**
* @addtogroup registry
* @{
*/
/**
* Initializes and returns the class loader.
*
......@@ -3118,167 +3096,6 @@ function drupal_classloader_register($name, $path) {
$loader->registerNamespace('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
}
/**
* Confirms that an interface is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $interface
* The name of the interface to check or load.
*
* @return
* TRUE if the interface is currently available, FALSE otherwise.
*/
function drupal_autoload_interface($interface) {
return _registry_check_code('interface', $interface);
}
/**
* Confirms that a class is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $class
* The name of the class to check or load.
*
* @return
* TRUE if the class is currently available, FALSE otherwise.
*/
function drupal_autoload_class($class) {
return _registry_check_code('class', $class);
}
/**
* Checks for a resource in the registry.
*
* @param $type
* The type of resource we are looking up, or one of the constants
* REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE, which
* signal that we should reset or write the cache, respectively.
* @param $name
* The name of the resource, or NULL if either of the REGISTRY_* constants
* is passed in.
*
* @return
* TRUE if the resource was found, FALSE if not.
* NULL if either of the REGISTRY_* constants is passed in as $type.
*/
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;
}
if (!isset($lookup_cache)) {
$lookup_cache = array();
if ($cache = cache('bootstrap')->get('lookup_cache')) {
$lookup_cache = $cache->data;
}
}
// When we rebuild the registry, we need to reset this cache so
// we don't keep lookups for resources that changed during the rebuild.
if ($type == REGISTRY_RESET_LOOKUP_CACHE) {
$cache_update_needed = TRUE;
$lookup_cache = NULL;
return;
}
// Called when closing requests, we write to permanent storage if there
// changes to the lookup cache for this request.
if ($type == REGISTRY_WRITE_LOOKUP_CACHE) {
if ($cache_update_needed) {
cache('bootstrap')->set('lookup_cache', $lookup_cache);
}
return;
}
// $type is either 'interface' or 'class', so we only need the first letter to
// keep the cache key unique.
$cache_key = $type[0] . $name;
if (isset($lookup_cache[$cache_key])) {
if ($lookup_cache[$cache_key]) {
require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
}
return (bool) $lookup_cache[$cache_key];
}
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
$file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
':name' => $name,
':type' => $type,
))
->fetchField();
// Flag that we've run a lookup query and need to update the cache.
$cache_update_needed = TRUE;
// Misses are valuable information worth caching, so cache even if
// $file is FALSE.
$lookup_cache[$cache_key] = $file;
if ($file) {
require_once DRUPAL_ROOT . '/' . $file;
return TRUE;
}
else {
return FALSE;
}
}
/**
* Rescans all enabled modules and rebuilds the registry.
*
* Rescans all code in modules or includes directories, storing the location of
* each interface or class in the database.
*/
function registry_rebuild() {
system_rebuild_module_data();
registry_update();
}
/**
* Updates the registry based on the latest files listed in the database.
*
* This function should be used when system_rebuild_module_data() does not need
* to be called, because it is already known that the list of files in the
* {system} table matches those in the file system.
*
* @return
* TRUE if the registry was rebuilt, FALSE if another thread was rebuilding
* in parallel and the current thread just waited for completion.
*
* @see registry_rebuild()
*/
function registry_update() {
// install_system_module() calls module_enable() which calls into this
// function during initial system installation, so the lock system is neither
// loaded nor does its storage exist yet.
$in_installer = drupal_installation_attempted();
if (!$in_installer && !lock_acquire(__FUNCTION__)) {
// Another request got the lock, wait for it to finish.
lock_wait(__FUNCTION__);
return FALSE;
}
require_once DRUPAL_ROOT . '/core/includes/registry.inc';
_registry_update();
if (!$in_installer) {
lock_release(__FUNCTION__);
}
return TRUE;
}
/**
* @} End of "addtogroup registry".
*/
/**
* Provides central static variable storage.
*
......
......@@ -7061,10 +7061,8 @@ function drupal_flush_all_caches() {
// Clear all non-drupal_static() static caches.
// None currently; kept if any static caches need to be reset in the future.
// Update and synchronize the class registry and extension information based
// on current/actual code.
// Module data is rebuilt as part of registry_rebuild().
registry_rebuild();
// Rebuild module and theme data.
system_rebuild_module_data();
system_rebuild_theme_data();
// Ensure that all modules that are currently supposed to be enabled are
......
......@@ -155,8 +155,6 @@ function drupal_get_database_types() {
// We define a driver as a directory in /core/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 . '/core/includes/database.inc';
foreach (file_scan_directory(DRUPAL_ROOT . '/core/lib/Drupal/Core/Database/Driver', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
if (file_exists($file->uri . '/Install/Tasks.php')) {
......
......@@ -442,8 +442,6 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
system_list_reset();
module_implements_reset();
_system_update_bootstrap_status();
// Update the registry to include it.
registry_update();
// Refresh the schema to include it.
drupal_get_schema(NULL, TRUE);
// Update the theme registry to include it.
......@@ -566,8 +564,6 @@ function module_disable($module_list, $disable_dependents = TRUE) {
// Invoke hook_modules_disabled before disabling modules,
// so we can still call module hooks to get information.
module_invoke_all('modules_disabled', $invoke_modules);
// Update the registry to remove the newly-disabled module.
registry_update();
_system_update_bootstrap_status();
// Update the theme registry to remove the newly-disabled module.
drupal_theme_rebuild();
......
<?php
use Drupal\Core\Database\Database;
/**
* @file
* This file contains the code registry parser engine.
*/
/**
* @defgroup registry Code registry
* @{
* The code registry engine.
*
* Drupal maintains an internal registry of all functions or classes in the
* system, allowing it to lazy-load code files as needed (reducing the amount
* of code that must be parsed on each request).
*/
/**
* Does the work for registry_update().
*/
function _registry_update() {
// The registry serves as a central autoloader for all non-namespaced classes.
// It is backed by the database, but the database system is autoloaded using
// a PSR-0 class loader. That avoids a fata circular dependency here, since
// the other class loader will be able to load the database for us.
$connection_info = Database::getConnectionInfo();
$driver = $connection_info['default']['driver'];
// During the first registry rebuild in a request, we check all the files.
// During subsequent rebuilds, we only add new files. It makes the rebuilding
// process faster during installation of modules.
static $check_existing_files = TRUE;
// Get current list of modules and their files.
$modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
// Get the list of files we are going to parse.
$files = array();
foreach ($modules as &$module) {
$module->info = unserialize($module->info);
$dir = dirname($module->filename);
// Store the module directory for use in hook_registry_files_alter().
$module->dir = $dir;
if ($module->status) {
// Add files for enabled modules to the registry.
foreach ($module->info['files'] as $file) {
$files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
}
}
}
$transaction = db_transaction();
try {
// Allow modules to manually modify the list of files before the registry
// parses them. The $modules array provides the .info file information, which
// includes the list of files registered to each module. Any files in the
// list can then be added to the list of files that the registry will parse,
// or modify attributes of a file.
drupal_alter('registry_files', $files, $modules);
foreach (registry_get_parsed_files() as $filename => $file) {
// Add the hash for those files we have already parsed.
if (isset($files[$filename])) {
if ($check_existing_files) {
$files[$filename]['hash'] = $file['hash'];
}
else {
// Ignore that file for this request, it has been parsed previously
// and it is unlikely it has changed.
unset($files[$filename]);
}
}
else {
// Flush the registry of resources in files that are no longer on disc
// or are in files that no installed modules require to be parsed.
db_delete('registry')
->condition('filename', $filename)
->execute();
db_delete('registry_file')
->condition('filename', $filename)
->execute();
}
}
$parsed_files = _registry_parse_files($files);
$unchanged_resources = array();
$lookup_cache = array();
if ($cache = cache('bootstrap')->get('lookup_cache')) {
$lookup_cache = $cache->data;
}
foreach ($lookup_cache as $key => $file) {
// If the file for this cached resource is carried over unchanged from
// the last registry build, then we can safely re-cache it.
if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
$unchanged_resources[$key] = $file;
}
}
module_implements_reset();
_registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('registry', $e);
throw $e;
}
// During the next run in this request, don't bother re-checking existing
// files.
$check_existing_files = FALSE;
// We have some unchanged resources, warm up the cache - no need to pay
// for looking them up again.
if (count($unchanged_resources) > 0) {
cache('bootstrap')->set('lookup_cache', $unchanged_resources);
}
}
/**
* Return the list of files in registry_file
*/
function registry_get_parsed_files() {
$files = array();
// We want the result as a keyed array.
$files = db_query("SELECT * FROM {registry_file}")->fetchAllAssoc('filename', PDO::FETCH_ASSOC);
return $files;
}
/**
* Parse all files that have changed since the registry was last built, and save their function and class listings.
*
* @param $files
* The list of files to check and parse.
*/
function _registry_parse_files($files) {
$parsed_files = array();
foreach ($files as $filename => $file) {
if (file_exists($filename)) {
$hash = hash_file('sha256', $filename);
if (empty($file['hash']) || $file['hash'] != $hash) {
$file['hash'] = $hash;
$parsed_files[$filename] = $file;
}
}
}
foreach ($parsed_files as $filename => $file) {
_registry_parse_file($filename, file_get_contents($filename), $file['module'], $file['weight']);
db_merge('registry_file')
->key(array('filename' => $filename))
->fields(array(
'hash' => $file['hash'],
))
->execute();
}
return array_keys($parsed_files);
}
/**
* Parse a file and save its function and class listings.
*
* @param $filename
* Name of the file we are going to parse.
* @param $contents
* Contents of the file we are going to parse as a string.
* @param $module
* (optional) Name of the module this file belongs to.
* @param $weight
* (optional) Weight of the module.
*/
function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
foreach ($matches[2] as $key => $name) {
db_merge('registry')
->key(array(
'name' => $name,
'type' => $matches[1][$key],
))
->fields(array(
'filename' => $filename,
'module' => $module,
'weight' => $weight,
))
->execute();
}
// Delete any resources for this file where the name is not in the list
// we just merged in.
db_delete('registry')
->condition('filename', $filename)
->condition('name', $matches[2], 'NOT IN')
->execute();
}
}
/**
* @} End of "defgroup registry".
*/
......@@ -374,8 +374,6 @@ final protected static function openConnection($key, $target) {
throw new DriverNotSpecifiedException('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\\Core\\Database\\Driver\\{$driver}\\Connection";
$new_connection = new $driver_class(self::$databaseInfo[$key][$target]);
$new_connection->setTarget($target);
......
......@@ -29,7 +29,6 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
*/
public function onTerminate(PostResponseEvent $event) {
module_invoke_all('exit');
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
module_implements_write_cache();
system_run_automated_cron();
......
......@@ -623,8 +623,6 @@ protected function setUp() {
include_once DRUPAL_ROOT . '/core/includes/install.inc';
drupal_install_system();
$this->preloadRegistry();
// Set path variables.
variable_set('file_public_path', $this->public_files_directory);
variable_set('file_private_path', $this->private_files_directory);
......@@ -706,42 +704,6 @@ protected function setUp() {
$this->setup = TRUE;
}
/**
* Preload the registry from the testing site.
*
* This method is called by Drupal\simpletest\WebTestBase::setUp(), and preloads
* the registry from the testing site to cut down on the time it takes to
* set up a clean environment for the current test run.
*/
protected function preloadRegistry() {
// Use two separate queries, each with their own connections: copy the
// {registry} and {registry_file} tables over from the parent installation
// to the child installation.
$original_connection = Database::getConnection('default', 'simpletest_original_default');
$test_connection = Database::getConnection();
foreach (array('registry', 'registry_file') as $table) {
// Find the records from the parent database.
$source_query = $original_connection
->select($table, array(), array('fetch' => PDO::FETCH_ASSOC))
->fields($table);
$dest_query = $test_connection->insert($table);
$first = TRUE;
foreach ($source_query->execute() as $row) {
if ($first) {
$dest_query->fields(array_keys($row));
$first = FALSE;
}
// Insert the records into the child database.
$dest_query->values($row);
}
$dest_query->execute();
}
}
/**
* Reset all data structures after having enabled new modules.
*
......
......@@ -288,13 +288,9 @@ function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALS
/**
* Get a list of all of the tests provided by the system.
*
* The list of test classes is loaded from the registry where it looks for
* files ending in ".test". Once loaded the test list is cached and stored in
* a static variable. In order to list tests provided by disabled modules
* hook_registry_files_alter() is used to forcefully add them to the registry.
*
* PSR-0 classes are found by searching the designated directory for each module
* for files matching the PSR-0 standard.
* The list of test classes is loaded by searching the designated directory for
* each module for files matching the PSR-0 standard. Once loaded the test list
* is cached and stored in a static variable.
*
* @return
* An array of tests keyed with the groups specified in each of the tests
......@@ -310,7 +306,6 @@ function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALS
* ),
* );
* @endcode
* @see simpletest_registry_files_alter()
*/
function simpletest_test_get_all() {
$groups = &drupal_static(__FUNCTION__);
......@@ -326,11 +321,8 @@ function simpletest_test_get_all() {
$groups = $cache->data;
}
else {
// Select all clases in files ending with .test.
// @todo: Remove this once all tests have been ported to PSR-0.
$classes = db_query("SELECT name FROM {registry} WHERE type = :type AND filename LIKE :name", array(':type' => 'class', ':name' => '%.test'))->fetchCol();
// Select all PSR-0 classes in the Tests namespace of all modules.
$classes = array();
$system_list = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed();
foreach ($system_list as $name => $filename) {
......@@ -402,29 +394,6 @@ function simpletest_classloader_register() {
}
}
/**
* Implements hook_registry_files_alter().
*
* Add the test files for disabled modules so that we get a list containing
* all the avialable tests.
*/
function simpletest_registry_files_alter(&$files, $modules) {
foreach ($modules as $module) {
// Only add test files for disabled modules, as enabled modules should
// already include any test files they provide.
if (!$module->status) {
$dir = $module->dir;
if (!empty($module->info['files'])) {
foreach ($module->info['files'] as $file) {
if (substr($file, -5) == '.test') {
$files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
}
}
}
}
}
}
/**
* Generate test file.
*/
......@@ -469,7 +438,6 @@ function simpletest_clean_environment() {
}
// Detect test classes that have been added, renamed or deleted.
registry_rebuild();
cache()->delete('simpletest');
}
......
......@@ -104,11 +104,6 @@ protected function setUp() {
return FALSE;
}
// Unregister the registry.
// This is required to make sure that the database layer works properly.
spl_autoload_unregister('drupal_autoload_class');
spl_autoload_unregister('drupal_autoload_interface');
// Load the database from the portable PHP dump.
// The files may be gzipped.
foreach ($this->databaseDumpFiles as $file) {
......@@ -252,10 +247,6 @@ protected function performUpgrade($register_errors = TRUE) {