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;+ }+ }