registry.inc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. /**
  3. * @file
  4. * This file contains the code registry parser engine.
  5. */
  6. /**
  7. * @defgroup registry Code registry
  8. * @{
  9. * The code registry engine.
  10. *
  11. * Drupal maintains an internal registry of all functions or classes in the
  12. * system, allowing it to lazy-load code files as needed (reducing the amount
  13. * of code that must be parsed on each request).
  14. */
  15. /**
  16. * Does the work for registry_update().
  17. */
  18. function _registry_update() {
  19. // The registry serves as a central autoloader for all classes, including
  20. // the database query builders. However, the registry rebuild process
  21. // requires write ability to the database, which means having access to the
  22. // query builders that require the registry in order to be loaded. That
  23. // causes a fatal race condition. Therefore we manually include the
  24. // appropriate query builders for the currently active database before the
  25. // registry rebuild process runs.
  26. $connection_info = Database::getConnectionInfo();
  27. $driver = $connection_info['default']['driver'];
  28. require_once DRUPAL_ROOT . '/includes/database/query.inc';
  29. require_once DRUPAL_ROOT . '/includes/database/select.inc';
  30. require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
  31. // Get current list of modules and their files.
  32. $modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
  33. // Get the list of files we are going to parse.
  34. $files = array();
  35. foreach ($modules as &$module) {
  36. $module->info = unserialize($module->info);
  37. $dir = dirname($module->filename);
  38. // Store the module directory for use in hook_registry_files_alter().
  39. $module->dir = $dir;
  40. if ($module->status) {
  41. // Add files for enabled modules to the registry.
  42. foreach ($module->info['files'] as $file) {
  43. $files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
  44. }
  45. }
  46. }
  47. foreach (file_scan_directory('includes', '/\.inc$/') as $filename => $file) {
  48. $files["$filename"] = array('module' => '', 'weight' => 0);
  49. }
  50. $transaction = db_transaction();
  51. try {
  52. // Allow modules to manually modify the list of files before the registry
  53. // parses them. The $modules array provides the .info file information, which
  54. // includes the list of files registered to each module. Any files in the
  55. // list can then be added to the list of files that the registry will parse,
  56. // or modify attributes of a file.
  57. drupal_alter('registry_files', $files, $modules);
  58. foreach (registry_get_parsed_files() as $filename => $file) {
  59. // Add the hash for those files we have already parsed.
  60. if (isset($files[$filename])) {
  61. $files[$filename]['hash'] = $file['hash'];
  62. }
  63. else {
  64. // Flush the registry of resources in files that are no longer on disc
  65. // or are in files that no installed modules require to be parsed.
  66. db_delete('registry')
  67. ->condition('filename', $filename)
  68. ->execute();
  69. db_delete('registry_file')
  70. ->condition('filename', $filename)
  71. ->execute();
  72. }
  73. }
  74. $parsed_files = _registry_parse_files($files);
  75. $unchanged_resources = array();
  76. $lookup_cache = array();
  77. if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
  78. $lookup_cache = $cache->data;
  79. }
  80. foreach ($lookup_cache as $key => $file) {
  81. // If the file for this cached resource is carried over unchanged from
  82. // the last registry build, then we can safely re-cache it.
  83. if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
  84. $unchanged_resources[$key] = $file;
  85. }
  86. }
  87. module_implements('', FALSE, TRUE);
  88. _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
  89. }
  90. catch (Exception $e) {
  91. $transaction->rollback();
  92. watchdog_exception('registry', $e);
  93. throw $e;
  94. }
  95. // We have some unchanged resources, warm up the cache - no need to pay
  96. // for looking them up again.
  97. if (count($unchanged_resources) > 0) {
  98. cache_set('lookup_cache', $unchanged_resources, 'cache_bootstrap');
  99. }
  100. }
  101. /**
  102. * Return the list of files in registry_file
  103. */
  104. function registry_get_parsed_files() {
  105. $files = array();
  106. // We want the result as a keyed array.
  107. $files = db_query("SELECT * FROM {registry_file}")->fetchAllAssoc('filename', PDO::FETCH_ASSOC);
  108. return $files;
  109. }
  110. /**
  111. * Parse all files that have changed since the registry was last built, and save their function and class listings.
  112. *
  113. * @param $files
  114. * The list of files to check and parse.
  115. */
  116. function _registry_parse_files($files) {
  117. $parsed_files = array();
  118. foreach ($files as $filename => $file) {
  119. if (file_exists($filename)) {
  120. $hash = hash_file('sha256', $filename);
  121. if (empty($file['hash']) || $file['hash'] != $hash) {
  122. $file['hash'] = $hash;
  123. $parsed_files[$filename] = $file;
  124. }
  125. }
  126. }
  127. foreach ($parsed_files as $filename => $file) {
  128. _registry_parse_file($filename, file_get_contents($filename), $file['module'], $file['weight']);
  129. db_merge('registry_file')
  130. ->key(array('filename' => $filename))
  131. ->fields(array(
  132. 'hash' => $file['hash'],
  133. ))
  134. ->execute();
  135. }
  136. return array_keys($parsed_files);
  137. }
  138. /**
  139. * Parse a file and save its function and class listings.
  140. *
  141. * @param $filename
  142. * Name of the file we are going to parse.
  143. * @param $contents
  144. * Contents of the file we are going to parse as a string.
  145. * @param $module
  146. * (optional) Name of the module this file belongs to.
  147. * @param $weight
  148. * (optional) Weight of the module.
  149. */
  150. function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
  151. if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
  152. foreach ($matches[2] as $key => $name) {
  153. db_merge('registry')
  154. ->key(array(
  155. 'name' => $name,
  156. 'type' => $matches[1][$key],
  157. ))
  158. ->fields(array(
  159. 'filename' => $filename,
  160. 'module' => $module,
  161. 'weight' => $weight,
  162. ))
  163. ->execute();
  164. }
  165. // Delete any resources for this file where the name is not in the list
  166. // we just merged in.
  167. db_delete('registry')
  168. ->condition('filename', $filename)
  169. ->condition('name', $matches[2], 'NOT IN')
  170. ->execute();
  171. }
  172. }
  173. /**
  174. * @} End of "defgroup registry".
  175. */