/[drupal]/contributions/modules/handler/handler.module
ViewVC logotype

Contents of /contributions/modules/handler/handler.module

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


Revision 1.14 - (show annotations) (download) (as text)
Sat Nov 8 08:58:31 2008 UTC (12 months, 2 weeks ago) by crell
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +63 -6 lines
File MIME type: text/x-php
A few more changes.
1 <?php
2 // $Id: handler.module,v 1.13 2008/10/27 00:45:37 crell Exp $
3
4 /**
5 * @file
6 * The handler module core routines.
7 *
8 * The handler module provides a mechanism for modules to create and register
9 * handler objects. Other modules may then request the appropriate handler
10 * for a given context, which will be returned as an object that may be invoked.
11 */
12
13 /**
14 * Include our additional libraries.
15 *
16 * These must be included before any hooks run, as some init hooks may depend
17 * on them.
18 */
19 require_once(drupal_get_path('module', 'handler') .'/handler.environment.inc');
20 require_once(drupal_get_path('module', 'handler') .'/handler.classes.inc');
21
22 /**
23 * Implementation of hook_init().
24 */
25 function handler_init() {
26 handler_rebuild();
27 }
28
29 /**
30 * Implementation of hook_flush_caches().
31 */
32 function handler_flush_caches() {
33 handler_rebuild();
34 }
35
36 /**
37 * Rebuild the handler and slot registries.
38 */
39 function handler_rebuild() {
40 $targets = handler_slot_build();
41 $handlers = handler_handler_build();
42 handler_lookup_build();
43 }
44
45 /**
46 * Rebuild the lookup table used for handler mapping.
47 *
48 */
49 function handler_lookup_build() {
50
51 $target_order = variable_get('handler_slot_targets', array());
52
53 // Now go through all the saved attachment directives and build out the lookup table.
54 $result = db_query("SELECT slot_id, handler_id, targets FROM {handler_attachments}");
55
56 db_query("DELETE FROM {handler_lookup}");
57
58 while ($record = db_fetch_object($result)) {
59 $record->targets = unserialize($record->targets);
60 $fields = array(
61 'slot' => $record->slot_id,
62 'handler' => $record->handler_id,
63 'specificity' => count($record->targets),
64 );
65 foreach ($record->targets as $target => $value) {
66 // The target order array tracks the mapping of target key to db field.
67 $fields[$target_order[$record->slot_id][$target]] = $value;
68 }
69
70 // Build the query. I want the D7 query builder! :-(
71 $query_fields = implode(',', array_keys($fields));
72 $placeholders = implode(',', array_fill(0, count($fields), "'%s'"));
73 $values = array();
74 foreach ($fields as $value) {
75 $values[] = "'{$value}'";
76 }
77
78 // Insert this lookup record into the table.
79 db_query("INSERT INTO {handler_lookup} ({$query_fields}) VALUES ({$placeholders})", $values);
80 }
81 }
82
83 /**
84 * Rebuild the handler info registry.
85 *
86 * @return
87 * The array of handlers just defined.
88 */
89 function handler_handler_build() {
90
91 $handlers = module_invoke_all('handler_info');
92
93 // Set default values, to avoid NULL issues if nothing else.
94 foreach ($handlers as $slot => $handler_info) {
95 foreach ($handler_info as $handler => $info) {
96 $handlers[$slot][$handler] += handler_handler_defaults();
97 }
98 }
99
100 // Let other modules alter the handler registry if needed.
101 drupal_alter('handler_info', $handlers);
102
103 // Don't do the deletion until after we've gotten the new data. That reduces
104 // the potential race condition window.
105 db_query('DELETE FROM {handler_info}');
106
107 // Build the handler data.
108 foreach ($handlers as $slot => $handler_info) {
109 foreach ($handler_info as $handler => $info) {
110 db_query("INSERT INTO {handler_info} (handler, slot, title, class, description)
111 VALUES ('%s', '%s', '%s', '%s', '%s')",
112 array($handler, $slot, $info['title'], $info['class'], $info['description'])
113 );
114 }
115 }
116
117 return $handlers ;
118 }
119
120
121 /**
122 * Rebuild the handler slot info registry.
123 *
124 * @return
125 * The array of slots just declared.
126 */
127 function handler_slot_build() {
128
129 $slots = module_invoke_all('handler_slot_info');
130
131 // Set default values, to avoid NULL issues if nothing else.
132 foreach ($slots as $slot => $info) {
133 $slots[$slot] += handler_slot_defaults();
134 }
135
136 // Let other modules alter the handler target registry if needed.
137 drupal_alter('handler_slot_info', $slots);
138
139 // Don't do the deletion until after we've gotten the new data. That reduces
140 // the potential race condition window.
141 db_query('DELETE FROM {handler_slot_info}');
142
143 $target_order = array();
144
145 // Build the target data.
146 foreach ($slots as $slot => $info) {
147 db_query("INSERT INTO {handler_slot_info} (slot, title, interface, factory, default_handler, targets, description)
148 VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s')",
149 array($slot, $info['title'], $info['interface'], $info['factory'], $info['default_handler'], serialize($info['targets']), $info['description'])
150 );
151
152 // Determine the order of all possible targets, as this will be the order
153 // used in the database.
154 $targets = array_keys($info['targets']);
155 ksort($targets);
156 $i = 1;
157 foreach ($targets as $target) {
158 $target_order[$slot][$target] = 't'. $i++;
159 }
160 }
161
162 variable_set('handler_slot_targets', $target_order);
163
164 return $slots;
165 }
166
167 /**
168 * Define various default values for handler slots.
169 *
170 * @return
171 * The default "empty" slot definition.
172 */
173 function handler_slot_defaults() {
174 return array(
175 'slot' => '',
176 'title' => '',
177 'interface' => 'HandlerInterface',
178 'factory' => 'handler_factory_generic',
179 'default_handler' => 'default',
180 'targets' => array(),
181 'description' => '',
182 );
183 }
184
185 /**
186 * Define various default values for handlers.
187 *
188 * @return
189 * The default "empty" handler definition.
190 */
191 function handler_handler_defaults() {
192 return array(
193 'handler' => '',
194 'slot' => '',
195 'title' => '',
196 'class' => '',
197 'description' => '',
198 );
199 }
200
201 /**
202 * Main handler entry-point.
203 *
204 * This function should be called in most instances in place of a factory
205 * function. It will automatically route the request to the appropriate
206 * factory.
207 *
208 * @param $slot_id
209 * The internal ID of the slot for which we want to retrieve a handler.
210 * @param $options
211 * The options array determines which handler object should be used, depending
212 * on the current configuration.
213 * @return
214 * The handler object required.
215 */
216 function handler($slot_id, $options = array()) {
217 $function = handler_get_factory($slot_id);
218 if (function_exists($function)) {
219 return $function($options, $slot_id);
220 }
221
222 return NULL;
223 }
224
225 /**
226 * Returns the factory function for a given target.
227 *
228 * If no factory is defined or the function does not exist, a generic
229 * factory is returned instead.
230 *
231 * @param $slot_id
232 * The internal ID of the target.
233 * @return
234 * The name of the factory function as a string.
235 */
236 function handler_get_factory($slot_id) {
237
238 $slot = handler_slot_load($slot_id);
239
240 return ($slot->factory && function_exists($slot->factory)) ? $slot->factory : 'handler_factory_generic';
241 }
242
243 /**
244 * Load function for slot info objects.
245 *
246 * @param $slot_id
247 * The internal slot ID.
248 * @param $refresh
249 * If this is set to TRUE, the internal slot cache will be flushed.
250 * @return
251 * The slot object or NULL if it is not defined.
252 */
253 function handler_slot_load($slot_id, $refresh = FALSE) {
254 static $slots;
255
256 if ($refresh) {
257 $slots = array();
258 }
259
260 if (empty($slots[$slot_id])) {
261 $slot = db_fetch_object(db_query("SELECT slot, title, interface, factory, default_handler, targets, description FROM {handler_slot_info} WHERE slot='%s'", array($slot_id)));
262 $slot->targets = unserialize($slot->targets);
263 $slots[$slot_id] = $slot;
264 }
265
266 return $slots[$slot_id];
267 }
268
269 /**
270 * Load function for handler info objects.
271 *
272 * @param $handler_id
273 * The internal handler ID.
274 * @param $refresh
275 * If this is set to TRUE, the internal handler cache will be flushed.
276 * @return
277 * The handler object or NULL if not defined.
278 */
279 function handler_load($handler_id, $refresh = FALSE) {
280 static $handlers;
281
282 if ($refresh) {
283 $handlers = array();
284 }
285
286 if (empty($handlers[$handler_id])) {
287 $handler = db_fetch_object(db_query("SELECT handler, slot, title, class, description FROM {handler_info} WHERE handler='%s'", array($handler_id)));
288 $handlers[$handler_id] = $handler;
289 }
290
291
292 return $handlers[$handler_id];
293 }
294
295 /**
296 * The generic handler factory.
297 *
298 * In most cases, this factory will be sufficient for handlers that do not
299 * need a new object generated on each call.
300 *
301 * @param $options
302 * The options array determines which handler object should be used, depending
303 * on the current configuration.
304 * @param $slot_id
305 * The slot we are handling.
306 * @return
307 * The handler object required.
308 */
309 function handler_factory_generic(Array $options = array(), $slot_id) {
310
311 static $handlers;
312
313 static $env;
314
315 // We only need one environment variable in the default case.
316 if (empty($env)) {
317 $env = new EnvironmentDefault();
318 }
319
320 $class = handler_get_registered_handler($slot_id, $options);
321
322 // If we haven't already created this handler, instantiate a new object for it.
323 if (empty($handlers[$class])) {
324 $handler = new $class();
325 $handler->setEnvironment($env);
326
327 $handlers[$class] = $handler;
328 }
329
330 return $handlers[$class];
331 }
332
333 /**
334 * Derive the handler class for the specified target.
335 *
336 * @param $target_id
337 * The internal ID of the target.
338 * @param $targets
339 * The array of targets to help route this handler request. If it does not
340 * have a value specified for every target, it will be filled in with the
341 * slot-defined defaults.
342 * @return
343 * The name of the class that should be loaded for this
344 * target/option configuration.
345 */
346 function handler_get_registered_handler($slot_id, $targets) {
347
348 $mapping = handler_slot_mapping($slot_id);
349
350 $targets += handler_default_targets($slot_id);
351
352 // Because we need to traverse down the options array somehow, we normalize
353 // the array order to alphabetic by the option key. That allows us to walk
354 // the array in a standardized order.
355 ksort($options);
356 $handler_id = &$mapping;
357 foreach ($options as $option => $value) {
358 if (isset($handler_id[$value])) {
359 $handler_id = &$handler_id[$value];
360 }
361 else {
362 $slot = handler_target_load($slot_id);
363 $handler_id = $slot->default_handler;
364 break;
365 }
366
367 }
368
369 $handler = handler_load($handler_id);
370
371 return $handler->class;
372 }
373
374 function handler_slot_mapping($slot_id) {
375
376 $mapping = db_result(db_query("SELECT mapping FROM {handler_mapping} WHERE target='%s'", array($target_id)));
377
378 if ($mapping) {
379 $mapping = unserialize($mapping);
380 }
381 else {
382 $mapping = array();
383 }
384
385 return $mapping;
386 }
387
388 /**
389 * Asociate a given handler to a give slot for the specified targets.
390 *
391 * @param $slot_id
392 * The internal ID of the slot.
393 * @param $handler_id
394 * The internal ID of the hand
395 * @param $options
396 * The array of options
397 */
398 function handler_attach($slot_id, $handler_id, $targets) {
399
400 db_query("INSERT INTO {handler_attachments} (slot_id, handler_id, targets) VALUES ('%s', '%s', '%s')", array(
401 $slot_id, $handler_id, serialize($targets)
402 ));
403
404 /*
405 $mapping = db_result(db_query("SELECT mapping FROM {handler_mapping} WHERE slot='%s'", array($slot_id)));
406
407 $is_new = FALSE;
408 if ($mapping) {
409 $mapping = unserialize($mapping);
410 }
411 else {
412 $mapping = array();
413 $is_new = TRUE;
414 }
415
416 $options += handler_default_options($slot_id);
417
418 ksort($options);
419 $map_point = &$mapping;
420 foreach ($options as $key => $value) {
421 if (empty($map_point[$value])) {
422 $map_point[$value] = array();
423 }
424 $map_point = &$map_point[$value];
425 }
426
427 $map_point = $handler_id;
428
429 // It would be nicer do a delete/insert cycle, but that would result in a race
430 // condition. What we really want here is a merge query, but those will have
431 // to wait until Drupal 7.
432 if ($is_new) {
433 db_query("INSERT INTO {handler_mapping} (slot, mapping) VALUES ('%s', '%s')", array($slot_id, serialize($mapping)));
434 }
435 else {
436 db_query("UPDATE {handler_mapping} SET mapping='%s' WHERE slot='%s'", array(serialize($mapping), $slot_id));
437 }
438 */
439 }
440
441 /**
442 * Get the default targets for a given slot.
443 *
444 * @param $slot_id
445 * The internal ID of the slot.
446 * @return
447 * An associative array of the default targets for the specified slot.
448 */
449 function handler_default_targets($slot_id) {
450 $slot = handler_slot_load($slot_id);
451
452 return $slot->targets;
453 }
454

  ViewVC Help
Powered by ViewVC 1.1.2