| 1 |
<?php
|
| 2 |
// $Id: registry.inc,v 1.24 2009/08/24 00:14:18 webchick Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* This file contains the code registry parser engine.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* @defgroup registry Code registry
|
| 11 |
* @{
|
| 12 |
* The code registry engine.
|
| 13 |
*
|
| 14 |
* Drupal maintains an internal registry of all functions or classes in the
|
| 15 |
* system, allowing it to lazy-load code files as needed (reducing the amount
|
| 16 |
* of code that must be parsed on each request).
|
| 17 |
*/
|
| 18 |
|
| 19 |
/**
|
| 20 |
* @see registry_rebuild.
|
| 21 |
*/
|
| 22 |
function _registry_rebuild() {
|
| 23 |
|
| 24 |
// The registry serves as a central autoloader for all classes, including
|
| 25 |
// the database query builders. However, the registry rebuild process
|
| 26 |
// requires write ability to the database, which means having access to the
|
| 27 |
// query builders that require the registry in order to be loaded. That
|
| 28 |
// causes a fatal race condition. Therefore we manually include the
|
| 29 |
// appropriate query builders for the currently active database before the
|
| 30 |
// registry rebuild process runs.
|
| 31 |
$connection_info = Database::getConnectionInfo();
|
| 32 |
$driver = $connection_info['default']['driver'];
|
| 33 |
require_once DRUPAL_ROOT . '/includes/database/query.inc';
|
| 34 |
require_once DRUPAL_ROOT . '/includes/database/select.inc';
|
| 35 |
require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
|
| 36 |
|
| 37 |
// Get current list of modules and their files.
|
| 38 |
$modules = system_rebuild_module_data();
|
| 39 |
// Get the list of files we are going to parse.
|
| 40 |
$files = array();
|
| 41 |
foreach ($modules as &$module) {
|
| 42 |
$dir = dirname($module->uri);
|
| 43 |
|
| 44 |
// Store the module directory for use in hook_registry_files_alter().
|
| 45 |
$module->dir = $dir;
|
| 46 |
|
| 47 |
if ($module->status) {
|
| 48 |
// Add files for enabled modules to the registry.
|
| 49 |
foreach ($module->info['files'] as $file) {
|
| 50 |
$files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
|
| 51 |
}
|
| 52 |
}
|
| 53 |
}
|
| 54 |
foreach (file_scan_directory('includes', '/\.inc$/') as $filename => $file) {
|
| 55 |
$files["$filename"] = array('module' => '', 'weight' => 0);
|
| 56 |
}
|
| 57 |
|
| 58 |
// Allow modules to manually modify the list of files before the registry
|
| 59 |
// parses them. The $modules array provides the .info file information, which
|
| 60 |
// includes the list of files registered to each module. Any files in the
|
| 61 |
// list can then be added to the list of files that the registry will parse,
|
| 62 |
// or modify attributes of a file.
|
| 63 |
drupal_alter('registry_files', $files, $modules);
|
| 64 |
foreach (registry_get_parsed_files() as $filename => $file) {
|
| 65 |
// Add the file creation and modification dates to those files we have
|
| 66 |
// already parsed.
|
| 67 |
if (isset($files[$filename])) {
|
| 68 |
$files[$filename]['filectime'] = $file['filectime'];
|
| 69 |
$files[$filename]['filemtime'] = $file['filemtime'];
|
| 70 |
}
|
| 71 |
else {
|
| 72 |
// Flush the registry of resources in files that are no longer on disc
|
| 73 |
// or are in files that no installed modules require to be parsed.
|
| 74 |
db_delete('registry')
|
| 75 |
->condition('filename', $filename)
|
| 76 |
->execute();
|
| 77 |
db_delete('registry_file')
|
| 78 |
->condition('filename', $filename)
|
| 79 |
->execute();
|
| 80 |
}
|
| 81 |
}
|
| 82 |
$parsed_files = _registry_parse_files($files);
|
| 83 |
|
| 84 |
$unchanged_resources = array();
|
| 85 |
$lookup_cache = array();
|
| 86 |
if ($cache = cache_get('lookup_cache', 'cache_registry')) {
|
| 87 |
$lookup_cache = $cache->data;
|
| 88 |
}
|
| 89 |
foreach ($lookup_cache as $key => $file) {
|
| 90 |
// If the file for this cached resource is carried over unchanged from
|
| 91 |
// the last registry build, then we can safely re-cache it.
|
| 92 |
if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
|
| 93 |
$unchanged_resources[$key] = $file;
|
| 94 |
}
|
| 95 |
}
|
| 96 |
module_implements('', FALSE, TRUE);
|
| 97 |
_registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
|
| 98 |
|
| 99 |
cache_clear_all('*', 'cache_registry', TRUE);
|
| 100 |
|
| 101 |
// We have some unchanged resources, warm up the cache - no need to pay
|
| 102 |
// for looking them up again.
|
| 103 |
if (count($unchanged_resources) > 0) {
|
| 104 |
cache_set('lookup_cache', $unchanged_resources, 'cache_registry');
|
| 105 |
}
|
| 106 |
}
|
| 107 |
|
| 108 |
/**
|
| 109 |
* Return the list of files in registry_file
|
| 110 |
*/
|
| 111 |
function registry_get_parsed_files() {
|
| 112 |
$files = array();
|
| 113 |
// We want the result as a keyed array.
|
| 114 |
$files = db_query("SELECT * FROM {registry_file}")->fetchAllAssoc('filename', PDO::FETCH_ASSOC);
|
| 115 |
return $files;
|
| 116 |
}
|
| 117 |
|
| 118 |
/**
|
| 119 |
* Parse all files that have changed since the registry was last built, and save their function and class listings.
|
| 120 |
*
|
| 121 |
* @param $files
|
| 122 |
* The list of files to check and parse.
|
| 123 |
*/
|
| 124 |
function _registry_parse_files($files) {
|
| 125 |
$parsed_files = array();
|
| 126 |
foreach ($files as $filename => $file) {
|
| 127 |
$filectime = filectime($filename);
|
| 128 |
$filemtime = filemtime($filename);
|
| 129 |
$modified_file = !isset($file['filectime']) || !isset($file['filemtime'])
|
| 130 |
|| $filectime != $file['filectime'] || $filemtime != $file['filemtime'];
|
| 131 |
if ($modified_file) {
|
| 132 |
$contents = file_get_contents($filename);
|
| 133 |
$parsed_files[] = $filename;
|
| 134 |
// We update the filectime/filemtime after we've saved the files resources
|
| 135 |
// rather than here, so if we don't make it through this rebuild, the next
|
| 136 |
// run will reparse the file.
|
| 137 |
_registry_parse_file($filename, $contents, $file['module'], $file['weight']);
|
| 138 |
db_merge('registry_file')
|
| 139 |
->key(array('filename' => $filename))
|
| 140 |
->fields(array(
|
| 141 |
'filectime' => $filectime,
|
| 142 |
'filemtime' => $filemtime,
|
| 143 |
))
|
| 144 |
->execute();
|
| 145 |
}
|
| 146 |
}
|
| 147 |
return $parsed_files;
|
| 148 |
}
|
| 149 |
|
| 150 |
/**
|
| 151 |
* Parse a file and save its function and class listings.
|
| 152 |
*
|
| 153 |
* @param $filename
|
| 154 |
* Name of the file we are going to parse.
|
| 155 |
* @param $contents
|
| 156 |
* Contents of the file we are going to parse as a string.
|
| 157 |
* @param $module
|
| 158 |
* (optional) Name of the module this file belongs to.
|
| 159 |
* @param $weight
|
| 160 |
* (optional) Weight of the module.
|
| 161 |
*/
|
| 162 |
function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
|
| 163 |
static $map = array(T_CLASS => 'class', T_INTERFACE => 'interface');
|
| 164 |
// Delete registry entries for this file, so we can insert the new resources.
|
| 165 |
db_delete('registry')
|
| 166 |
->condition('filename', $filename)
|
| 167 |
->execute();
|
| 168 |
if (preg_match_all('/^\s*(?:abstract)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
|
| 169 |
$query = db_insert('registry')->fields(array('name', 'type', 'filename', 'module', 'weight'));
|
| 170 |
foreach ($matches[2] as $key => $name) {
|
| 171 |
$query->values(array(
|
| 172 |
'name' => $name,
|
| 173 |
'type' => $matches[1][$key],
|
| 174 |
'filename' => $filename,
|
| 175 |
'module' => $module,
|
| 176 |
'weight' => $weight,
|
| 177 |
));
|
| 178 |
}
|
| 179 |
$query->execute();
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
| 183 |
/**
|
| 184 |
* @} End of "defgroup registry".
|
| 185 |
*/
|
| 186 |
|