DrupalBin
Submit Code
About
Recent Posts
How do i make this work in D6 (I have to clear the cache everytime to see the tabs)
53 min 20 sec
ago
Code
1 hour 48 min
ago
Code
5 hours 47 min
ago
oopsie in modules/taxonomy/taxonomy.test
7 hours 45 min
ago
more
Tags
CCK
drupal
fapi
jquery
menu
module
Panels
php
simpletest
test
theme
views
more tags
User login
Log in using OpenID:
What is OpenID?
Username:
*
Password:
*
Create new account
Request new password
Log in using OpenID
Cancel OpenID login
Home
Fix for Registry and testable refactoring
View
Download
Fix
This fix will not be saved to the database until you submit.
Summary:
Tags:
Any tags you'd like to associate with your code, delimitered by commas (example: Views, CCK, Module, etc).
Source code:
*
Before: +function drupal_rebuild_code_registry($check = FALSE) { + static $running; + if ($check) { + return $running; + } + $running = TRUE; + // Flush the old registry. + db_query("DELETE FROM {registry}"); + // We can't use module_invoke_all here because it depends on the registry + // which is currently being rebuilt. + $list = module_list(TRUE, FALSE, FALSE); + $patterns = array(); + foreach ($list as $module) { + $function = $module .'_hooks'; + if (function_exists($function)) { + $result = (array)$function(); + foreach ($result as $pattern) { + // For example 'form__alter'. + $patterns[] = '/'. str_replace('__', '_.*_', $pattern) .'/'; + } + } + } + + foreach ($list as $module) { + _registry_parse_directory(drupal_get_path('module', $module), $patterns); + } + + _registry_parse_directory('includes', $patterns); + $implementations = _registry_save_resource(); + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); +} + +/** + * Parse all loadable files in a directory and save their function listings. + */ +function _registry_parse_directory($path, $patterns) { + static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'); + $files = file_scan_directory($path, '\.(inc|module|install)$'); + foreach ($files as $filename => $file) { + $tokens = token_get_all(file_get_contents($filename)); + while ($token = next($tokens)) { + if (is_array($token) && isset($map[$token[0]])) { + $result = _registry_save_resource($token, $tokens, $map[$token[0]], $filename, $patterns); + // If this is a disabled module then we skip the whole file. + if ($result == -1) { + continue 2; + } + // We skip the body because classes might contain functions. + _registry_skip_body($tokens); + } + } + } +} + +/** + * Save a resource into the database. + * + * @param mixed $token + * @param array $tokens + * @param string $type + * @param string $module_path + * @param string $filename + */ +function _registry_save_resource($token = NULL, &$tokens = NULL, $type = NULL, $filename = NULL, $patterns = NULL) { + static $implementations, $resources, $dirs, $node_functions; + if (!isset($token)) { + return $implementations; + } + if (empty($node_functions)) { + $types = node_get_types(); + foreach ($types as $node_type) { + $module = $node_type->module == 'node' ? 'node_content' : $node_type->module; + foreach (array('load', 'validate', 'insert', 'update', 'delete', 'view', 'prepare', 'form') as $hook) { + $node_functions[$module .'_'. $hook] = $module; + } + } + } + next($tokens); // Eat a space. + $token = next($tokens); + if ($token == '&') { + $token = next($tokens); + } + $resource_name = $token[1]; + if (isset($resources[$type][$resource_name])) { + return; + } + $resources[$type][$resource_name] = TRUE; + $file_parts = explode('.', $filename); + $module = ''; + $hook = ''; + $count = count($file_parts); + if (isset($node_functions[$resource_name]) && $type == 'function') { + $module = $node_functions[$resource_name]; + } + else { + if ($count == 2 && $file_parts[1] == 'inc') { + if (!isset($dirs[$filename])) { + $dir_parts = explode('/', $file_parts[0]); + array_pop($dir_parts); + $dirs[$filename] = array_pop($dir_parts); + } + $module = $dirs[$filename]; + } + if (($count == 2 && ($file_parts[1] == 'module' || $file_parts[1] == 'install')) || ($count == 3 && $file_parts[2] == 'inc')) { + $module = basename($file_parts[0]); + if ($module != 'includes' && !in_array($module, module_list())) { + // We indicate that this file needs to be skipped. + return -1; + } + } + } + if ($module && strpos($resource_name, $module) === 0) { + $hook = substr($resource_name, strlen($module) + 1); + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + $implementations[$hook][] = $module; + } + } + } + db_query("INSERT INTO {registry} (name, type, module, hook, file) VALUES ('%s', '%s', '%s', '%s', '%s')", array($resource_name, $type, $module, $hook, "./$filename")); +} + +/** + * 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 array $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; + } + } +} After: +/** + * @defgroup registry Code registry + * @{ + * The code registry engine. + * + * Drupal maintains an internal registry of all functions or classes in the + * system. That in turn allows Drupal to lazy-load code files selectively + * as needed, reducing the amount of code that needs to be parsed on each + * request. The list of files included is then cached per menu callback + * so that they can be loaded by the menu router. That way, a given page + * request will have all the code it needs and little else, minimizing the + * time wasted parsing unneeded code. + */ + +/** + * Rescan all enabled modules and rebuild the registry. + * + * This function rescans all code in modules or the includes directory and + * stores a mapping of function, file, and hook implementation to the database. + * + * @param $check + * If TRUE, return whether or not a rebuild is currently in progress. That is + * needed so that this process can call module_implements(), which in turn + * needs to bypass the registry if the registry is still in the process of + * being rebuilt. + * @return + * If $checked is TRUE, returns TRUE if the registry is in the process of + * being rebuilt and FALSE otherwise. If $checked is FALSE, this function + * returns nothing. + */ +function drupal_rebuild_code_registry($check = FALSE) { + + // Simple recursion blocking. See DocBlock above. + static $running; + if ($check) { + return $running; + } + $running = TRUE; + // Flush the old registry. + db_query("DELETE FROM {registry}"); + // We can't use module_invoke_all here because it depends on the registry + // which is currently being rebuilt. + $list = module_list(TRUE, FALSE, FALSE); + $patterns = array(); + foreach ($list as $module) { + $function = $module .'_hooks'; + if (function_exists($function)) { + $result = (array)$function(); + foreach ($result as $pattern) { + // For example 'form__alter'. + $patterns[] = '/'. str_replace('__', '_.*_', $pattern) .'/'; + } + } + } + + foreach ($list as $module) { + _registry_parse_directory(drupal_get_path('module', $module), $patterns); + } + + _registry_parse_directory('includes', $patterns); + $implementations = _registry_hook_implementations(); + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); + + // Reset our recursion blocker. + $running = FALSE; +} + +/** + * Parse all loadable files in a directory and save their function and class listings. + * + * @param $path + * The path relative to Drupal root to scan. + * @param $patterns + * The function pattern to identify as a hook. That allows us to record + * what hook implementations exist and in what module/file. + */ +function _registry_parse_directory($path, $patterns) { + static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'); + + $active_modules = module_list(); + + $files = file_scan_directory($path, '\.(inc|module|install)$'); + foreach ($files as $filename => $file) { + $tokens = token_get_all(file_get_contents($filename)); + while ($token = next($tokens)) { + // Ignore all tokens except for those we are specifically saving. + if (is_array($token) && isset($map[$token[0]])) { + if ($resource_name = _registry_get_resource_name($tokens, $map[$token[0]]) ) { + $module = _registry_get_resource_module($resource_name, $filename); + if ($module != 'includes' && !in_array($module, $active_modules)) { + // If this is a disabled module then we skip the whole file. + continue 2; + } + + // Now save the resource record to the database. + $result = _registry_save_resource($resource_name, $map[$token[0]], $module, $hook, $filename); + // We skip the body because classes might contain functions. + _registry_skip_body($tokens); + } + } + } + } +} + +/** + * Derive the name of the next resource in the token stream. + * + * @param array $tokens + * The collection of tokens for the current file being parsed. + * @param string $type + * The human-readable token name: One of "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, $type) { + // Keep a running list of all resources we've saved so far, so that we never + // save one more than once. + static $resources; + + // 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; +} + +/** + * Determine the module that the given resource beongs to. + * + * In the case of node "hooks", the module is determined by calculating all + * possible node hooks and the module they correspond to. Otherwise, the module + * is derived from the file name or directory name of the file. + * + * Detectable files follow one of the following patterns: + * - <module>.module + * - <module>.install + * - <module>.<some-arbitrary-string>.inc + * - <module>/<some-arbitrary-string>.inc + * + * Note that the last option will treat any code in Drupal's core "includes" + * directory as belonging to the module "includes". That is by design. + * + * In order for a module to provide a hook on behalf of another module, the + * name of the file the implementation exists in must match the module the hook + * applies to, not the providing module. That is, if module foo is providing + * the implementation of hook_example() on behalf of module bar, then the function + * must reside in foo/bar.something.inc for it to associate with module bar + * correctly. + * + * @param string $resource_name + * The name of the resource; the function or class name. + * @param string $filename + * The name of the file in which the resource resides, relative to Drupal root. + * @return + * The name of the module that "owns" the resource. + */ +function _registry_get_resource_module($resource_name, $filename) { + static $dirs, $node_functions; + + // Node "hooks" aren't "real hooks", but still get called indirectly. Therefore, + // we build up a list of all possible node hooks for the current node types + // that we can match against later. + if (empty($node_functions)) { + $types = node_get_types(); + foreach ($types as $node_type) { + $module = $node_type->module == 'node' ? 'node_content' : $node_type->module; + foreach (array('load', 'validate', 'insert', 'update', 'delete', 'view', 'prepare', 'form') as $hook) { + $node_functions[$module .'_'. $hook] = $module; + } + } + } + + // Extract the module from the file name or directory name. + $file_parts = explode('.', $filename); + $module = ''; + $hook = ''; + $count = count($file_parts); + if (isset($node_functions[$resource_name]) && $type == 'function') { + $module = $node_functions[$resource_name]; + } + else { + if ($count == 2 && $file_parts[1] == 'inc') { + if (!isset($dirs[$filename])) { + $dir_parts = explode('/', $file_parts[0]); + array_pop($dir_parts); + $dirs[$filename] = array_pop($dir_parts); + } + $module = $dirs[$filename]; + } + if (($count == 2 && ($file_parts[1] == 'module' || $file_parts[1] == 'install')) || ($count == 3 && $file_parts[2] == 'inc')) { + $module = basename($file_parts[0]); + } + } + + return $module; +} + +/** + * Determine what hook this resource could bean implementation for. + * + * We record the hook for a given function so that we can easily request + * in a single query all hook implementations for a given hook. In most + * cases, that is a simple mapping. However, Drupal also supports dynamic + * hook names, such as hook_form_$form_id_alter. For those, we use + * module-provided patterns from hook_hooks() to determine the dynamic hook + * this resource correspond to. + * + * Note that this mechanism will generate false-positives for functions that + * are not actually hook implementations. That is OK, because they will never + * be queried by hook anyway. + * + * @param string $resource_name + * The name of the resource; the function or class name. + * @param string $module + * The name of the module that provides the resource. + * @param array $patterns + * A list of patterns of dynamic hooks to match against. + * @return + * The hook for which this resource is a potential implementation. + */ +function _registry_get_resource_hook($resource_name, $module, $patterns) { + if ($module && strpos($resource_name, $module) === 0) { + $hook = substr($resource_name, strlen($module) + 1); + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + // If it's a dynamic hook, queue up that information to cache later. + _registry_hook_implementations($hook, $module); + } + } + } + return $hook; +} + +/** + * Save a resource into the database. + * + * @param string $resource_name + * The name of the resource; the function or class name. + * @param string $type + * The human-readable token name: One of "function", "class", or "interface". + * @param string $module + * The name of the module that provides the resource. + * @param string $hook + * The hook this resource is an implementation for. + * @param string $filename + * The name of the file in which the resource resides, relative to Drupal root. + */ +function _registry_save_resource($resource_name, $type, $module, $hook, $filename) { + db_query("INSERT INTO {registry} (name, type, module, hook, file) VALUES ('%s', '%s', '%s', '%s', '%s')", array($resource_name, $type, $module, $hook, "./$filename")); +} + +/** + * Store the hook/module implementation information that we need to cache. + */ +function _registry_hook_implementations($hook = NULL, $module = NULL) { + static $implementations; + + if (isset($hook)) { + $implementations[$hook][] = $module; + } + else { + return $implementations; + } +} + +/** + * 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 array $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; + } + }
Syntax highlighting mode:
ActionScript
ColdFusion
Diff
Drupal
Drupal 5
Drupal 6
HTML
Javascript
MySQL
PHP
Python
robots.txt
SQL
Text
Select the syntax highlighting mode to use.