Drupal PHP Cross Reference Content Management Systems

Source: /includes/module.inc - 1058 lines - 39167 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * API for loading and interacting with Drupal modules.
   6   */
   7  
   8  /**
   9   * Loads all the modules that have been enabled in the system table.
  10   *
  11   * @param $bootstrap
  12   *   Whether to load only the reduced set of modules loaded in "bootstrap mode"
  13   *   for cached pages. See bootstrap.inc.
  14   *
  15   * @return
  16   *   If $bootstrap is NULL, return a boolean indicating whether all modules
  17   *   have been loaded.
  18   */
  19  function module_load_all($bootstrap = FALSE) {
  20    static $has_run = FALSE;
  21  
  22    if (isset($bootstrap)) {
  23      foreach (module_list(TRUE, $bootstrap) as $module) {
  24        drupal_load('module', $module);
  25      }
  26      // $has_run will be TRUE if $bootstrap is FALSE.
  27      $has_run = !$bootstrap;
  28    }
  29    return $has_run;
  30  }
  31  
  32  
  33  /**
  34   * Returns a list of currently active modules.
  35   *
  36   * Usually, this returns a list of all enabled modules. When called early on in
  37   * the bootstrap, it will return a list of vital modules only (those needed to
  38   * generate cached pages).
  39   *
  40   * All parameters to this function are optional and should generally not be
  41   * changed from their defaults.
  42   *
  43   * @param $refresh
  44   *   (optional) Whether to force the module list to be regenerated (such as
  45   *   after the administrator has changed the system settings). Defaults to
  46   *   FALSE.
  47   * @param $bootstrap_refresh
  48   *   (optional) When $refresh is TRUE, setting $bootstrap_refresh to TRUE forces
  49   *   the module list to be regenerated using the reduced set of modules loaded
  50   *   in "bootstrap mode" for cached pages. Otherwise, setting $refresh to TRUE
  51   *   generates the complete list of enabled modules.
  52   * @param $sort
  53   *   (optional) By default, modules are ordered by weight and module name. Set
  54   *   this option to TRUE to return a module list ordered only by module name.
  55   * @param $fixed_list
  56   *   (optional) If an array of module names is provided, this will override the
  57   *   module list with the given set of modules. This will persist until the next
  58   *   call with $refresh set to TRUE or with a new $fixed_list passed in. This
  59   *   parameter is primarily intended for internal use (e.g., in install.php and
  60   *   update.php).
  61   *
  62   * @return
  63   *   An associative array whose keys and values are the names of the modules in
  64   *   the list.
  65   */
  66  function module_list($refresh = FALSE, $bootstrap_refresh = FALSE, $sort = FALSE, $fixed_list = NULL) {
  67    static $list = array(), $sorted_list;
  68  
  69    if (empty($list) || $refresh || $fixed_list) {
  70      $list = array();
  71      $sorted_list = NULL;
  72      if ($fixed_list) {
  73        foreach ($fixed_list as $name => $module) {
  74          drupal_get_filename('module', $name, $module['filename']);
  75          $list[$name] = $name;
  76        }
  77      }
  78      else {
  79        if ($refresh) {
  80          // For the $refresh case, make sure that system_list() returns fresh
  81          // data.
  82          drupal_static_reset('system_list');
  83        }
  84        if ($bootstrap_refresh) {
  85          $list = system_list('bootstrap');
  86        }
  87        else {
  88          // Not using drupal_map_assoc() here as that requires common.inc.
  89          $list = array_keys(system_list('module_enabled'));
  90          $list = (!empty($list) ? array_combine($list, $list) : array());
  91        }
  92      }
  93    }
  94    if ($sort) {
  95      if (!isset($sorted_list)) {
  96        $sorted_list = $list;
  97        ksort($sorted_list);
  98      }
  99      return $sorted_list;
 100    }
 101    return $list;
 102  }
 103  
 104  /**
 105   * Builds a list of bootstrap modules and enabled modules and themes.
 106   *
 107   * @param $type
 108   *   The type of list to return:
 109   *   - module_enabled: All enabled modules.
 110   *   - bootstrap: All enabled modules required for bootstrap.
 111   *   - theme: All themes.
 112   *
 113   * @return
 114   *   An associative array of modules or themes, keyed by name. For $type
 115   *   'bootstrap', the array values equal the keys. For $type 'module_enabled'
 116   *   or 'theme', the array values are objects representing the respective
 117   *   database row, with the 'info' property already unserialized.
 118   *
 119   * @see module_list()
 120   * @see list_themes()
 121   */
 122  function system_list($type) {
 123    $lists = &drupal_static(__FUNCTION__);
 124  
 125    // For bootstrap modules, attempt to fetch the list from cache if possible.
 126    // if not fetch only the required information to fire bootstrap hooks
 127    // in case we are going to serve the page from cache.
 128    if ($type == 'bootstrap') {
 129      if (isset($lists['bootstrap'])) {
 130        return $lists['bootstrap'];
 131      }
 132      if ($cached = cache_get('bootstrap_modules', 'cache_bootstrap')) {
 133        $bootstrap_list = $cached->data;
 134      }
 135      else {
 136        $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name');
 137        cache_set('bootstrap_modules', $bootstrap_list, 'cache_bootstrap');
 138      }
 139      // To avoid a separate database lookup for the filepath, prime the
 140      // drupal_get_filename() static cache for bootstrap modules only.
 141      // The rest is stored separately to keep the bootstrap module cache small.
 142      foreach ($bootstrap_list as $module) {
 143        drupal_get_filename('module', $module->name, $module->filename);
 144      }
 145      // We only return the module names here since module_list() doesn't need
 146      // the filename itself.
 147      $lists['bootstrap'] = array_keys($bootstrap_list);
 148    }
 149    // Otherwise build the list for enabled modules and themes.
 150    elseif (!isset($lists['module_enabled'])) {
 151      if ($cached = cache_get('system_list', 'cache_bootstrap')) {
 152        $lists = $cached->data;
 153      }
 154      else {
 155        $lists = array(
 156          'module_enabled' => array(),
 157          'theme' => array(),
 158          'filepaths' => array(),
 159        );
 160        // The module name (rather than the filename) is used as the fallback
 161        // weighting in order to guarantee consistent behavior across different
 162        // Drupal installations, which might have modules installed in different
 163        // locations in the file system. The ordering here must also be
 164        // consistent with the one used in module_implements().
 165        $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC");
 166        foreach ($result as $record) {
 167          $record->info = unserialize($record->info);
 168          // Build a list of all enabled modules.
 169          if ($record->type == 'module') {
 170            $lists['module_enabled'][$record->name] = $record;
 171          }
 172          // Build a list of themes.
 173          if ($record->type == 'theme') {
 174            $lists['theme'][$record->name] = $record;
 175          }
 176          // Build a list of filenames so drupal_get_filename can use it.
 177          if ($record->status) {
 178            $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename);
 179          }
 180        }
 181        foreach ($lists['theme'] as $key => $theme) {
 182          if (!empty($theme->info['base theme'])) {
 183            // Make a list of the theme's base themes.
 184            require_once  DRUPAL_ROOT . '/includes/theme.inc';
 185            $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
 186            // Don't proceed if there was a problem with the root base theme.
 187            if (!current($lists['theme'][$key]->base_themes)) {
 188              continue;
 189            }
 190            // Determine the root base theme.
 191            $base_key = key($lists['theme'][$key]->base_themes);
 192            // Add to the list of sub-themes for each of the theme's base themes.
 193            foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
 194              $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
 195            }
 196            // Add the base theme's theme engine info.
 197            $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme';
 198          }
 199          else {
 200            // A plain theme is its own engine.
 201            $base_key = $key;
 202            if (!isset($lists['theme'][$key]->info['engine'])) {
 203              $lists['theme'][$key]->info['engine'] = 'theme';
 204            }
 205          }
 206          // Set the theme engine prefix.
 207          $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
 208        }
 209        cache_set('system_list', $lists, 'cache_bootstrap');
 210      }
 211      // To avoid a separate database lookup for the filepath, prime the
 212      // drupal_get_filename() static cache with all enabled modules and themes.
 213      foreach ($lists['filepaths'] as $item) {
 214        drupal_get_filename($item['type'], $item['name'], $item['filepath']);
 215      }
 216    }
 217  
 218    return $lists[$type];
 219  }
 220  
 221  /**
 222   * Resets all system_list() caches.
 223   */
 224  function system_list_reset() {
 225    drupal_static_reset('system_list');
 226    drupal_static_reset('system_rebuild_module_data');
 227    drupal_static_reset('list_themes');
 228    cache_clear_all('bootstrap_modules', 'cache_bootstrap');
 229    cache_clear_all('system_list', 'cache_bootstrap');
 230  }
 231  
 232  /**
 233   * Determines which modules require and are required by each module.
 234   *
 235   * @param $files
 236   *   The array of filesystem objects used to rebuild the cache.
 237   *
 238   * @return
 239   *   The same array with the new keys for each module:
 240   *   - requires: An array with the keys being the modules that this module
 241   *     requires.
 242   *   - required_by: An array with the keys being the modules that will not work
 243   *     without this module.
 244   */
 245  function _module_build_dependencies($files) {
 246    require_once  DRUPAL_ROOT . '/includes/graph.inc';
 247    foreach ($files as $filename => $file) {
 248      $graph[$file->name]['edges'] = array();
 249      if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
 250        foreach ($file->info['dependencies'] as $dependency) {
 251          $dependency_data = drupal_parse_dependency($dependency);
 252          $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
 253        }
 254      }
 255    }
 256    drupal_depth_first_search($graph);
 257    foreach ($graph as $module => $data) {
 258      $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
 259      $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
 260      $files[$module]->sort = $data['weight'];
 261    }
 262    return $files;
 263  }
 264  
 265  /**
 266   * Determines whether a given module exists.
 267   *
 268   * @param $module
 269   *   The name of the module (without the .module extension).
 270   *
 271   * @return
 272   *   TRUE if the module is both installed and enabled.
 273   */
 274  function module_exists($module) {
 275    $list = module_list();
 276    return isset($list[$module]);
 277  }
 278  
 279  /**
 280   * Loads a module's installation hooks.
 281   *
 282   * @param $module
 283   *   The name of the module (without the .module extension).
 284   *
 285   * @return
 286   *   The name of the module's install file, if successful; FALSE otherwise.
 287   */
 288  function module_load_install($module) {
 289    // Make sure the installation API is available
 290    include_once  DRUPAL_ROOT . '/includes/install.inc';
 291  
 292    return module_load_include('install', $module);
 293  }
 294  
 295  /**
 296   * Loads a module include file.
 297   *
 298   * Examples:
 299   * @code
 300   *   // Load node.admin.inc from the node module.
 301   *   module_load_include('inc', 'node', 'node.admin');
 302   *   // Load content_types.inc from the node module.
 303   *   module_load_include('inc', 'node', 'content_types');
 304   * @endcode
 305   *
 306   * Do not use this function to load an install file, use module_load_install()
 307   * instead. Do not use this function in a global context since it requires
 308   * Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file'
 309   * instead.
 310   *
 311   * @param $type
 312   *   The include file's type (file extension).
 313   * @param $module
 314   *   The module to which the include file belongs.
 315   * @param $name
 316   *   (optional) The base file name (without the $type extension). If omitted,
 317   *   $module is used; i.e., resulting in "$module.$type" by default.
 318   *
 319   * @return
 320   *   The name of the included file, if successful; FALSE otherwise.
 321   */
 322  function module_load_include($type, $module, $name = NULL) {
 323    if (!isset($name)) {
 324      $name = $module;
 325    }
 326  
 327    if (function_exists('drupal_get_path')) {
 328      $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
 329      if (is_file($file)) {
 330        require_once $file;
 331        return $file;
 332      }
 333    }
 334    return FALSE;
 335  }
 336  
 337  /**
 338   * Loads an include file for each module enabled in the {system} table.
 339   */
 340  function module_load_all_includes($type, $name = NULL) {
 341    $modules = module_list();
 342    foreach ($modules as $module) {
 343      module_load_include($type, $module, $name);
 344    }
 345  }
 346  
 347  /**
 348   * Enables or installs a given list of modules.
 349   *
 350   * Definitions:
 351   * - "Enabling" is the process of activating a module for use by Drupal.
 352   * - "Disabling" is the process of deactivating a module.
 353   * - "Installing" is the process of enabling it for the first time or after it
 354   *   has been uninstalled.
 355   * - "Uninstalling" is the process of removing all traces of a module.
 356   *
 357   * Order of events:
 358   * - Gather and add module dependencies to $module_list (if applicable).
 359   * - For each module that is being enabled:
 360   *   - Install module schema and update system registries and caches.
 361   *   - If the module is being enabled for the first time or had been
 362   *     uninstalled, invoke hook_install() and add it to the list of installed
 363   *     modules.
 364   *   - Invoke hook_enable().
 365   * - Invoke hook_modules_installed().
 366   * - Invoke hook_modules_enabled().
 367   *
 368   * @param $module_list
 369   *   An array of module names.
 370   * @param $enable_dependencies
 371   *   If TRUE, dependencies will automatically be added and enabled in the
 372   *   correct order. This incurs a significant performance cost, so use FALSE
 373   *   if you know $module_list is already complete and in the correct order.
 374   *
 375   * @return
 376   *   FALSE if one or more dependencies are missing, TRUE otherwise.
 377   *
 378   * @see hook_install()
 379   * @see hook_enable()
 380   * @see hook_modules_installed()
 381   * @see hook_modules_enabled()
 382   */
 383  function module_enable($module_list, $enable_dependencies = TRUE) {
 384    if ($enable_dependencies) {
 385      // Get all module data so we can find dependencies and sort.
 386      $module_data = system_rebuild_module_data();
 387      // Create an associative array with weights as values.
 388      $module_list = array_flip(array_values($module_list));
 389  
 390      while (list($module) = each($module_list)) {
 391        if (!isset($module_data[$module])) {
 392          // This module is not found in the filesystem, abort.
 393          return FALSE;
 394        }
 395        if ($module_data[$module]->status) {
 396          // Skip already enabled modules.
 397          unset($module_list[$module]);
 398          continue;
 399        }
 400        $module_list[$module] = $module_data[$module]->sort;
 401  
 402        // Add dependencies to the list, with a placeholder weight.
 403        // The new modules will be processed as the while loop continues.
 404        foreach (array_keys($module_data[$module]->requires) as $dependency) {
 405          if (!isset($module_list[$dependency])) {
 406            $module_list[$dependency] = 0;
 407          }
 408        }
 409      }
 410  
 411      if (!$module_list) {
 412        // Nothing to do. All modules already enabled.
 413        return TRUE;
 414      }
 415  
 416      // Sort the module list by pre-calculated weights.
 417      arsort($module_list);
 418      $module_list = array_keys($module_list);
 419    }
 420  
 421    // Required for module installation checks.
 422    include_once  DRUPAL_ROOT . '/includes/install.inc';
 423  
 424    $modules_installed = array();
 425    $modules_enabled = array();
 426    foreach ($module_list as $module) {
 427      // Only process modules that are not already enabled.
 428      $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array(
 429        ':type' => 'module',
 430        ':name' => $module))
 431        ->fetchObject();
 432      if ($existing->status == 0) {
 433        // Load the module's code.
 434        drupal_load('module', $module);
 435        module_load_install($module);
 436  
 437        // Update the database and module list to reflect the new module. This
 438        // needs to be done first so that the module's hook implementations,
 439        // hook_schema() in particular, can be called while it is being
 440        // installed.
 441        db_update('system')
 442          ->fields(array('status' => 1))
 443          ->condition('type', 'module')
 444          ->condition('name', $module)
 445          ->execute();
 446        // Refresh the module list to include it.
 447        system_list_reset();
 448        module_list(TRUE);
 449        module_implements('', FALSE, TRUE);
 450        _system_update_bootstrap_status();
 451        // Update the registry to include it.
 452        registry_update();
 453        // Refresh the schema to include it.
 454        drupal_get_schema(NULL, TRUE);
 455        // Update the theme registry to include it.
 456        drupal_theme_rebuild();
 457        // Clear entity cache.
 458        entity_info_cache_clear();
 459  
 460        // Now install the module if necessary.
 461        if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
 462          drupal_install_schema($module);
 463  
 464          // Set the schema version to the number of the last update provided
 465          // by the module.
 466          $versions = drupal_get_schema_versions($module);
 467          $version = $versions ? max($versions) : SCHEMA_INSTALLED;
 468  
 469          // If the module has no current updates, but has some that were
 470          // previously removed, set the version to the value of
 471          // hook_update_last_removed().
 472          if ($last_removed = module_invoke($module, 'update_last_removed')) {
 473            $version = max($version, $last_removed);
 474          }
 475          drupal_set_installed_schema_version($module, $version);
 476          // Allow the module to perform install tasks.
 477          module_invoke($module, 'install');
 478          // Record the fact that it was installed.
 479          $modules_installed[] = $module;
 480          watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO);
 481        }
 482  
 483        // Enable the module.
 484        module_invoke($module, 'enable');
 485  
 486        // Record the fact that it was enabled.
 487        $modules_enabled[] = $module;
 488        watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO);
 489      }
 490    }
 491  
 492    // If any modules were newly installed, invoke hook_modules_installed().
 493    if (!empty($modules_installed)) {
 494      module_invoke_all('modules_installed', $modules_installed);
 495    }
 496  
 497    // If any modules were newly enabled, invoke hook_modules_enabled().
 498    if (!empty($modules_enabled)) {
 499      module_invoke_all('modules_enabled', $modules_enabled);
 500    }
 501  
 502    return TRUE;
 503  }
 504  
 505  /**
 506   * Disables a given set of modules.
 507   *
 508   * @param $module_list
 509   *   An array of module names.
 510   * @param $disable_dependents
 511   *   If TRUE, dependent modules will automatically be added and disabled in the
 512   *   correct order. This incurs a significant performance cost, so use FALSE
 513   *   if you know $module_list is already complete and in the correct order.
 514   */
 515  function module_disable($module_list, $disable_dependents = TRUE) {
 516    if ($disable_dependents) {
 517      // Get all module data so we can find dependents and sort.
 518      $module_data = system_rebuild_module_data();
 519      // Create an associative array with weights as values.
 520      $module_list = array_flip(array_values($module_list));
 521  
 522      $profile = drupal_get_profile();
 523      while (list($module) = each($module_list)) {
 524        if (!isset($module_data[$module]) || !$module_data[$module]->status) {
 525          // This module doesn't exist or is already disabled, skip it.
 526          unset($module_list[$module]);
 527          continue;
 528        }
 529        $module_list[$module] = $module_data[$module]->sort;
 530  
 531        // Add dependent modules to the list, with a placeholder weight.
 532        // The new modules will be processed as the while loop continues.
 533        foreach ($module_data[$module]->required_by as $dependent => $dependent_data) {
 534          if (!isset($module_list[$dependent]) && $dependent != $profile) {
 535            $module_list[$dependent] = 0;
 536          }
 537        }
 538      }
 539  
 540      // Sort the module list by pre-calculated weights.
 541      asort($module_list);
 542      $module_list = array_keys($module_list);
 543    }
 544  
 545    $invoke_modules = array();
 546  
 547    foreach ($module_list as $module) {
 548      if (module_exists($module)) {
 549        // Check if node_access table needs rebuilding.
 550        if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
 551          node_access_needs_rebuild(TRUE);
 552        }
 553  
 554        module_load_install($module);
 555        module_invoke($module, 'disable');
 556        db_update('system')
 557          ->fields(array('status' => 0))
 558          ->condition('type', 'module')
 559          ->condition('name', $module)
 560          ->execute();
 561        $invoke_modules[] = $module;
 562        watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO);
 563      }
 564    }
 565  
 566    if (!empty($invoke_modules)) {
 567      // Refresh the module list to exclude the disabled modules.
 568      system_list_reset();
 569      module_list(TRUE);
 570      module_implements('', FALSE, TRUE);
 571      entity_info_cache_clear();
 572      // Invoke hook_modules_disabled before disabling modules,
 573      // so we can still call module hooks to get information.
 574      module_invoke_all('modules_disabled', $invoke_modules);
 575      // Update the registry to remove the newly-disabled module.
 576      registry_update();
 577      _system_update_bootstrap_status();
 578      // Update the theme registry to remove the newly-disabled module.
 579      drupal_theme_rebuild();
 580    }
 581  
 582    // If there remains no more node_access module, rebuilding will be
 583    // straightforward, we can do it right now.
 584    if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
 585      node_access_rebuild();
 586    }
 587  }
 588  
 589  /**
 590   * @defgroup hooks Hooks
 591   * @{
 592   * Allow modules to interact with the Drupal core.
 593   *
 594   * Drupal's module system is based on the concept of "hooks". A hook is a PHP
 595   * function that is named foo_bar(), where "foo" is the name of the module
 596   * (whose filename is thus foo.module) and "bar" is the name of the hook. Each
 597   * hook has a defined set of parameters and a specified result type.
 598   *
 599   * To extend Drupal, a module need simply implement a hook. When Drupal wishes
 600   * to allow intervention from modules, it determines which modules implement a
 601   * hook and calls that hook in all enabled modules that implement it.
 602   *
 603   * The available hooks to implement are explained here in the Hooks section of
 604   * the developer documentation. The string "hook" is used as a placeholder for
 605   * the module name in the hook definitions. For example, if the module file is
 606   * called example.module, then hook_help() as implemented by that module would
 607   * be defined as example_help().
 608   *
 609   * The example functions included are not part of the Drupal core, they are
 610   * just models that you can modify. Only the hooks implemented within modules
 611   * are executed when running Drupal.
 612   *
 613   * See also @link themeable the themeable group page. @endlink
 614   */
 615  
 616  /**
 617   * Determines whether a module implements a hook.
 618   *
 619   * @param $module
 620   *   The name of the module (without the .module extension).
 621   * @param $hook
 622   *   The name of the hook (e.g. "help" or "menu").
 623   *
 624   * @return
 625   *   TRUE if the module is both installed and enabled, and the hook is
 626   *   implemented in that module.
 627   */
 628  function module_hook($module, $hook) {
 629    $function = $module . '_' . $hook;
 630    if (function_exists($function)) {
 631      return TRUE;
 632    }
 633    // If the hook implementation does not exist, check whether it may live in an
 634    // optional include file registered via hook_hook_info().
 635    $hook_info = module_hook_info();
 636    if (isset($hook_info[$hook]['group'])) {
 637      module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
 638      if (function_exists($function)) {
 639        return TRUE;
 640      }
 641    }
 642    return FALSE;
 643  }
 644  
 645  /**
 646   * Determines which modules are implementing a hook.
 647   *
 648   * @param $hook
 649   *   The name of the hook (e.g. "help" or "menu").
 650   * @param $sort
 651   *   By default, modules are ordered by weight and filename, settings this option
 652   *   to TRUE, module list will be ordered by module name.
 653   * @param $reset
 654   *   For internal use only: Whether to force the stored list of hook
 655   *   implementations to be regenerated (such as after enabling a new module,
 656   *   before processing hook_enable).
 657   *
 658   * @return
 659   *   An array with the names of the modules which are implementing this hook.
 660   *
 661   * @see module_implements_write_cache()
 662   */
 663  function module_implements($hook, $sort = FALSE, $reset = FALSE) {
 664    // Use the advanced drupal_static() pattern, since this is called very often.
 665    static $drupal_static_fast;
 666    if (!isset($drupal_static_fast)) {
 667      $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__);
 668    }
 669    $implementations = &$drupal_static_fast['implementations'];
 670  
 671    // We maintain a persistent cache of hook implementations in addition to the
 672    // static cache to avoid looping through every module and every hook on each
 673    // request. Benchmarks show that the benefit of this caching outweighs the
 674    // additional database hit even when using the default database caching
 675    // backend and only a small number of modules are enabled. The cost of the
 676    // cache_get() is more or less constant and reduced further when non-database
 677    // caching backends are used, so there will be more significant gains when a
 678    // large number of modules are installed or hooks invoked, since this can
 679    // quickly lead to module_hook() being called several thousand times
 680    // per request.
 681    if ($reset) {
 682      $implementations = array();
 683      cache_set('module_implements', array(), 'cache_bootstrap');
 684      drupal_static_reset('module_hook_info');
 685      drupal_static_reset('drupal_alter');
 686      cache_clear_all('hook_info', 'cache_bootstrap');
 687      return;
 688    }
 689  
 690    // Fetch implementations from cache.
 691    if (empty($implementations)) {
 692      $implementations = cache_get('module_implements', 'cache_bootstrap');
 693      if ($implementations === FALSE) {
 694        $implementations = array();
 695      }
 696      else {
 697        $implementations = $implementations->data;
 698      }
 699    }
 700  
 701    if (!isset($implementations[$hook])) {
 702      // The hook is not cached, so ensure that whether or not it has
 703      // implementations, that the cache is updated at the end of the request.
 704      $implementations['#write_cache'] = TRUE;
 705      $hook_info = module_hook_info();
 706      $implementations[$hook] = array();
 707      $list = module_list(FALSE, FALSE, $sort);
 708      foreach ($list as $module) {
 709        $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
 710        // Since module_hook() may needlessly try to load the include file again,
 711        // function_exists() is used directly here.
 712        if (function_exists($module . '_' . $hook)) {
 713          $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
 714        }
 715      }
 716      // Allow modules to change the weight of specific implementations but avoid
 717      // an infinite loop.
 718      if ($hook != 'module_implements_alter') {
 719        drupal_alter('module_implements', $implementations[$hook], $hook);
 720      }
 721    }
 722    else {
 723      foreach ($implementations[$hook] as $module => $group) {
 724        // If this hook implementation is stored in a lazy-loaded file, so include
 725        // that file first.
 726        if ($group) {
 727          module_load_include('inc', $module, "$module.$group");
 728        }
 729        // It is possible that a module removed a hook implementation without the
 730        // implementations cache being rebuilt yet, so we check whether the
 731        // function exists on each request to avoid undefined function errors.
 732        // Since module_hook() may needlessly try to load the include file again,
 733        // function_exists() is used directly here.
 734        if (!function_exists($module . '_' . $hook)) {
 735          // Clear out the stale implementation from the cache and force a cache
 736          // refresh to forget about no longer existing hook implementations.
 737          unset($implementations[$hook][$module]);
 738          $implementations['#write_cache'] = TRUE;
 739        }
 740      }
 741    }
 742  
 743    return array_keys($implementations[$hook]);
 744  }
 745  
 746  /**
 747   * Retrieves a list of hooks that are declared through hook_hook_info().
 748   *
 749   * @return
 750   *   An associative array whose keys are hook names and whose values are an
 751   *   associative array containing a group name. The structure of the array
 752   *   is the same as the return value of hook_hook_info().
 753   *
 754   * @see hook_hook_info()
 755   */
 756  function module_hook_info() {
 757    // This function is indirectly invoked from bootstrap_invoke_all(), in which
 758    // case common.inc, subsystems, and modules are not loaded yet, so it does not
 759    // make sense to support hook groups resp. lazy-loaded include files prior to
 760    // full bootstrap.
 761    if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) {
 762      return array();
 763    }
 764    $hook_info = &drupal_static(__FUNCTION__);
 765  
 766    if (!isset($hook_info)) {
 767      $hook_info = array();
 768      $cache = cache_get('hook_info', 'cache_bootstrap');
 769      if ($cache === FALSE) {
 770        // Rebuild the cache and save it.
 771        // We can't use module_invoke_all() here or it would cause an infinite
 772        // loop.
 773        foreach (module_list() as $module) {
 774          $function = $module . '_hook_info';
 775          if (function_exists($function)) {
 776            $result = $function();
 777            if (isset($result) && is_array($result)) {
 778              $hook_info = array_merge_recursive($hook_info, $result);
 779            }
 780          }
 781        }
 782        // We can't use drupal_alter() for the same reason as above.
 783        foreach (module_list() as $module) {
 784          $function = $module . '_hook_info_alter';
 785          if (function_exists($function)) {
 786            $function($hook_info);
 787          }
 788        }
 789        cache_set('hook_info', $hook_info, 'cache_bootstrap');
 790      }
 791      else {
 792        $hook_info = $cache->data;
 793      }
 794    }
 795  
 796    return $hook_info;
 797  }
 798  
 799  /**
 800   * Writes the hook implementation cache.
 801   *
 802   * @see module_implements()
 803   */
 804  function module_implements_write_cache() {
 805    $implementations = &drupal_static('module_implements');
 806    // Check whether we need to write the cache. We do not want to cache hooks
 807    // which are only invoked on HTTP POST requests since these do not need to be
 808    // optimized as tightly, and not doing so keeps the cache entry smaller.
 809    if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
 810      unset($implementations['#write_cache']);
 811      cache_set('module_implements', $implementations, 'cache_bootstrap');
 812    }
 813  }
 814  
 815  /**
 816   * Invokes a hook in a particular module.
 817   *
 818   * @param $module
 819   *   The name of the module (without the .module extension).
 820   * @param $hook
 821   *   The name of the hook to invoke.
 822   * @param ...
 823   *   Arguments to pass to the hook implementation.
 824   *
 825   * @return
 826   *   The return value of the hook implementation.
 827   */
 828  function module_invoke($module, $hook) {
 829    $args = func_get_args();
 830    // Remove $module and $hook from the arguments.
 831    unset($args[0], $args[1]);
 832    if (module_hook($module, $hook)) {
 833      return call_user_func_array($module . '_' . $hook, $args);
 834    }
 835  }
 836  
 837  /**
 838   * Invokes a hook in all enabled modules that implement it.
 839   *
 840   * @param $hook
 841   *   The name of the hook to invoke.
 842   * @param ...
 843   *   Arguments to pass to the hook.
 844   *
 845   * @return
 846   *   An array of return values of the hook implementations. If modules return
 847   *   arrays from their implementations, those are merged into one array.
 848   */
 849  function module_invoke_all($hook) {
 850    $args = func_get_args();
 851    // Remove $hook from the arguments.
 852    unset($args[0]);
 853    $return = array();
 854    foreach (module_implements($hook) as $module) {
 855      $function = $module . '_' . $hook;
 856      if (function_exists($function)) {
 857        $result = call_user_func_array($function, $args);
 858        if (isset($result) && is_array($result)) {
 859          $return = array_merge_recursive($return, $result);
 860        }
 861        elseif (isset($result)) {
 862          $return[] = $result;
 863        }
 864      }
 865    }
 866  
 867    return $return;
 868  }
 869  
 870  /**
 871   * @} End of "defgroup hooks".
 872   */
 873  
 874  /**
 875   * Returns an array of modules required by core.
 876   */
 877  function drupal_required_modules() {
 878    $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'modules', 'name', 0);
 879    $required = array();
 880  
 881    // An installation profile is required and one must always be loaded.
 882    $required[] = drupal_get_profile();
 883  
 884    foreach ($files as $name => $file) {
 885      $info = drupal_parse_info_file($file->uri);
 886      if (!empty($info) && !empty($info['required']) && $info['required']) {
 887        $required[] = $name;
 888      }
 889    }
 890  
 891    return $required;
 892  }
 893  
 894  /**
 895   * Passes alterable variables to specific hook_TYPE_alter() implementations.
 896   *
 897   * This dispatch function hands off the passed-in variables to type-specific
 898   * hook_TYPE_alter() implementations in modules. It ensures a consistent
 899   * interface for all altering operations.
 900   *
 901   * A maximum of 2 alterable arguments is supported. In case more arguments need
 902   * to be passed and alterable, modules provide additional variables assigned by
 903   * reference in the last $context argument:
 904   * @code
 905   *   $context = array(
 906   *     'alterable' => &$alterable,
 907   *     'unalterable' => $unalterable,
 908   *     'foo' => 'bar',
 909   *   );
 910   *   drupal_alter('mymodule_data', $alterable1, $alterable2, $context);
 911   * @endcode
 912   *
 913   * Note that objects are always passed by reference in PHP5. If it is absolutely
 914   * required that no implementation alters a passed object in $context, then an
 915   * object needs to be cloned:
 916   * @code
 917   *   $context = array(
 918   *     'unalterable_object' => clone $object,
 919   *   );
 920   *   drupal_alter('mymodule_data', $data, $context);
 921   * @endcode
 922   *
 923   * @param $type
 924   *   A string describing the type of the alterable $data. 'form', 'links',
 925   *   'node_content', and so on are several examples. Alternatively can be an
 926   *   array, in which case hook_TYPE_alter() is invoked for each value in the
 927   *   array, ordered first by module, and then for each module, in the order of
 928   *   values in $type. For example, when Form API is using drupal_alter() to
 929   *   execute both hook_form_alter() and hook_form_FORM_ID_alter()
 930   *   implementations, it passes array('form', 'form_' . $form_id) for $type.
 931   * @param $data
 932   *   The variable that will be passed to hook_TYPE_alter() implementations to be
 933   *   altered. The type of this variable depends on the value of the $type
 934   *   argument. For example, when altering a 'form', $data will be a structured
 935   *   array. When altering a 'profile', $data will be an object.
 936   * @param $context1
 937   *   (optional) An additional variable that is passed by reference.
 938   * @param $context2
 939   *   (optional) An additional variable that is passed by reference. If more
 940   *   context needs to be provided to implementations, then this should be an
 941   *   associative array as described above.
 942   */
 943  function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
 944    // Use the advanced drupal_static() pattern, since this is called very often.
 945    static $drupal_static_fast;
 946    if (!isset($drupal_static_fast)) {
 947      $drupal_static_fast['functions'] = &drupal_static(__FUNCTION__);
 948    }
 949    $functions = &$drupal_static_fast['functions'];
 950  
 951    // Most of the time, $type is passed as a string, so for performance,
 952    // normalize it to that. When passed as an array, usually the first item in
 953    // the array is a generic type, and additional items in the array are more
 954    // specific variants of it, as in the case of array('form', 'form_FORM_ID').
 955    if (is_array($type)) {
 956      $cid = implode(',', $type);
 957      $extra_types = $type;
 958      $type = array_shift($extra_types);
 959      // Allow if statements in this function to use the faster isset() rather
 960      // than !empty() both when $type is passed as a string, or as an array with
 961      // one item.
 962      if (empty($extra_types)) {
 963        unset($extra_types);
 964      }
 965    }
 966    else {
 967      $cid = $type;
 968    }
 969  
 970    // Some alter hooks are invoked many times per page request, so statically
 971    // cache the list of functions to call, and on subsequent calls, iterate
 972    // through them quickly.
 973    if (!isset($functions[$cid])) {
 974      $functions[$cid] = array();
 975      $hook = $type . '_alter';
 976      $modules = module_implements($hook);
 977      if (!isset($extra_types)) {
 978        // For the more common case of a single hook, we do not need to call
 979        // function_exists(), since module_implements() returns only modules with
 980        // implementations.
 981        foreach ($modules as $module) {
 982          $functions[$cid][] = $module . '_' . $hook;
 983        }
 984      }
 985      else {
 986        // For multiple hooks, we need $modules to contain every module that
 987        // implements at least one of them.
 988        $extra_modules = array();
 989        foreach ($extra_types as $extra_type) {
 990          $extra_modules = array_merge($extra_modules, module_implements($extra_type . '_alter'));
 991        }
 992        // If any modules implement one of the extra hooks that do not implement
 993        // the primary hook, we need to add them to the $modules array in their
 994        // appropriate order. module_implements() can only return ordered
 995        // implementations of a single hook. To get the ordered implementations
 996        // of multiple hooks, we mimic the module_implements() logic of first
 997        // ordering by module_list(), and then calling
 998        // drupal_alter('module_implements').
 999        if (array_diff($extra_modules, $modules)) {
1000          // Merge the arrays and order by module_list().
1001          $modules = array_intersect(module_list(), array_merge($modules, $extra_modules));
1002          // Since module_implements() already took care of loading the necessary
1003          // include files, we can safely pass FALSE for the array values.
1004          $implementations = array_fill_keys($modules, FALSE);
1005          // Let modules adjust the order solely based on the primary hook. This
1006          // ensures the same module order regardless of whether this if block
1007          // runs. Calling drupal_alter() recursively in this way does not result
1008          // in an infinite loop, because this call is for a single $type, so we
1009          // won't end up in this code block again.
1010          drupal_alter('module_implements', $implementations, $hook);
1011          $modules = array_keys($implementations);
1012        }
1013        foreach ($modules as $module) {
1014          // Since $modules is a merged array, for any given module, we do not
1015          // know whether it has any particular implementation, so we need a
1016          // function_exists().
1017          $function = $module . '_' . $hook;
1018          if (function_exists($function)) {
1019            $functions[$cid][] = $function;
1020          }
1021          foreach ($extra_types as $extra_type) {
1022            $function = $module . '_' . $extra_type . '_alter';
1023            if (function_exists($function)) {
1024              $functions[$cid][] = $function;
1025            }
1026          }
1027        }
1028      }
1029      // Allow the theme to alter variables after the theme system has been
1030      // initialized.
1031      global $theme, $base_theme_info;
1032      if (isset($theme)) {
1033        $theme_keys = array();
1034        foreach ($base_theme_info as $base) {
1035          $theme_keys[] = $base->name;
1036        }
1037        $theme_keys[] = $theme;
1038        foreach ($theme_keys as $theme_key) {
1039          $function = $theme_key . '_' . $hook;
1040          if (function_exists($function)) {
1041            $functions[$cid][] = $function;
1042          }
1043          if (isset($extra_types)) {
1044            foreach ($extra_types as $extra_type) {
1045              $function = $theme_key . '_' . $extra_type . '_alter';
1046              if (function_exists($function)) {
1047                $functions[$cid][] = $function;
1048              }
1049            }
1050          }
1051        }
1052      }
1053    }
1054  
1055    foreach ($functions[$cid] as $function) {
1056      $function($data, $context1, $context2);
1057    }
1058  }

title

Description

title

Description

title

Description

title

title

Body