Commit f33faa2c authored by Dries's avatar Dries

- Patch #221964 by chx, dopry, webernet, moshe, webchick, justinrandall, flobruit

  et al.  Can you say 'registry'?  Drupal now 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). The
  list of included files is cached per menu callback for subsequent loading by
  the menu router. This way, a given page request will have all the code it needs
  but little else, minimizing time spent parsing unneeded code.
parent 2e18cb89
<?php
// $Id$
/**
* @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). The list of included files is
* cached per menu callback for subsequent loading by the menu router. This way,
* a given page request will have all the code it needs but little else, minimizing
* time spent parsing unneeded code.
*/
/**
* @See drupal_rebuild_code_registry.
*/
function _drupal_rebuild_code_registry() {
// Reset the resources cache.
_registry_get_resource_name();
// Get the list of files we are going to parse.
$files = array();
foreach (module_rebuild_cache() as $module) {
if ($module->status) {
$dir = dirname($module->filename);
foreach ($module->info['files'] as $file) {
$files["./$dir/$file"] = array();
}
}
}
foreach (file_scan_directory('includes', '\.inc$') as $filename => $file) {
$files["./$filename"] = array();
}
foreach (registry_get_parsed_files() as $filename => $file) {
// Add the md5 to those files we've already parsed.
if (isset($files[$filename])) {
$files[$filename]['md5'] = $file['md5'];
}
else {
// Flush the registry of resources in files that are no longer on disc
// or don't belong to installed modules.
db_query("DELETE FROM {registry} WHERE filename = '%s'", $filename);
db_query("DELETE FROM {registry_file} WHERE filename = '%s'", $filename);
}
}
_registry_parse_files($files);
cache_clear_all('*', 'cache_registry');
}
/**
* Return the list of files in registry_file
*/
function registry_get_parsed_files() {
$files = array();
$res = db_query("SELECT * FROM {registry_file}");
while ($file = db_fetch_array($res)) {
$files[$file['filename']] = $file;
}
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) {
$changed_files = array();
foreach ($files as $filename => $file) {
$contents = file_get_contents($filename);
$md5 = md5($contents);
$new_file = !isset($file['md5']);
if ($new_file || $md5 != $file['md5']) {
// We update the md5 after we've saved the files resources rather than here, so if we
// don't make it through this rebuild, the next run will reparse the file.
_registry_parse_file($filename, $contents);
$file['md5'] = $md5;
if ($new_file) {
db_query("INSERT INTO {registry_file} (md5, filename) VALUES ('%s', '%s')", $md5, $filename);
}
else {
db_query("UPDATE {registry_file} SET md5 = '%s' WHERE filename = '%s'", $md5, $filename);
}
}
}
}
/**
* 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.
*/
function _registry_parse_file($filename, $contents) {
static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface');
// Delete registry entries for this file, so we can insert the new resources.
db_query("DELETE FROM {registry} WHERE filename = '%s'", $filename);
$tokens = token_get_all($contents);
while ($token = next($tokens)) {
// Ignore all tokens except for those we are specifically saving.
if (is_array($token) && isset($map[$token[0]])) {
$type = $map[$token[0]];
if ($resource_name = _registry_get_resource_name($tokens, $type)) {
db_query("INSERT INTO {registry} (name, type, filename) VALUES ('%s', '%s', '%s')", $resource_name, $type, $filename);
// We skip the body because classes may contain functions.
_registry_skip_body($tokens);
}
}
}
}
/**
* Derive the name of the next resource in the token stream.
*
* When called without arguments, it resets its static cache.
*
* @param $tokens
* The collection of tokens for the current file being parsed.
* @param $type
* The human-readable token name, either: "function", "class", or "interface".
* @return
* The name of the resource, or FALSE if the resource has already been processed.
*/
function _registry_get_resource_name(&$tokens = NULL, $type = NULL) {
// Keep a running list of all resources we've saved so far, so that we never
// save one more than once.
static $resources;
if (!isset($tokens)) {
$resources = array();
return;
}
// Determine the name of the resource.
next($tokens); // Eat a space.
$token = next($tokens);
if ($token == '&') {
$token = next($tokens);
}
$resource_name = $token[1];
// Ensure that we never save it more than once.
if (isset($resources[$type][$resource_name])) {
return FALSE;
}
$resources[$type][$resource_name] = TRUE;
return $resource_name;
}
/**
* Skip the body of a code block, as defined by { and }.
*
* This function assumes that the body starts at the next instance
* of { from the current position.
*
* @param $tokens
*/
function _registry_skip_body(&$tokens) {
$num_braces = 1;
$token = '';
// Get to the first open brace.
while ($token != '{' && ($token = next($tokens)));
// Scan through the rest of the tokens until we reach the matching
// end brace.
while ($num_braces && ($token = next($tokens))) {
if ($token == '{') {
++$num_braces;
}
elseif ($token == '}') {
--$num_braces;
}
}
}
/**
* @} End of "defgroup registry".
*/
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