All pastes #961193 Raw Copy code Copy link Edit

Crell

public unlisted php v1 · immutable
#961193 ·published 2008-03-28 19:04 UTC
rendered paste body
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;+    }+  }