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