/[drupal]/contributions/sandbox/crell/registry/registry.module
ViewVC logotype

Contents of /contributions/sandbox/crell/registry/registry.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Mon Jan 21 07:53:04 2008 UTC (22 months ago) by crell
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +1 -1 lines
File MIME type: text/x-php
- Call registry_check_interface() registry_check_interface(), rather than _class().  Duh.
1 <?php
2
3 function registry_init() {
4 spl_autoload_register('registry_check_class');
5 spl_autoload_register('registry_check_interface');
6 }
7
8 function registry_menu() {
9 $items = array();
10
11 $items['registry'] = array(
12 'title' => t('Registry'),
13 'page callback' => 'registry_page',
14 'access arguments' => array('access content'),
15 );
16
17 $items['registry/rebuild'] = array(
18 'title' => t('Registry rebuild'),
19 'page callback' => 'registry_rebuild_page',
20 'access arguments' => array('access content'),
21 );
22
23 return $items;
24 }
25
26 /**
27 * Utility page to rebuild the registry.
28 *
29 * This will not be included in the final system, but may be in devel.
30 */
31 function registry_rebuild_page() {
32 registry_rebuild();
33 drupal_goto();
34 }
35
36 /**
37 * Master index page of the registry, for testing purposes.
38 *
39 */
40 function registry_page() {
41
42 $output = '';
43 $rows = array();
44
45 // Load all "admin" files with only one query.
46 registry_check_grouping('admin');
47
48 // Load the block_add_block_form() function if it isn't
49 // already, wherever it is.
50 registry_check_function('block_add_block_form');
51 registry_check_function('block_add_block_form');
52
53 // Test that autoload works.
54 //$test = new update_xml_parser();
55
56 // Show the entire registry table, for testing purposes.
57 $result = db_query("SELECT * FROM {registry}");
58 while ($record = db_fetch_array($result)) {
59 $rows[] = $record;
60 }
61
62 // Convenience link to rebuild the registry, for testing.
63 $output .= l('Rebuild registry', 'registry/rebuild', array('query' => drupal_get_destination()));
64
65 $output .= theme('table', NULL, $rows);
66 $output .= theme('box', 'Memory', memory_get_usage());
67 $output .= theme('box', 'Total', memory_get_peak_usage());
68
69 return $output;
70 }
71
72 /**
73 * Rescan all installed modules and rebuild the registry.
74 */
75 function registry_rebuild() {
76
77 // Flush the old registry.
78 db_query("DELETE FROM {registry}");
79
80 // We want to strip out the base of each file name so that it's
81 // relative to Drupal root.
82 $base = dirname($_SERVER['SCRIPT_FILENAME']);
83 $start = strlen($base) + 1;
84
85 // First we need to load all of Drupal so that we have all functions and classes available to us.
86 // This sucks, but the alternative is string-parsing each file. Ick.
87 foreach (module_list() as $module) {
88 $module_path = drupal_get_path('module', $module);
89 $directory = new DirectoryFilterLoadable($module_path);
90 foreach ($directory as $file) {
91
92 $tokens = new ArrayIterator(token_get_all(file_get_contents($file->getPath() .'/'. $file->getFilename())));
93 foreach ($tokens as $token) {
94 if (!is_array($token)) {
95 continue;
96 }
97 if (T_FUNCTION == $token[0]) {
98 _registry_save_resource($token, $tokens, 'function', $module_path, $file->getFilename());
99 }
100 elseif (T_CLASS == $token[0]) {
101 _registry_save_resource($token, $tokens, 'class', $module_path, $file->getFilename());
102 _registry_skip_body($tokens);
103 }
104 elseif (T_INTERFACE == $token[0]) {
105 _registry_save_resource($token, $tokens, 'interface', $module_path, $file->getFilename());
106 _registry_skip_body($tokens);
107 }
108 }
109 }
110 }
111 }
112
113 /**
114 * Save a resource into the database.
115 *
116 * @param mixed $token
117 * @param ArrayIterator $tokens
118 * @param string $type
119 * @param string $module_path
120 * @param string $file_name
121 */
122 function _registry_save_resource($token, ArrayIterator $tokens, $type, $module_path, $file_name) {
123 $tokens->next(); // The next token is a space, which we just swallow.
124 $tokens->next(); // ArrayIterator's next() method doesn't return the next value, it just increments.
125 $token = $tokens->current();
126 $resource_name = $token[1];
127 $file = $module_path . '/'. $file_name;
128 $file_parts = explode('.', $file_name);
129 $grouping = $file_parts[1] == 'inc' ? '' : $file_parts[1];
130 db_query("INSERT INTO {registry} (name, type, grouping, file) VALUES ('%s', '%s', '%s', '%s')", array($resource_name, $type, $grouping, $file));
131 }
132
133 /**
134 * Skip the body of a code block, as defined by { and }.
135 *
136 * This function assumes that the body starts at the next instance
137 * of { from the current position.
138 *
139 * @param ArrayIterator $tokens
140 */
141 function _registry_skip_body(ArrayIterator $tokens) {
142 $num_braces = 0;
143
144 // Get to the first open brace.
145 while ($token = $tokens->current()) {
146 if ($token == '{') {
147 ++$num_braces;
148 break;
149 }
150 $tokens->next();
151 }
152
153 // Scan through the rest of the tokens until we reach the matching
154 // end brace.
155 while ($num_braces && $token = $tokens->current()) {
156 if ($token == '{') {
157 ++$num_braces;
158 }
159 elseif ($token == '}') {
160 ++$num_braces;
161 }
162 $tokens->next();
163 }
164 }
165
166
167 /**
168 * Confirm that a function is available.
169 *
170 * If the function is already available, this function does nothing.
171 * If it is not, it tries to load the file in which it lives. If
172 * the file is not available, it simply returns false. That way it
173 * can be used as a drop-in replacement for function_exists().
174 *
175 * @param string $function
176 * The name of the function to check or load.
177 * @return
178 * TRUE if the function is now available, FALSE otherwise.
179 */
180 function registry_check_function($function) {
181 if (!function_exists($function)) {
182 $file = db_result(db_query("SELECT file FROM {registry} WHERE name='%s' AND type='%s'", $function, 'function'));
183 if (!$file) {
184 return FALSE;
185 }
186 dsm(__FUNCTION__ . ": Including $file");
187 include_once($file);
188 }
189 return TRUE;
190 }
191
192 /**
193 * Confirm that a class is available.
194 *
195 * This function parallel's registry_check_function(), but will rarely
196 * be called directly. Instead, it will be registered as an spl_autoload()
197 * handler, and PHP will call it for us when necessary.
198 *
199 * @param string $class
200 * The name of the class to check or load.
201 * @return
202 * TRUE if the class is now available, FALSE otherwise.
203 */
204 function registry_check_class($class) {
205 $file = db_result(db_query("SELECT file FROM {registry} WHERE name='%s' AND type='%s'", $class, 'class'));
206 if (!$file) {
207 return FALSE;
208 }
209 dsm(__FUNCTION__ . ": Including $file");
210 include_once($file);
211 return TRUE;
212 }
213
214 /**
215 * Confirm that an interface is available.
216 *
217 * This function parallel's registry_check_function(), but will rarely
218 * be called directly. Instead, it will be registered as an spl_autoload()
219 * handler, and PHP will call it for us when necessary.
220 *
221 * @param string $interface
222 * The name of the interface to check or load.
223 * @return
224 * TRUE if the interface is now available, FALSE otherwise.
225 */
226 function registry_check_interface($interface) {
227 $file = db_result(db_query("SELECT file FROM {registry} WHERE name='%s' AND type='%s'", $interface, 'interface'));
228 if (!$file) {
229 return FALSE;
230 }
231 dsm(__FUNCTION__ . ": Including $file");
232 include_once($file);
233 return TRUE;
234 }
235
236 /**
237 * Load a set of parallel functions or classes.
238 *
239 * A grouping (I need a better name) allows us to load all of
240 * a given type of function/class at once. For example, if we're
241 * running the install or upgrade process it makes sense to hit
242 * the database once and load all $module.install.inc files in one
243 * go. That saves us a lot of database hits.
244 *
245 * @param string $grouping
246 * The grouping to load, such as "admin" or "install".
247 */
248 function registry_check_grouping($grouping) {
249 $result = db_query("SELECT DISTINCT (file) FROM {registry} WHERE grouping='%s'", $grouping);
250 while ($record = db_fetch_object($result)) {
251 dsm(__FUNCTION__ . ": Including $record->file");
252 include_once($record->file);
253 }
254 }
255
256 /**
257 * An iteratable list of "loadable" code files.
258 */
259 class DirectoryFilterLoadable extends FilterIterator {
260
261 /**
262 * Constructor for an interable list of flies.
263 *
264 * @param string $path
265 * The directory we want to iterate.
266 */
267 public function __construct($path) {
268 parent::__construct(new DirectoryIterator($path));
269 }
270
271 public function accept() {
272 $file = $this->getInnerIterator();
273 if ($file->isFile()) {
274 $extension = end(explode('.', $file->getFileName()));
275 if (in_array($extension, array('module', 'inc', 'install', 'schema'))) {
276 return TRUE;
277 }
278 }
279 return FALSE;
280 }
281 }
282
283 /**
284 * Sample dummy interface for testing.
285 */
286 interface Bar {
287 function baz();
288 }
289
290 /**
291 * Sample dummy class for testing.
292 */
293 class Foo implements Bar {
294 function baz() { }
295 }
296
297 /*
298 function registry_rebuild() {
299
300 // Flush the old registry.
301 db_query("DELETE FROM {registry}");
302
303 // We want to strip out the base of each file name so that it's
304 // relative to Drupal root.
305 $base = dirname($_SERVER['SCRIPT_FILENAME']);
306 $start = strlen($base) + 1;
307
308 // First we need to load all of Drupal so that we have all functions and classes available to us.
309 // This sucks, but the alternative is string-parsing each file. Ick.
310 foreach (module_list() as $module) {
311 $directory = new DirectoryFilterLoadable(drupal_get_path('module', $module));
312 foreach ($directory as $file) {
313 include_once($file->getPath() .'/'. $file->getFilename());
314 }
315 }
316
317 $functions = get_defined_functions();
318 foreach ($functions['user'] as $function) {
319 $func = new ReflectionFunction($function);
320 $file = substr($func->getFileName(), $start);
321 db_query("INSERT INTO {registry} (name, type, file) VALUES ('%s', '%s', '%s')", array($function, 'function', $file));
322 }
323
324 $classes = get_declared_classes();
325 foreach ($classes as $class) {
326 $cls = new ReflectionClass($class);
327 if (!$cls->isInternal()) {
328 $file = substr($cls->getFileName(), $start);
329 db_query("INSERT INTO {registry} (name, type, file) VALUES ('%s', '%s', '%s')", array($class, 'class', $file));
330 }
331 }
332
333 $interfaces = get_declared_interfaces();
334 foreach ($interfaces as $interface) {
335 $iface = new ReflectionClass($interface);
336 if (!$iface->isInternal()) {
337 $file = substr($iface->getFileName(), $start);
338 db_query("INSERT INTO {registry} (name, type, file) VALUES ('%s', '%s', '%s')", array($interface, 'interface', $file));
339 }
340 }
341 }
342 */

  ViewVC Help
Powered by ViewVC 1.1.2