Code coverage for /20080809/includes/registry.inc

Line #Times calledCode
1
<?php
2
// $Id: registry.inc,v 1.2 2008/08/02 19:01:02 dries Exp $
3
4
/**
5
 * @file
6
 * This file contains the code registry parser engine.
7
 */
8
9
/**
10
 * @defgroup registry Code registry
11
 * @{
12
 * The code registry engine.
13
 *
14
 * Drupal maintains an internal registry of all functions or classes in
the
15
 * system, allowing it to lazy-load code files as needed (reducing the
amount
16
 * of code that must be parsed on each request). The list of included files
is
17
 * cached per menu callback for subsequent loading by the menu router. This
way,
18
 * a given page request will have all the code it needs but little else,
minimizing
19
 * time spent parsing unneeded code.
20
 */
21
22
/**
23
 * @see registry_rebuild.
24
 */
2565
function _registry_rebuild() {
26
  // Reset the resources cache.
2765
  _registry_get_resource_name();
28
  // Get the list of files we are going to parse.
2965
  $files = array();
3065
  foreach (module_rebuild_cache() as $module) {
3165
    if ($module->status) {
3265
      $dir = dirname($module->filename);
3365
      foreach ($module->info['files'] as $file) {
3465
        $files["./$dir/$file"] = array();
3565
      }
3665
    }
3765
  }
3865
  foreach (file_scan_directory('includes', '\.inc$') as $filename => $file)
{
3965
    $files["./$filename"] = array();
4065
  }
41
4265
  foreach (registry_get_parsed_files() as $filename => $file) {
43
    // Add the md5 to those files we've already parsed.
443
    if (isset($files[$filename])) {
453
      $files[$filename]['md5'] = $file['md5'];
463
    }
47
    else {
48
      // Flush the registry of resources in files that are no longer on
disc
49
      // or don't belong to installed modules.
501
      db_query("DELETE FROM {registry} WHERE filename = '%s'", $filename);
511
      db_query("DELETE FROM {registry_file} WHERE filename = '%s'",
$filename);
52
    }
533
  }
5465
  _registry_parse_files($files);
55
5665
  cache_clear_all('*', 'cache_registry');
5765
}
58
59
/**
60
 * Return the list of files in registry_file
61
 */
6265
function registry_get_parsed_files() {
6365
  $files = array();
6465
  $res = db_query("SELECT * FROM {registry_file}");
6565
  while ($file = db_fetch_array($res)) {
663
    $files[$file['filename']] = $file;
673
  }
6865
  return $files;
690
}
70
71
/**
72
 * Parse all files that have changed since the registry was last built, and
save their function and class listings.
73
 *
74
 * @param $files
75
 *  The list of files to check and parse.
76
 */
7765
function _registry_parse_files($files) {
7865
  $changed_files = array();
7965
  foreach ($files as $filename => $file) {
8065
    $contents = file_get_contents($filename);
8165
    $md5 = md5($contents);
8265
    $new_file = !isset($file['md5']);
8365
    if ($new_file || $md5 != $file['md5']) {
84
      // We update the md5 after we've saved the files resources rather
than here, so if we
85
      // don't make it through this rebuild, the next run will reparse the
file.
8664
      _registry_parse_file($filename, $contents);
8764
      $file['md5'] = $md5;
8864
      if ($new_file) {
8964
        db_query("INSERT INTO {registry_file} (md5, filename) VALUES ('%s',
'%s')", $md5, $filename);
9064
      }
91
      else {
920
        db_query("UPDATE {registry_file} SET md5 = '%s' WHERE filename =
'%s'", $md5, $filename);
93
      }
9464
    }
9565
  }
9665
}
97
98
/**
99
 * Parse a file and save its function and class listings.
100
 *
101
 * @param $filename
102
 *  Name of the file we are going to parse.
103
 * @param $contents
104
 *  Contents of the file we are going to parse as a string.
105
 */
10665
function _registry_parse_file($filename, $contents) {
10764
  static $map = array(T_FUNCTION => 'function', T_CLASS => 'class',
T_INTERFACE => 'interface');
108
  // Delete registry entries for this file, so we can insert the new
resources.
10964
  db_query("DELETE FROM {registry} WHERE filename = '%s'", $filename);
11064
  $tokens = token_get_all($contents);
11164
  while ($token = next($tokens)) {
112
    // Ignore all tokens except for those we are specifically saving.
11364
    if (is_array($token) && isset($map[$token[0]])) {
11464
      $type = $map[$token[0]];
11564
      if ($resource_name = _registry_get_resource_name($tokens, $type)) {
11664
        db_query("INSERT INTO {registry} (name, type, filename) VALUES
('%s', '%s', '%s')", $resource_name, $type, $filename);
117
        // We skip the body because classes may contain functions.
11864
        _registry_skip_body($tokens);
11964
      }
12064
    }
12164
  }
12264
}
123
124
/**
125
 * Derive the name of the next resource in the token stream.
126
 *
127
 * When called without arguments, it resets its static cache.
128
 *
129
 * @param $tokens
130
 *  The collection of tokens for the current file being parsed.
131
 * @param $type
132
 *  The human-readable token name, either: "function", "class", or
"interface".
133
 * @return
134
 *  The name of the resource, or FALSE if the resource has already been
processed.
135
 */
13665
function _registry_get_resource_name(&$tokens = NULL, $type = NULL) {
137
  // Keep a running list of all resources we've saved so far, so that we
never
138
  // save one more than once.
13965
  static $resources;
140
14165
  if (!isset($tokens)) {
14265
    $resources = array();
14365
    return;
1440
  }
145
  // Determine the name of the resource.
14664
  next($tokens); // Eat a space.
14764
  $token = next($tokens);
14864
  if ($token == '&') {
14962
    $token = next($tokens);
15062
  }
15164
  $resource_name = $token[1];
152
153
  // Ensure that we never save it more than once.
15464
  if (isset($resources[$type][$resource_name])) {
15562
    return FALSE;
1560
  }
15764
  $resources[$type][$resource_name] = TRUE;
158
15964
  return $resource_name;
1600
}
161
162
/**
163
 * Skip the body of a code block, as defined by { and }.
164
 *
165
 * This function assumes that the body starts at the next instance
166
 * of { from the current position.
167
 *
168
 * @param $tokens
169
 */
17065
function _registry_skip_body(&$tokens) {
17164
  $num_braces = 1;
172
17364
  $token = '';
174
  // Get to the first open brace.
17564
  while ($token != '{' && ($token = next($tokens)));
176
177
  // Scan through the rest of the tokens until we reach the matching
178
  // end brace.
17964
  while ($num_braces && ($token = next($tokens))) {
18064
    if ($token == '{') {
18164
      ++$num_braces;
18264
    }
18364
    elseif ($token == '}') {
18464
      --$num_braces;
18564
    }
18664
  }
18764
}
188
189
/**
190
 * @} End of "defgroup registry".
191
 */
192
19365