| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: plugins.inc,v 1.18.2.30 2010/08/25 20:22:22 merlinofchaos Exp $ 3 4 /** 5 * @file 6 * 7 * Contains routines to organize and load plugins. It allows a special 8 * variation of the hook system so that plugins can be kept in separate 9 * .inc files, and can be either loaded all at once or loaded only when 10 * necessary. 11 */ 12 13 /** 14 * Get an array of information about modules that support an API. 15 * 16 * This will ask each module if they support the given API, and if they do 17 * it will return an array of information about the modules that do. 18 * 19 * This function invokes hook_ctools_api. This invocation is statically 20 * cached, so feel free to call it as often per page run as you like, it 21 * will cost very little. 22 * 23 * This function can be used as an alternative to module_implements and can 24 * thus be used to find a precise list of modules that not only support 25 * a given hook (aka 'api') but also restrict to only modules that use 26 * the given version. This will allow multiple modules moving at different 27 * paces to still be able to work together and, in the event of a mismatch, 28 * either fall back to older behaviors or simply cease loading, which is 29 * still better than a crash. 30 * 31 * @param $owner 32 * The name of the module that controls the API. 33 * @param $api 34 * The name of the api. The api name forms the file name: 35 * $module.$api.inc 36 * @param $minimum_version 37 * The lowest version API that is compatible with this one. If a module 38 * reports its API as older than this, its files will not be loaded. This 39 * should never change during operation. 40 * @param $current_version 41 * The current version of the api. If a module reports its minimum API as 42 * higher than this, its files will not be loaded. This should never change 43 * during operation. 44 * 45 * @return 46 * An array of API information, keyed by module. Each module's information will 47 * contain: 48 * - 'version': The version of the API required by the module. The module 49 * should use the lowest number it can support so that the widest range 50 * of supported versions can be used. 51 * - 'path': If not provided, this will be the module's path. This is 52 * where the module will store any subsidiary files. This differs from 53 * plugin paths which are figured separately. 54 * 55 * APIs can request any other information to be placed here that they might 56 * need. This should be in the documentation for that particular API. 57 */ 58 function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version) { 59 $cache = &ctools_static(__FUNCTION__, array()); 60 if (!isset($cache[$owner][$api])) { 61 $cache[$owner][$api] = array(); 62 foreach (module_implements('ctools_plugin_api') as $module) { 63 $function = $module . '_ctools_plugin_api'; 64 $info = $function($owner, $api); 65 if (!isset($info['version'])) { 66 continue; 67 } 68 69 // Only process if version is between minimum and current, inclusive. 70 if ($info['version'] >= $minimum_version && $info['version'] <= $current_version) { 71 if (!isset($info['path'])) { 72 $info['path'] = drupal_get_path('module', $module); 73 } 74 $cache[$owner][$api][$module] = $info; 75 } 76 } 77 78 // And allow themes to implement these as well. 79 $themes = _ctools_list_themes(); 80 foreach ($themes as $name => $theme) { 81 if (!empty($theme->info['api'][$owner][$api])) { 82 $info = $theme->info['api'][$owner][$api]; 83 if (!isset($info['version'])) { 84 continue; 85 } 86 87 // Only process if version is between minimum and current, inclusive. 88 if ($info['version'] >= $minimum_version && $info['version'] <= $current_version) { 89 if (!isset($info['path'])) { 90 $info['path'] = ''; 91 } 92 // Because themes can't easily specify full path, we add it here 93 // even though we do not for modules: 94 $info['path'] = drupal_get_path('theme', $name) . '/' . $info['path']; 95 $cache[$owner][$api][$name] = $info; 96 } 97 } 98 } 99 } 100 101 return $cache[$owner][$api]; 102 } 103 104 /** 105 * Load a group of API files. 106 * 107 * This will ask each module if they support the given API, and if they do 108 * it will load the specified file name. The API and the file name 109 * coincide by design. 110 * 111 * @param $owner 112 * The name of the module that controls the API. 113 * @param $api 114 * The name of the api. The api name forms the file name: 115 * $module.$api.inc, though this can be overridden by the module's response. 116 * @param $minimum_version 117 * The lowest version API that is compatible with this one. If a module 118 * reports its API as older than this, its files will not be loaded. This 119 * should never change during operation. 120 * @param $current_version 121 * The current version of the api. If a module reports its minimum API as 122 * higher than this, its files will not be loaded. This should never change 123 * during operation. 124 * 125 * @return 126 * The API information, in case you need it. 127 */ 128 function ctools_plugin_api_include($owner, $api, $minimum_version, $current_version) { 129 static $already_done = array(); 130 131 $info = ctools_plugin_api_info($owner, $api, $minimum_version, $current_version); 132 if (!isset($already_done[$owner][$api])) { 133 foreach ($info as $module => $plugin_info) { 134 if (!isset($plugin_info['file'])) { 135 $plugin_info['file'] = "$module.$api.inc"; 136 } 137 if (file_exists("./$plugin_info[path]/$plugin_info[file]")) { 138 $plugin_info[$module]['included'] = TRUE; 139 require_once "./$plugin_info[path]/$plugin_info[file]"; 140 } 141 $info[$module] = $plugin_info; 142 } 143 $already_done[$owner][$api] = TRUE; 144 } 145 146 return $info; 147 } 148 149 /** 150 * Fetch a group of plugins by name. 151 * 152 * @param $module 153 * The name of the module that utilizes this plugin system. It will be 154 * used to call hook_ctools_plugin_$plugin() to get more data about the plugin. 155 * @param $type 156 * The type identifier of the plugin. 157 * @param $id 158 * If specified, return only information about plugin with this identifier. 159 * The system will do its utmost to load only plugins with this id. 160 * 161 * @return 162 * An array of information arrays about the plugins received. The contents 163 * of the array are specific to the plugin. 164 */ 165 function ctools_get_plugins($module, $type, $id = NULL) { 166 // Store local caches of plugins and plugin info so we don't have to do full 167 // lookups everytime. 168 $info = &ctools_static('ctools_plugin_info', array()); 169 $plugins = &ctools_static('ctools_plugins', array()); 170 171 // Attempt to shortcut this whole piece of code if we already have 172 // the requested plugin: 173 if ($id && isset($plugins[$module][$type]) && array_key_exists($id, $plugins[$module][$type])) { 174 return $plugins[$module][$type][$id]; 175 } 176 177 // Store the status of plugin loading. If a module plugin type pair is true, 178 // then it is fully loaded and no searching or setup needs to be done. 179 $setup = &ctools_static('ctools_plugin_setup', array()); 180 181 // Request metadata/defaults for this plugin from the declaring module. This 182 // is done once per page request, upon a request being made for that plugin. 183 if (!isset($info[$module][$type])) { 184 $info[$module][$type] = ctools_plugin_get_info($module, $type); 185 // Also, initialize the local plugin cache. 186 $plugins[$module][$type] = array(); 187 } 188 189 // We assume we don't need to build a cache. 190 $build_cache = FALSE; 191 192 // If the plugin info says this can be cached, check cache first. 193 if ($info[$module][$type]['cache'] && empty($setup[$module][$type])) { 194 $cache = cache_get("plugins:$module:$type", $info[$module][$type]['cache table']); 195 196 if (!empty($cache->data)) { 197 // Cache load succeeded so use the cached plugin list. 198 $plugins[$module][$type] = $cache->data; 199 // Set $setup to true so we know things where loaded. 200 $setup[$module][$type] = TRUE; 201 } 202 else { 203 // Cache load failed so store that we need to build and write the cache. 204 $build_cache = TRUE; 205 } 206 } 207 208 // Always load all hooks if we need them. Note we only need them now if the 209 // plugin asks for them. We can assume that if we have plugins we've already 210 // called the global hook. 211 if (!empty($info[$module][$type]['use hooks']) && empty($plugins[$module][$type])) { 212 $plugins[$module][$type] = ctools_plugin_load_hooks($info[$module][$type]); 213 } 214 215 // Then see if we should load all files. We only do this if we 216 // want a list of all plugins or there was a cache miss. 217 if (empty($setup[$module][$type]) && ($build_cache || !$id)) { 218 $setup[$module][$type] = TRUE; 219 $plugins[$module][$type] = array_merge($plugins[$module][$type], ctools_plugin_load_includes($info[$module][$type])); 220 // If the plugin can have child plugins, and we're loading all plugins, 221 // go through the list of plugins we have and find child plugins. 222 if (!$id && !empty($info[$module][$type]['child plugins'])) { 223 // If a plugin supports children, go through each plugin and ask. 224 $temp = array(); 225 foreach ($plugins[$module][$type] as $name => $plugin) { 226 if (!empty($plugin['get children']) && function_exists($plugin['get children'])) { 227 $temp = array_merge($plugin['get children']($plugin, $name), $temp); 228 } 229 else { 230 $temp[$name] = $plugin; 231 } 232 } 233 $plugins[$module][$type] = $temp; 234 } 235 } 236 237 238 // If we were told earlier that this is cacheable and the cache was 239 // empty, give something back. 240 if ($build_cache) { 241 cache_set("plugins:$module:$type", $plugins[$module][$type], $info[$module][$type]['cache table']); 242 } 243 244 // If no id was requested, we are finished here: 245 if (!$id) { 246 // Use array_filter because looking for unknown plugins could cause NULL 247 // entries to appear in the list later. 248 return array_filter($plugins[$module][$type]); 249 } 250 251 // Check to see if we need to look for the file 252 if (!array_key_exists($id, $plugins[$module][$type])) { 253 // If we can have child plugins, check to see if the plugin name is in the 254 // format of parent:child and break it up if it is. 255 if (!empty($info[$module][$type]['child plugins']) && strpos($id, ':') !== FALSE) { 256 list($parent, $child) = explode(':', $id, 2); 257 } 258 else { 259 $parent = $id; 260 } 261 262 if (!array_key_exists($parent, $plugins[$module][$type])) { 263 $result = ctools_plugin_load_includes($info[$module][$type], $parent); 264 // Set to either what was returned or NULL. 265 $plugins[$module][$type][$parent] = isset($result[$parent]) ? $result[$parent] : NULL; 266 } 267 268 // If we are looking for a child, and have the parent, ask the parent for the child. 269 if (!empty($child) && !empty($plugins[$module][$type][$parent]) && function_exists($plugins[$module][$type][$parent]['get child'])) { 270 $plugins[$module][$type][$id] = $plugins[$module][$type][$parent]['get child']($plugins[$module][$type][$parent], $parent, $child); 271 } 272 } 273 274 // At this point we should either have the plugin, or a NULL. 275 return $plugins[$module][$type][$id]; 276 } 277 278 /** 279 * Load plugins from a directory. 280 * 281 * @param $info 282 * The plugin info as returned by ctools_plugin_get_info() 283 * @param $file 284 * The file to load if we're looking for just one particular plugin. 285 * 286 * @return 287 * An array of information created for this plugin. 288 */ 289 function ctools_plugin_load_includes($info, $filename = NULL) { 290 // Keep a static array so we don't hit drupal_system_listing more than necessary. 291 $all_files = &ctools_static(__FUNCTION__, array()); 292 293 // If we're being asked for all plugins of a type, skip any caching 294 // we may have done because this is an admin task and it's ok to 295 // spend the extra time. 296 if (!isset($filename)) { 297 $all_files[$info['module']][$info['type']] = NULL; 298 } 299 300 if (!isset($all_files[$info['module']][$info['type']])) { 301 // If a filename was set, we will try to load our list of files from 302 // cache. This is considered normal operation and we try to reduce 303 // the time spent finding files. 304 if (isset($filename)) { 305 $cache = cache_get("ctools_plugin_files:$info[module]:$info[type]"); 306 if ($cache) { 307 $all_files[$info['module']][$info['type']] = $cache->data; 308 } 309 } 310 311 if (!isset($all_files[$info['module']][$info['type']])) { 312 $all_files[$info['module']][$info['type']] = array(); 313 // Load all our plugins. 314 $directories = ctools_plugin_get_directories($info); 315 $extension = empty($info['info file']) ? $info['extension'] : 'info'; 316 317 foreach ($directories as $module => $path) { 318 $all_files[$info['module']][$info['type']][$module] = drupal_system_listing('\.' . $extension . '$', $path, 'name', 0); 319 } 320 321 cache_set("ctools_plugin_files:$info[module]:$info[type]", $all_files[$info['module']][$info['type']]); 322 } 323 } 324 $file_list = $all_files[$info['module']][$info['type']]; 325 $plugins = array(); 326 327 // Iterate through all the plugin .inc files, load them and process the hook 328 // that should now be available. 329 foreach (array_filter($file_list) as $module => $files) { 330 if ($filename) { 331 $files = isset($files[$filename]) ? array($filename => $files[$filename]) : array(); 332 } 333 foreach ($files as $file) { 334 if (!empty($info['info file'])) { 335 // Parse a .info file 336 $result = ctools_plugin_process_info($info, $module, $file); 337 } 338 else { 339 // Parse a hook. 340 $plugin = NULL; // ensure that we don't have something leftover from earlier. 341 include_once './' . $file->filename; 342 // .inc files have a special format for the hook identifier. 343 // For example, 'foo.inc' in the module 'mogul' using the plugin 344 // whose hook is named 'borg_type' should have a function named (deep breath) 345 // mogul_foo_borg_type() 346 347 // If, however, the .inc file set the quasi-global $plugin array, we 348 // can use that and not even call a function. Set the $identifier 349 // appropriately and ctools_plugin_process() will handle it. 350 $identifier = isset($plugin) ? $plugin : $module . '_' . $file->name; 351 $result = ctools_plugin_process($info, $module, $identifier, dirname($file->filename), basename($file->filename), $file->name); 352 } 353 if (is_array($result)) { 354 $plugins = array_merge($plugins, $result); 355 } 356 } 357 } 358 return $plugins; 359 } 360 361 /** 362 * Get a list of directories to search for plugins of the given type. 363 * 364 * This utilizes hook_ctools_plugin_directory() to determine a complete list of 365 * directories. Only modules that implement this hook and return a string 366 * value will have their directories included. 367 * 368 * @param $info 369 * The $info array for the plugin as returned by ctools_plugin_get_info(). 370 * 371 * @return array $directories 372 * An array of directories to search. 373 */ 374 function ctools_plugin_get_directories($info) { 375 $directories = array(); 376 377 foreach (module_implements('ctools_plugin_directory') as $module) { 378 $function = $module . '_ctools_plugin_directory'; 379 $result = $function($info['module'], $info['type']); 380 if ($result && is_string($result)) { 381 $directories[$module] = drupal_get_path('module', $module) . '/' . $result; 382 } 383 } 384 385 if (!empty($info['load themes'])) { 386 $themes = _ctools_list_themes(); 387 foreach ($themes as $name => $theme) { 388 if (!empty($theme->info['plugins'][$info['module']][$info['type']])) { 389 $directories[$name] = drupal_get_path('theme', $name) . '/' . $theme->info['plugins'][$info['module']][$info['type']]; 390 } 391 } 392 } 393 return $directories; 394 } 395 396 /** 397 * Helper function to build a ctools-friendly list of themes capable of 398 * providing plugins. 399 * 400 * @return array $themes 401 * A list of themes that can act as plugin providers, sorted parent-first with 402 * the active theme placed last. 403 */ 404 function _ctools_list_themes() { 405 static $themes; 406 if (is_null($themes)) { 407 $current = variable_get('theme_default', FALSE); 408 $themes = $active = array(); 409 $all_themes = list_themes(); 410 foreach ($all_themes as $name => $theme) { 411 // Only search from active themes 412 if (empty($theme->status) && $theme->name != $current) { 413 continue; 414 } 415 $active[$name] = $theme; 416 // Prior to drupal 6.14, $theme->base_themes does not exist. Build it. 417 if (!isset($theme->base_themes) && !empty($theme->base_theme)) { 418 $active[$name]->base_themes = ctools_find_base_themes($all_themes, $name); 419 } 420 } 421 422 // Construct a parent-first list of all themes 423 foreach ($active as $name => $theme) { 424 $base_themes = isset($theme->base_themes) ? $theme->base_themes : array(); 425 $themes = array_merge($themes, $base_themes, array($name => $theme->info['name'])); 426 } 427 // Put the actual theme info objects into the array 428 foreach (array_keys($themes) as $name) { 429 $themes[$name] = $all_themes[$name]; 430 } 431 432 // Make sure the current default theme always gets the last word 433 if ($current_key = array_search($current, array_keys($themes))) { 434 $themes += array_splice($themes, $current_key, 1); 435 } 436 } 437 return $themes; 438 } 439 440 441 /** 442 * Find all the base themes for the specified theme. 443 * 444 * Themes can inherit templates and function implementations from earlier themes. 445 * 446 * NOTE: this is a verbatim copy of system_find_base_themes(), which was not 447 * implemented until 6.14. It is included here only as a fallback for outdated 448 * versions of drupal core. 449 * 450 * @param $themes 451 * An array of available themes. 452 * @param $key 453 * The name of the theme whose base we are looking for. 454 * @param $used_keys 455 * A recursion parameter preventing endless loops. 456 * @return 457 * Returns an array of all of the theme's ancestors; the first element's value 458 * will be NULL if an error occurred. 459 */ 460 function ctools_find_base_themes($themes, $key, $used_keys = array()) { 461 $base_key = $themes[$key]->info['base theme']; 462 // Does the base theme exist? 463 if (!isset($themes[$base_key])) { 464 return array($base_key => NULL); 465 } 466 467 $current_base_theme = array($base_key => $themes[$base_key]->info['name']); 468 469 // Is the base theme itself a child of another theme? 470 if (isset($themes[$base_key]->info['base theme'])) { 471 // Do we already know the base themes of this theme? 472 if (isset($themes[$base_key]->base_themes)) { 473 return $themes[$base_key]->base_themes + $current_base_theme; 474 } 475 // Prevent loops. 476 if (!empty($used_keys[$base_key])) { 477 return array($base_key => NULL); 478 } 479 $used_keys[$base_key] = TRUE; 480 return ctools_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme; 481 } 482 // If we get here, then this is our parent theme. 483 return $current_base_theme; 484 } 485 486 487 /** 488 * Load plugin info for the provided hook; this is handled separately from 489 * plugins from files. 490 * 491 * @param $info 492 * The info array about the plugin as created by ctools_plugin_get_info() 493 * 494 * @return 495 * An array of info supplied by any hook implementations. 496 */ 497 function ctools_plugin_load_hooks($info) { 498 $hooks = array(); 499 foreach (module_implements($info['hook']) as $module) { 500 $result = ctools_plugin_process($info, $module, $module, drupal_get_path('module', $module)); 501 if (is_array($result)) { 502 $hooks = array_merge($hooks, $result); 503 } 504 } 505 return $hooks; 506 } 507 508 /** 509 * Process a single hook implementation of a ctools plugin. 510 * 511 * @param $info 512 * The $info array about the plugin as returned by ctools_plugin_get_info() 513 * @param $module 514 * The module that implements the plugin being processed. 515 * @param $identifier 516 * The plugin identifier, which is used to create the name of the hook 517 * function being called. 518 * @param $path 519 * The path where files utilized by this plugin will be found. 520 * @param $file 521 * The file that was loaded for this plugin, if it exists. 522 * @param $base 523 * The base plugin name to use. If a file was loaded for the plugin, this 524 * is the plugin to assume must be present. This is used to automatically 525 * translate the array to make the syntax more friendly to plugin 526 * implementors. 527 */ 528 function ctools_plugin_process($info, $module, $identifier, $path, $file = NULL, $base = NULL) { 529 if (is_array($identifier)) { 530 $result = $identifier; 531 } 532 else { 533 $function = $identifier . '_' . $info['hook']; 534 if (!function_exists($function)) { 535 return NULL; 536 } 537 $result = $function(); 538 if (!isset($result) || !is_array($result)) { 539 return NULL; 540 } 541 } 542 543 // Automatically convert to the proper format that lets plugin implementations 544 // not nest arrays as deeply as they used to, but still support the older 545 // format where they do: 546 if ($base && (!isset($result[$base]) || !is_array($result[$base]))) { 547 $result = array($base => $result); 548 } 549 550 return _ctools_process_data($result, $info, $module, $path, $file); 551 } 552 553 /** 554 * Fill in default values and run hooks for data loaded for one or 555 * more plugins. 556 */ 557 function _ctools_process_data($result, $info, $module, $path, $file) { 558 // Fill in global defaults. 559 foreach ($result as $name => $plugin) { 560 $result[$name] += array( 561 'module' => $module, 562 'name' => $name, 563 'path' => $path, 564 'file' => $file, 565 'plugin module' => $info['module'], 566 'plugin type' => $info['type'], 567 ); 568 569 // Fill in plugin-specific defaults, if they exist. 570 if (!empty($info['defaults'])) { 571 if (is_array($info['defaults'])) { 572 $result[$name] += $info['defaults']; 573 } 574 // FIXME This callback-based approach for the 'defaults' key is entirely 575 // redundant with the 'process' callback. Consequently, that approach is 576 // DEPRECATED in favor using a 'process' callback. In the next major 577 // version, 'defaults' callbacks will be removed entirely; only the array 578 // addition 'defaults' approach will be allowed. 579 else if (function_exists($info['defaults'])) { 580 $info['defaults']($info, $result[$name]); 581 } 582 } 583 584 // Allow the plugin owner to do additional processing. 585 if (!empty($info['process']) && function_exists($info['process'])) { 586 $info['process']($result[$name], $info); 587 } 588 } 589 return $result; 590 } 591 592 593 /** 594 * Process an info file for plugin information, rather than a hook. 595 */ 596 function ctools_plugin_process_info($info, $module, $file) { 597 $result = drupal_parse_info_file($file->filename); 598 if ($result) { 599 $result = array($file->name => $result); 600 return _ctools_process_data($result, $info, $module, dirname($file->filename), basename($file->filename)); 601 } 602 } 603 604 /** 605 * Ask a module for info about a particular plugin type. 606 */ 607 function ctools_plugin_get_info($module, $type) { 608 $info = array(); 609 $function = $module . '_ctools_plugin_' . $type; 610 if (function_exists($function)) { 611 $info = $function(); 612 } 613 614 // Apply defaults. Array addition will not overwrite pre-existing keys. 615 $info += array( 616 'module' => $module, 617 'type' => $type, 618 'cache' => FALSE, 619 'cache table' => 'cache', 620 'use hooks' => TRUE, // TODO will default to FALSE in next major version 621 'defaults' => array(), 622 'process' => '', 623 'extension' => 'inc', 624 'info file' => FALSE, 625 'hook' => $module . '_' . $type, 626 'load themes' => FALSE, 627 ); 628 629 return $info; 630 } 631 632 /** 633 * Get a function from a plugin, if it exists. If the plugin is not already 634 * loaded, try ctools_plugin_load_function() instead. 635 * 636 * @param $plugin_definition 637 * The loaded plugin type. 638 * @param $function_name 639 * The identifier of the function. For example, 'settings form'. 640 * 641 * @return 642 * The actual name of the function to call, or NULL if the function 643 * does not exist. 644 */ 645 function ctools_plugin_get_function($plugin_definition, $function_name) { 646 // If cached the .inc file may not have been loaded. require_once is quite safe 647 // and fast so it's okay to keep calling it. 648 if (isset($plugin_definition['file'])) { 649 // Plugins that are loaded from info files have the info file as 650 // $plugin['file']. Don't try to run those. 651 $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']); 652 if (empty($info['info file'])) { 653 require_once './' . $plugin_definition['path'] . '/' . $plugin_definition['file']; 654 } 655 } 656 657 if (!isset($plugin_definition[$function_name])) { 658 return; 659 } 660 661 if (is_array($plugin_definition[$function_name]) && isset($plugin_definition[$function_name]['function'])) { 662 $function = $plugin_definition[$function_name]['function']; 663 if (isset($plugin_definition[$function_name]['file'])) { 664 $file = $plugin_definition[$function_name]['file']; 665 if (isset($plugin_definition[$function_name]['path'])) { 666 $file = $plugin_definition[$function_name]['path'] . '/' . $file; 667 } 668 require_once './' . $file; 669 } 670 } 671 else { 672 $function = $plugin_definition[$function_name]; 673 } 674 675 if (function_exists($function)) { 676 return $function; 677 } 678 } 679 680 /** 681 * Load a plugin and get a function name from it, returning success only 682 * if the function exists. 683 * 684 * @param $module 685 * The module that owns the plugin type. 686 * @param $type 687 * The type of plugin. 688 * @param $id 689 * The id of the specific plugin to load. 690 * @param $function_name 691 * The identifier of the function. For example, 'settings form'. 692 * 693 * @return 694 * The actual name of the function to call, or NULL if the function 695 * does not exist. 696 */ 697 function ctools_plugin_load_function($module, $type, $id, $function_name) { 698 $plugin = ctools_get_plugins($module, $type, $id); 699 return ctools_plugin_get_function($plugin, $function_name); 700 } 701 702 /** 703 * Get a class from a plugin, if it exists. If the plugin is not already 704 * loaded, try ctools_plugin_load_class() instead. 705 * 706 * @param $plugin_definition 707 * The loaded plugin type. 708 * @param $class_name 709 * The identifier of the class. For example, 'handler'. 710 * @param $abstract 711 * If true, will return abstract classes. Otherwise, parents will be included but nothing will be returned. 712 * 713 * @return 714 * The actual name of the class to call, or NULL if the class does not exist. 715 */ 716 function ctools_plugin_get_class($plugin_definition, $class_name, $abstract = FALSE) { 717 // If cached the .inc file may not have been loaded. require_once is quite safe 718 // and fast so it's okay to keep calling it. 719 if (isset($plugin_definition['file'])) { 720 // Plugins that are loaded from info files have the info file as 721 // $plugin['file']. Don't try to run those. 722 $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']); 723 if (empty($info['info file'])) { 724 require_once './' . $plugin_definition['path'] . '/' . $plugin_definition['file']; 725 } 726 } 727 728 if (!isset($plugin_definition[$class_name])) { 729 return; 730 } 731 732 if (is_array($plugin_definition[$class_name]) && isset($plugin_definition[$class_name]['class'])) { 733 if (isset($plugin_definition[$class_name]['parent'])) { 734 // Make sure parents are included. 735 // TODO parent-loading needs to be better documented; the 'parent' designated 736 // on the plugin actually corresponds not to the name of the parent CLASS, 737 // but the name of the parent PLUGIN (and it then loads loads whatever 738 // class is in the same $class_name slot). Initially unintuitive. 739 ctools_plugin_load_class($plugin_definition['plugin module'], $plugin_definition['plugin type'], $plugin_definition[$class_name]['parent'], $class_name); 740 } 741 $class = $plugin_definition[$class_name]['class']; 742 if (isset($plugin_definition[$class_name]['file'])) { 743 $file = $plugin_definition[$class_name]['file']; 744 if (isset($plugin_definition[$class_name]['path'])) { 745 $file = $plugin_definition[$class_name]['path'] . '/' . $file; 746 } 747 require_once './' . $file; 748 } 749 } 750 else { 751 $class = $plugin_definition[$class_name]; 752 } 753 754 // If we didn't explicitly include a file above, try autoloading a file 755 // based on the class' name. 756 if (!isset($file) && file_exists($plugin_definition['path'] . "/$class.class.php")) { 757 require_once $plugin_definition['path'] . "/$class.class.php"; 758 } 759 760 if (class_exists($class) && 761 (empty($plugin_definition[$class_name]['abstract']) || $abstract)) { 762 return $class; 763 } 764 } 765 766 /** 767 * Load a plugin and get a class name from it, returning success only if the 768 * class exists. 769 * 770 * @param $module 771 * The module that owns the plugin type. 772 * @param $type 773 * The type of plugin. 774 * @param $id 775 * The id of the specific plugin to load. 776 * @param $class_name 777 * The identifier of the class. For example, 'handler'. 778 * @param $abstract 779 * If true, will tell ctools_plugin_get_class to allow the return of abstract classes. 780 * 781 * @return 782 * The actual name of the class to call, or NULL if the class does not exist. 783 */ 784 function ctools_plugin_load_class($module, $type, $id, $class_name, $abstract = FALSE) { 785 $plugin = ctools_get_plugins($module, $type, $id); 786 return ctools_plugin_get_class($plugin, $class_name, $abstract); 787 } 788 789 /** 790 * Sort callback for sorting plugins naturally. 791 * 792 * Sort first by weight, then by title. 793 */ 794 function ctools_plugin_sort($a, $b) { 795 if (is_object($a)) { 796 $a = (array) $a; 797 } 798 if (is_object($b)) { 799 $b = (array) $b; 800 } 801 802 if (empty($a['weight'])) { 803 $a['weight'] = 0; 804 } 805 806 if (empty($b['weight'])) { 807 $b['weight'] = 0; 808 } 809 810 if ($a['weight'] == $b['weight']) { 811 return strnatcmp(strtolower($a['title']), strtolower($b['title'])); 812 } 813 return ($a['weight'] < $b['weight']) ? -1 : 1; 814 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |