Commit e63e8502 authored by webchick's avatar webchick

Of all the patches to accidentally commit without a message. :( Rolling back...

Of all the patches to accidentally commit without a message. :( Rolling back registry rip. Let's try that again.
parent 4ae238ea
......@@ -95,8 +95,7 @@ Drupal 7.0, xxxx-xx-xx (development version)
contributed modules.
- Added code registry:
* Using the registry, modules declare their includable files via their .info file,
allowing Drupal to lazy-load code as needed, resulting in significant performance
and memory improvements.
allowing Drupal to lazy-load classes and interfaces as needed.
- Theme system:
* Removed the Bluemarine, Chameleon and Pushbutton themes. These themes live
on as contributed themes (http://drupal.org/project/bluemarine,
......
......@@ -211,7 +211,7 @@ function ajax_form_callback() {
// Get the callback function from the clicked button.
$ajax = $form_state['clicked_button']['#ajax'];
$callback = $ajax['callback'];
if (function_exists($callback)) {
if (drupal_function_exists($callback)) {
$html = $callback($form, $form_state);
// If the returned value is a string, assume it is HTML and create
......
......@@ -654,7 +654,7 @@ function drupal_get_filename($type, $name, $filename = NULL) {
$mask = "/$name\.$type$/";
}
if (function_exists('drupal_system_listing')) {
if (drupal_function_exists('drupal_system_listing')) {
$matches = drupal_system_listing($mask, $dir, 'name', 0);
if (!empty($matches[$name]->uri)) {
$files[$type][$name] = $matches[$name]->uri;
......@@ -787,19 +787,6 @@ function drupal_page_is_cacheable($allow_caching = NULL) {
&& $_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI';
}
/**
* Call all init or exit hooks without including all modules.
*
* @param $hook
* The name of the bootstrap hook we wish to invoke.
*/
function bootstrap_invoke_all($hook) {
foreach (module_list(TRUE, TRUE) as $module) {
drupal_load('module', $module);
module_invoke($module, $hook);
}
}
/**
* Includes a file with the provided type and name. This prevents
* including a theme, engine, module, etc., more than once.
......@@ -1091,13 +1078,6 @@ function drupal_serve_page_from_cache(stdClass $cache) {
print $cache->data;
}
/**
* Define the critical hooks that force modules to always be loaded.
*/
function bootstrap_hooks() {
return array('boot', 'exit', 'watchdog');
}
/**
* Unserializes and appends elements from a serialized string.
*
......@@ -1489,14 +1469,15 @@ function _drupal_bootstrap($phase) {
// If the skipping of the bootstrap hooks is not enforced, call
// hook_boot.
if (variable_get('page_cache_invoke_hooks', TRUE)) {
bootstrap_invoke_all('boot');
require_once DRUPAL_ROOT . '/includes/module.inc';
module_invoke_all('boot');
}
header('X-Drupal-Cache: HIT');
drupal_serve_page_from_cache($cache);
// If the skipping of the bootstrap hooks is not enforced, call
// hook_exit.
if (variable_get('page_cache_invoke_hooks', TRUE)) {
bootstrap_invoke_all('exit');
module_invoke_all('exit');
}
// We are done.
exit;
......@@ -1523,9 +1504,6 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_VARIABLES:
// Load variables from the database, but do not overwrite variables set in settings.php.
$conf = variable_initialize(isset($conf) ? $conf : array());
// Load bootstrap modules.
require_once DRUPAL_ROOT . '/includes/module.inc';
module_load_all(TRUE);
break;
case DRUPAL_BOOTSTRAP_SESSION:
......@@ -1534,7 +1512,8 @@ function _drupal_bootstrap($phase) {
break;
case DRUPAL_BOOTSTRAP_PAGE_HEADER:
bootstrap_invoke_all('boot');
require_once DRUPAL_ROOT . '/includes/module.inc';
module_invoke_all('boot');
if (!$cache && drupal_page_is_cacheable()) {
header('X-Drupal-Cache: MISS');
}
......@@ -1772,7 +1751,7 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
// Load the .install files to get hook_schema.
// On some databases this function may be called before bootstrap has
// been completed, so we force the functions we need to load just in case.
if (function_exists('module_load_all_includes')) {
if (drupal_function_exists('module_load_all_includes')) {
// There is currently a bug in module_list() where it caches what it
// was last called with, which is not always what you want.
......@@ -1782,7 +1761,7 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
// "prime" module_list() here to to values we want, specifically
// "yes rebuild the list and don't limit to bootstrap".
// TODO: Remove this call after http://drupal.org/node/222109 is fixed.
module_list(TRUE, FALSE);
module_list(TRUE);
module_load_all_includes('install');
}
......@@ -1790,11 +1769,17 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
// Invoke hook_schema for all modules.
foreach (module_implements('schema') as $module) {
$current = module_invoke($module, 'schema');
_drupal_schema_initialize($module, $current);
if (drupal_function_exists('_drupal_schema_initialize')) {
_drupal_schema_initialize($module, $current);
}
$schema = array_merge($schema, $current);
}
drupal_alter('schema', $schema);
if (drupal_function_exists('drupal_alter')) {
drupal_alter('schema', $schema);
}
// If the schema is empty, avoid saving it: some database engines require
// the schema to perform queries, and this could lead to infinite loops.
if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
......@@ -1824,11 +1809,52 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
* @{
*/
/**
* Confirm that a function is available.
*
* If the function is already available, this function does nothing.
* If the function is not available, it tries to load the file where the
* function lives. If the file is not available, it returns false, so that it
* can be used as a drop-in replacement for function_exists().
*
* @param $function
* The name of the function to check or load.
* @return
* TRUE if the function is now available, FALSE otherwise.
*/
function drupal_function_exists($function) {
static $checked = array();
static $maintenance;
if (!isset($maintenance)) {
$maintenance = defined('MAINTENANCE_MODE');
}
if ($maintenance) {
return function_exists($function);
}
if (isset($checked[$function])) {
return $checked[$function];
}
$checked[$function] = FALSE;
if (function_exists($function)) {
$checked[$function] = TRUE;
return TRUE;
}
$checked[$function] = _registry_check_code('function', $function);
return $checked[$function];
}
/**
* Confirm 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.
* This function parallels drupal_function_exists(), but 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.
......@@ -1842,8 +1868,9 @@ function drupal_autoload_interface($interface) {
/**
* Confirm 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.
* This function parallels drupal_function_exists(), but 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.
......@@ -1933,8 +1960,8 @@ function _registry_check_code($type, $name = NULL) {
/**
* Rescan all enabled modules and rebuild the registry.
*
* Rescans all code in modules or includes directories, storing the location of
* each interface or class in the database.
* Rescans all code in modules or includes directory, storing a mapping of
* each function, file, and hook implementation in the database.
*/
function registry_rebuild() {
require_once DRUPAL_ROOT . '/includes/registry.inc';
......
......@@ -1219,7 +1219,7 @@ function t($string, array $args = array(), array $options = array()) {
$string = $custom_strings[$options['langcode']][$options['context']][$string];
}
// Translate with locale module if enabled.
// We don't use function_exists() here, because it breaks the testing
// We don't use drupal_function_exists() here, because it breaks the testing
// framework if the locale module is enabled in the parent site (we cannot
// unload functions in PHP).
elseif (function_exists('locale') && $options['langcode'] != 'en') {
......@@ -2319,6 +2319,7 @@ function drupal_page_footer() {
ob_flush();
}
module_implements(MODULE_IMPLEMENTS_WRITE_CACHE);
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
}
......@@ -2867,7 +2868,7 @@ function drupal_clear_css_cache() {
* - 'file': Path to the file relative to base_path().
* - 'inline': The JavaScript code that should be placed in the given scope.
* - 'external': The absolute path to an external JavaScript file that is not
* hosted on the local server. These files will not be aggregated if
* hosted on the local server. These files will not be aggregated if
* JavaScript aggregation is enabled.
* - 'setting': An array with configuration options as associative array. The
* array is directly placed in Drupal.settings. All modules should wrap
......@@ -3574,6 +3575,14 @@ function _drupal_bootstrap_full() {
set_error_handler('_drupal_error_handler');
set_exception_handler('_drupal_exception_handler');
if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) {
// Valid SimpleTest user-agent, log fatal errors to test specific file
// directory. The user-agent is validated in DRUPAL_BOOTSTRAP_DATABASE
// phase so as long as it is a SimpleTest user-agent it is valid.
ini_set('log_errors', 1);
ini_set('error_log', file_directory_path() . '/error.log');
}
// Emit the correct charset HTTP header.
drupal_set_header('Content-Type', 'text/html; charset=utf-8');
// Detect string handling method
......@@ -3584,14 +3593,6 @@ function _drupal_bootstrap_full() {
module_load_all();
// Make sure all stream wrappers are registered.
file_get_stream_wrappers();
if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) {
// Valid SimpleTest user-agent, log fatal errors to test specific file
// directory. The user-agent is validated in DRUPAL_BOOTSTRAP_DATABASE
// phase so as long as it is a SimpleTest user-agent it is valid.
ini_set('log_errors', 1);
ini_set('error_log', file_directory_path() . '/error.log');
}
// Let all modules take action before menu system handles the request
// We do not want this while running update.php.
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
......@@ -3766,7 +3767,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
$searchdir[] = "$config/$directory";
}
// If the database is not available, we can't use function_exists(), so
// If the database is not available, we can't use drupal_function_exists(), so
// we load the file_scan_directory function definition manually.
if (!function_exists('file_scan_directory')) {
require_once DRUPAL_ROOT . '/includes/file.inc';
......@@ -3956,7 +3957,7 @@ function drupal_render(&$elements) {
// element is rendered into the final text.
if (isset($elements['#pre_render'])) {
foreach ($elements['#pre_render'] as $function) {
if (function_exists($function)) {
if (drupal_function_exists($function)) {
$elements = $function($elements);
}
}
......@@ -3990,7 +3991,7 @@ function drupal_render(&$elements) {
// which allows the output'ed text to be filtered.
if (isset($elements['#post_render'])) {
foreach ($elements['#post_render'] as $function) {
if (function_exists($function)) {
if (drupal_function_exists($function)) {
$elements['#children'] = $function($elements['#children'], $elements);
}
}
......@@ -4119,7 +4120,6 @@ function element_info($type) {
if (!isset($cache)) {
$basic_defaults = element_basic_defaults();
$cache = array();
foreach (module_implements('elements') as $module) {
$elements = module_invoke($module, 'elements');
if (isset($elements) && is_array($elements)) {
......@@ -5055,32 +5055,3 @@ function drupal_check_incompatibility($v, $current_version) {
}
}
}
/**
* Performs one or more XML-RPC request(s).
*
* @param $url
* An absolute URL of the XML-RPC endpoint.
* Example:
* http://www.example.com/xmlrpc.php
* @param ...
* For one request:
* The method name followed by a variable number of arguments to the method.
* For multiple requests (system.multicall):
* An array of call arrays. Each call array follows the pattern of the single
* request: method name followed by the arguments to the method.
* @return
* For one request:
* Either the return value of the method on success, or FALSE.
* If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
* For multiple requests:
* An array of results. Each result will either be the result
* returned by the method called, or an xmlrpc_error object if the call
* failed. See xmlrpc_error().
*/
function xmlrpc($url) {
require_once DRUPAL_ROOT . '/includes/xmlrpc.inc';
$args = func_get_args();
return call_user_func_array('_xmlrpc', $args);
}
......@@ -320,7 +320,7 @@ function file_create_url($uri) {
return FALSE;
}
}
// @todo Implement CDN integration hook stuff in this function.
// @see http://drupal.org/node/499156
}
......@@ -1250,7 +1250,7 @@ function file_validate(&$file, $validators = array()) {
// Call the validation functions specified by this function's caller.
$errors = array();
foreach ($validators as $function => $args) {
if (function_exists($function)) {
if (drupal_function_exists($function)) {
array_unshift($args, $file);
$errors = array_merge($errors, call_user_func_array($function, $args));
}
......
......@@ -418,7 +418,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
// We first check to see if there's a function named after the $form_id.
// If there is, we simply pass the arguments on to it to get the form.
if (!function_exists($form_id)) {
if (!drupal_function_exists($form_id)) {
// In cases where many form_ids need to share a central constructor function,
// such as the node editing form, modules can implement hook_forms(). It
// maps one or more form_ids to the correct constructor functions.
......@@ -439,6 +439,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
}
if (isset($form_definition['callback'])) {
$callback = $form_definition['callback'];
drupal_function_exists($callback);
}
}
......@@ -612,13 +613,13 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
$form += array('#tree' => FALSE, '#parents' => array());
if (!isset($form['#validate'])) {
if (function_exists($form_id . '_validate')) {
if (drupal_function_exists($form_id . '_validate')) {
$form['#validate'] = array($form_id . '_validate');
}
}
if (!isset($form['#submit'])) {
if (function_exists($form_id . '_submit')) {
if (drupal_function_exists($form_id . '_submit')) {
// We set submit here so that it can be altered.
$form['#submit'] = array($form_id . '_submit');
}
......@@ -793,7 +794,7 @@ function _form_validate($elements, &$form_state, $form_id = NULL) {
// #value data.
elseif (isset($elements['#element_validate'])) {
foreach ($elements['#element_validate'] as $function) {
if (function_exists($function)) {
if (drupal_function_exists($function)) {
$function($elements, $form_state, $form_state['complete form']);
}
}
......@@ -830,7 +831,7 @@ function form_execute_handlers($type, &$form, &$form_state) {
}
foreach ($handlers as $function) {
if (function_exists($function)) {
if (drupal_function_exists($function)) {
// Check to see if a previous _submit handler has set a batch, but
// make sure we do not react to a batch that is already being processed
// (for instance if a batch operation performs a drupal_form_submit()).
......@@ -969,7 +970,7 @@ function form_builder($form_id, $element, &$form_state) {
// checkboxes and files.
if (isset($element['#process']) && !$element['#processed']) {
foreach ($element['#process'] as $process) {
if (function_exists($process)) {
if (drupal_function_exists($process)) {
$element = $process($element, $form_state, $form_state['complete form']);
}
}
......@@ -1096,7 +1097,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
// If we have input for the current element, assign it to the #value property.
if (!$form_state['programmed'] || isset($input)) {
// Call #type_value to set the form value;
if (function_exists($value_callback)) {
if (drupal_function_exists($value_callback)) {
$element['#value'] = $value_callback($element, $input, $form_state);
}
if (!isset($element['#value']) && isset($input)) {
......@@ -1111,7 +1112,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
// Load defaults.
if (!isset($element['#value'])) {
// Call #type_value without a second argument to request default_value handling.
if (function_exists($value_callback)) {
if (drupal_function_exists($value_callback)) {
$element['#value'] = $value_callback($element, FALSE, $form_state);
}
// Final catch. If we haven't set a value yet, use the explicit default value.
......
......@@ -65,7 +65,7 @@ function image_get_toolkit() {
if (!isset($toolkit)) {
$toolkits = image_get_available_toolkits();
$toolkit = variable_get('image_toolkit', 'gd');
if (!isset($toolkits[$toolkit]) || !function_exists('image_' . $toolkit . '_load')) {
if (!isset($toolkits[$toolkit]) || !drupal_function_exists('image_' . $toolkit . '_load')) {
// The selected toolkit isn't available so return the first one found. If
// none are available this will return FALSE.
reset($toolkits);
......@@ -90,7 +90,7 @@ function image_get_toolkit() {
*/
function image_toolkit_invoke($method, stdClass $image, array $params = array()) {
$function = 'image_' . $image->toolkit . '_' . $method;
if (function_exists($function)) {
if (drupal_function_exists($function)) {
array_unshift($params, $image);
return call_user_func_array($function, $params);
}
......
......@@ -632,16 +632,15 @@ function drupal_install_system() {
$system_versions = drupal_get_schema_versions('system');
$system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
db_insert('system')
->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap'))
->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version'))
->values(array(
'filename' => $system_path . '/system.module',
'name' => 'system',
'type' => 'module',
'owner' => '',
'status' => 1,
'schema_version' => $system_version,
'bootstrap' => 0,
))
'schema_version' => $system_version
))
->execute();
// Now that we've installed things properly, bootstrap the full Drupal environment
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
......
......@@ -115,7 +115,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
// Build the e-mail (get subject and body, allow additional headers) by
// invoking hook_mail() on this module. We cannot use module_invoke() as
// we need to have $message by reference in hook_mail().
if (function_exists($function = $module . '_mail')) {
if (drupal_function_exists($function = $module . '_mail')) {
$function($key, $message, $params);
}
......
......@@ -411,10 +411,9 @@ function menu_execute_active_handler($path = NULL) {
}
if ($router_item = menu_get_item($path)) {
if ($router_item['access']) {
if ($router_item['file']) {
require_once($router_item['file']);
if (drupal_function_exists($router_item['page_callback'])) {
return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
}
return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
}
else {
return MENU_ACCESS_DENIED;
......@@ -517,7 +516,7 @@ function _menu_check_access(&$item, $map) {
if ($callback == 'user_access') {
$item['access'] = (count($arguments) == 1) ? user_access($arguments[0]) : user_access($arguments[0], $arguments[1]);
}
elseif (function_exists($callback)) {
elseif (drupal_function_exists($callback)) {
$item['access'] = call_user_func_array($callback, $arguments);
}
}
......@@ -568,7 +567,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
$item['title'] = t($item['title'], menu_unserialize($item['title_arguments'], $map));
}
}
elseif ($callback && function_exists($callback)) {
elseif ($callback && drupal_function_exists($callback)) {
if (empty($item['title_arguments'])) {
$item['title'] = $callback($item['title']);
}
......@@ -1906,7 +1905,7 @@ function menu_cache_clear_all() {
function menu_rebuild() {
if (!lock_acquire('menu_rebuild')) {
// Wait for another request that is already doing this work.
// We choose to block here since otherwise the router item may not
// We choose to block here since otherwise the router item may not
// be available in menu_execute_active_handler() resulting in a 404.
lock_wait('menu_rebuild');
return FALSE;
......@@ -2607,12 +2606,12 @@ function _menu_router_build($callbacks) {
$load_functions[$k] = NULL;
}
else {
if (function_exists($matches[1] . '_to_arg')) {
if (drupal_function_exists($matches[1] . '_to_arg')) {
$to_arg_functions[$k] = $matches[1] . '_to_arg';
$load_functions[$k] = NULL;
$match = TRUE;
}
if (function_exists($matches[1] . '_load')) {
if (drupal_function_exists($matches[1] . '_load')) {
$function = $matches[1] . '_load';
// Create an array of arguments that will be passed to the _load
// function when this menu path is checked, if 'load arguments'
......@@ -2698,12 +2697,6 @@ function _menu_router_build($callbacks) {
if (!isset($item['page arguments']) && isset($parent['page arguments'])) {
$item['page arguments'] = $parent['page arguments'];
}
if (!isset($item['file']) && isset($parent['file'])) {
$item['file'] = $parent['file'];
}
if (!isset($item['file path']) && isset($parent['file path'])) {
$item['file path'] = $parent['file path'];
}
}
}
}
......@@ -2731,17 +2724,7 @@ function _menu_router_build($callbacks) {
'tab_parent' => '',
'tab_root' => $path,
'path' => $path,
'file' => '',
'file path' => '',
'include file' => '',
'module' => '',
);
// Calculate out the file to be included for each callback, if any.
if ($item['file']) {
$file_path = $item['file path'] ? $item['file path'] : drupal_get_path('module', $item['module']);
$item['include file'] = $file_path . '/' . $item['file'];
}
}
// Sort the masks so they are in order of descending fit.
......@@ -2780,7 +2763,6 @@ function _menu_router_save($menu, $masks) {
'description',
'position',
'weight',
'file',
));
foreach ($menu as $path => $item) {
......@@ -2805,7 +2787,6 @@ function _menu_router_save($menu, $masks) {
'description' => $item['description'],
'position' => $item['position'],
'weight' => $item['weight'],
'file' => $item['include file'],
));
}
// Execute insert object.
......
This diff is collapsed.
......@@ -13,7 +13,7 @@
*
* 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).
* of code that must be parsed on each request).
*/
/**
......@@ -34,6 +34,9 @@ function _registry_rebuild() {
require_once DRUPAL_ROOT . '/includes/database/select.inc';
require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
// Reset the resources cache.
_registry_get_resource_name();
// Get current list of modules and their files.
$modules = system_get_module_data();
// Get the list of files we are going to parse.
......@@ -93,9 +96,9 @@ function _registry_rebuild() {
$unchanged_resources[$key] = $file;
}
}
module_implements('', FALSE, TRUE);
_registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
module_implements(MODULE_IMPLEMENTS_CLEAR_CACHE);
cache_clear_all('*', 'cache_registry', TRUE);
// We have some unchanged resources, warm up the cache - no need to pay
......@@ -160,23 +163,123 @@ function _registry_parse_files($files) {
* (optional) Weight of the module.
*/
function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
static $map = array(T_CLASS => 'class', T_INTERFACE => 'interface');
$map = &drupal_static(__FUNCTION__, array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'));
// Delete registry entries for this file, so we can insert the new resources.
db_delete('registry')
->condition('filename', $filename)
->execute();
if (preg_match_all('/^\s*(?:abstract)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
$query = db_insert('registry')->fields(array('name', 'type', 'filename', 'module', 'weight'));
foreach ($matches[2] as $key => $name) {
$query->values(array(
'name' => $name,
'type' => $matches[1][$key],
'filename' => $filename,
'module' => $module,
'weight' => $weight,
));
$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)) {
$suffix = '';
// Collect the part of the function name after the module name,
// so that we can query the registry for possible hook implementations.
if ($type == 'function' && !empty($module)) {
$n = strlen($module);
if (substr($resource_name, 0, $n) == $module) {
$suffix = substr($resource_name, $n + 1);
}
}
$fields = array(
'filename' => $filename,
'module' => $module,
'suffix' => $suffix,
'weight' => $weight,
);
// Because some systems, such as cache, currently use duplicate function
// names in separate files an insert query cannot be used here as it
// would cause a key constraint violation. Instead we use a merge query.
// In practice this should not be an issue as those systems all initialize
// pre-registry and therefore are never loaded by the registry so it
// doesn't matter if those records in the registry table point to one
// filename instead of another.
// TODO: Convert this back to an insert query after all duplicate
// function names have been purged from Drupal.
db_merge('registry')
->key(array('name' => $resource_name, 'type' => $type))
->fields($fields)
->execute();
// 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.
$resources = &drupal_static(__FUNCTION__);
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)));