Code coverage for /20081101/includes/module.inc

Line #Times calledCode
1
<?php
2
// $Id: module.inc,v 1.132 2008/10/31 02:18:22 dries Exp $
3
4
/**
5
 * @file
6
 * API for loading and interacting with Drupal modules.
7
 */
8
9
/**
10
 * Pass this to module_implements when its cache needs to be written.
11
 */
122367
define('MODULE_IMPLEMENTS_WRITE_CACHE', -1);
13
14
/**
15
 * Pass this to module_implements when its cache needs to be cleared.
16
 */
172367
define('MODULE_IMPLEMENTS_CLEAR_CACHE', -2);
18
19
20
/**
21
 * Load all the modules that have been enabled in the system table.
22
 */
232367
function module_load_all() {
242366
  foreach (module_list(TRUE, FALSE) as $module) {
252366
    drupal_load('module', $module);
262366
  }
272366
}
28
29
/**
30
 * Collect a list of all loaded modules. During the bootstrap, return only
31
 * vital modules. See bootstrap.inc
32
 *
33
 * @param $refresh
34
 *   Whether to force the module list to be regenerated (such as after the
35
 *   administrator has changed the system settings).
36
 * @param $bootstrap
37
 *   Whether to return the reduced set of modules loaded in "bootstrap
mode"
38
 *   for cached pages. See bootstrap.inc.
39
 * @param $sort
40
 *   By default, modules are ordered by weight and filename. Set this
option to
41
 *   TRUE to return a module list ordered only by module name.
42
 * @param $fixed_list
43
 *   (Optional) Override the module list with the given modules. Stays
until the
44
 *   next call with $refresh = TRUE.
45
 * @return
46
 *   An associative array whose keys and values are the names of all loaded
47
 *   modules.
48
 */
492367
function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE,
$fixed_list = NULL) {
502497
  static $list = array(), $sorted_list;
51
522497
  if (empty($list) || $refresh || $fixed_list) {
532497
    unset($sorted_list);
542497
    $list = array();
552497
    if ($fixed_list) {
560
      foreach ($fixed_list as $name => $module) {
570
        drupal_get_filename('module', $name, $module['filename']);
580
        $list[$name] = $name;
590
      }
600
    }
61
    else {
622497
      if ($bootstrap) {
632497
        $result = db_query("SELECT name, filename FROM {system} WHERE type
= 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename
ASC");
642497
      }
65
      else {
662496
        $result = db_query("SELECT name, filename FROM {system} WHERE type
= 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
67
      }
682497
      while ($module = db_fetch_object($result)) {
692496
        if (file_exists($module->filename)) {
702496
          drupal_get_filename('module', $module->name, $module->filename);
712496
          $list[$module->name] = $module->name;
722496
        }
732496
      }
74
    }
752497
  }
762497
  if ($sort) {
7730
    if (!isset($sorted_list)) {
7830
      $sorted_list = $list;
7930
      ksort($sorted_list);
8030
    }
8130
    return $sorted_list;
820
  }
832497
  return $list;
840
}
85
86
/**
87
 * Rebuild the database cache of module files.
88
 *
89
 * @return
90
 *   The array of filesystem objects used to rebuild the cache.
91
 */
922367
function module_rebuild_cache() {
93
  // Get current list of modules
94154
  $files = drupal_system_listing('/\.module$/', 'modules', 'name', 0);
95
96
  // Extract current files from database.
97154
  system_get_files_database($files, 'module');
98
99154
  ksort($files);
100
101
  // Set defaults for module info
102
  $defaults = array(
103154
    'dependencies' => array(),
104154
    'dependents' => array(),
105154
    'description' => '',
106154
    'package' => 'Other',
107154
    'version' => NULL,
108154
    'php' => DRUPAL_MINIMUM_PHP,
109154
    'files' => array(),
110154
  );
111
112154
  foreach ($files as $filename => $file) {
113
    // Look for the info file.
114154
    $file->info = drupal_parse_info_file(dirname($file->filename) . '/' .
$file->name . '.info');
115
116
    // Skip modules that don't provide info.
117154
    if (empty($file->info)) {
1180
      unset($files[$filename]);
1190
      continue;
1200
    }
121
    // Merge in defaults and save.
122154
    $files[$filename]->info = $file->info + $defaults;
123
124
    // Invoke hook_system_info_alter() to give installed modules a chance
to
125
    // modify the data in the .info files if necessary.
126154
    drupal_alter('system_info', $files[$filename]->info,
$files[$filename]);
127
128
    // Log the critical hooks implemented by this module.
129154
    $bootstrap = 0;
130154
    foreach (bootstrap_hooks() as $hook) {
131154
      if (module_hook($file->name, $hook)) {
1320
        $bootstrap = 1;
1330
        break;
1340
      }
135154
    }
136
137
    // Update the contents of the system table:
138154
    if (isset($file->status) || (isset($file->old_filename) &&
$file->old_filename != $file->filename)) {
139154
      db_query("UPDATE {system} SET info = '%s', name = '%s', filename =
'%s', bootstrap = %d WHERE filename = '%s'",
serialize($files[$filename]->info), $file->name, $file->filename,
$bootstrap, $file->old_filename);
140154
    }
141
    else {
142
      // This is a new module.
143134
      $files[$filename]->status = 0;
144134
      db_query("INSERT INTO {system} (name, info, type, filename, status,
bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d)", $file->name,
serialize($files[$filename]->info), 'module', $file->filename, 0,
$bootstrap);
145
    }
146154
  }
147154
  $files = _module_build_dependencies($files);
148154
  return $files;
1490
}
150
151
/**
152
 * Find dependencies any level deep and fill in dependents information too.
153
 *
154
 * If module A depends on B which in turn depends on C then this function
will
155
 * add C to the list of modules A depends on. This will be repeated until
156
 * module A has a list of all modules it depends on. If it depends on
itself,
157
 * called a circular dependency, that's marked by adding a nonexistent
module,
158
 * called -circular- to this list of modules. Because this does not exist,
159
 * it'll be impossible to switch module A on.
160
 *
161
 * Also we fill in a dependents array in $file->info. Using the names
above,
162
 * the dependents array of module B lists A.
163
 *
164
 * @param $files
165
 *   The array of filesystem objects used to rebuild the cache.
166
 * @return
167
 *   The same array with dependencies and dependents added where
applicable.
168
 */
1692367
function _module_build_dependencies($files) {
170
  do {
171154
    $new_dependency = FALSE;
172154
    foreach ($files as $filename => $file) {
173
      // We will modify this object (module A, see doxygen for module A, B,
C).
174154
      $file = &$files[$filename];
175154
      if (isset($file->info['dependencies']) &&
is_array($file->info['dependencies'])) {
176154
        foreach ($file->info['dependencies'] as $dependency_name) {
177
          // This is a nonexistent module.
178154
          if ($dependency_name == '-circular-' ||
!isset($files[$dependency_name])) {
1790
            continue;
1800
          }
181
          // $dependency_name is module B (again, see doxygen).
182154
          $files[$dependency_name]->info['dependents'][$filename] =
$filename;
183154
          $dependency = $files[$dependency_name];
184154
          if (isset($dependency->info['dependencies']) &&
is_array($dependency->info['dependencies'])) {
185
            // Let's find possible C modules.
186154
            foreach ($dependency->info['dependencies'] as $candidate) {
1870
              if (array_search($candidate, $file->info['dependencies']) ===
FALSE) {
188
                // Is this a circular dependency?
1890
                if ($candidate == $filename) {
190
                  // As a module name can not contain dashes, this makes
191
                  // impossible to switch on the module.
1920
                  $candidate = '-circular-';
193
                  // Do not display the message or add -circular- more than
once.
1940
                  if (array_search($candidate, $file->info['dependencies'])
!== FALSE) {
1950
                    continue;
1960
                  }
1970
                  drupal_set_message(t('%module is part of a circular
dependency. This is not supported and you will not be able to switch it
on.', array('%module' => $file->info['name'])), 'error');
1980
                }
199
                else {
200
                  // We added a new dependency to module A. The next loop
will
201
                  // be able to use this as "B module" thus finding even
202
                  // deeper dependencies.
2030
                  $new_dependency = TRUE;
204
                }
2050
                $file->info['dependencies'][] = $candidate;
2060
              }
2070
            }
208154
          }
209154
        }
210154
      }
211
      // Don't forget to break the reference.
212154
      unset($file);
213154
    }
2140
  } while ($new_dependency);
215154
  return $files;
2160
}
217
218
/**
219
 * Determine whether a given module exists.
220
 *
221
 * @param $module
222
 *   The name of the module (without the .module extension).
223
 * @return
224
 *   TRUE if the module is both installed and enabled.
225
 */
2262367
function module_exists($module) {
2271792
  $list = module_list();
2281792
  return isset($list[$module]);
2290
}
230
231
/**
232
 * Load a module's installation hooks.
233
 */
2342367
function module_load_install($module) {
235
  // Make sure the installation API is available
236143
  include_once DRUPAL_ROOT . '/includes/install.inc';
237
238143
  module_load_include('install', $module);
239143
}
240
241
/**
242
 * Load a module include file.
243
 *
244
 * @param $type
245
 *   The include file's type (file extension).
246
 * @param $module
247
 *   The module to which the include file belongs.
248
 * @param $name
249
 *   Optionally, specify the file name. If not set, the module's name is
used.
250
 */
2512367
function module_load_include($type, $module, $name = NULL) {
252146
  if (empty($name)) {
253143
    $name = $module;
254143
  }
255
256146
  if (drupal_function_exists('drupal_get_path')) {
257146
    $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) .
"/$name.$type";
258146
    if (is_file($file)) {
259146
      require_once $file;
260146
      return $file;
2610
    }
262138
  }
263138
  return FALSE;
2640
}
265
266
/**
267
 * Load an include file for each of the modules that have been enabled in
268
 * the system table.
269
 */
2702367
function module_load_all_includes($type, $name = NULL) {
271134
  $modules = module_list();
272134
  foreach ($modules as $module) {
273134
    module_load_include($type, $module, $name);
274134
  }
275134
}
276
277
/**
278
 * Enable a given list of modules.
279
 *
280
 * @param $module_list
281
 *   An array of module names.
282
 */
2832367
function module_enable($module_list) {
284136
  $invoke_modules = array();
285136
  foreach ($module_list as $module) {
286136
    $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE
type = '%s' AND name = '%s'", 'module', $module));
287136
    if ($existing->status == 0) {
288136
      module_load_install($module);
289136
      db_query("UPDATE {system} SET status = %d WHERE type = '%s' AND name
= '%s'", 1, 'module', $module);
290136
      drupal_load('module', $module);
291136
      $invoke_modules[] = $module;
292136
    }
293136
  }
294
295136
  if (!empty($invoke_modules)) {
296
    // Refresh the module list to include the new enabled module.
297136
    module_list(TRUE, FALSE);
298
    // Force to regenerate the stored list of hook implementations.
299136
    registry_rebuild();
300136
  }
301
302136
  foreach ($invoke_modules as $module) {
303136
    module_invoke($module, 'enable');
304
    // Check if node_access table needs rebuilding.
305
    // We check for the existence of node_access_needs_rebuild() since
306
    // at install time, module_enable() could be called while node.module
307
    // is not enabled yet.
308136
    if (drupal_function_exists('node_access_needs_rebuild') &&
!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
3090
      node_access_needs_rebuild(TRUE);
3100
    }
311136
  }
312
313136
  if (!empty($invoke_modules)) {
314
    // Invoke the hook_module_enable after all the modules have been
315
    // enabled.
316136
    module_invoke_all('modules_enabled', $invoke_modules);
317136
  }
318136
}
319
320
/**
321
 * Disable a given set of modules.
322
 *
323
 * @param $module_list
324
 *   An array of module names.
325
 */
3262367
function module_disable($module_list) {
3271
  $invoke_modules = array();
3281
  foreach ($module_list as $module) {
3291
    if (module_exists($module)) {
330
      // Check if node_access table needs rebuilding.
3311
      if (!node_access_needs_rebuild() && module_hook($module,
'node_grants')) {
3320
        node_access_needs_rebuild(TRUE);
3330
      }
334
3351
      module_load_install($module);
3361
      module_invoke($module, 'disable');
3371
      db_query("UPDATE {system} SET status = %d WHERE type = '%s' AND name
= '%s'", 0, 'module', $module);
3381
      $invoke_modules[] = $module;
3391
    }
3401
  }
341
3421
  if (!empty($invoke_modules)) {
343
    // Invoke hook_module_disable before disabling modules,
344
    // so we can still call module hooks to get information.
3451
    module_invoke_all('modules_disabled', $invoke_modules);
346
    // Refresh the module list to exclude the disabled modules.
3471
    module_list(TRUE, FALSE);
348
    // Force to regenerate the stored list of hook implementations.
3491
    registry_rebuild();
3501
  }
351
352
  // If there remains no more node_access module, rebuilding will be
353
  // straightforward, we can do it right now.
3541
  if (node_access_needs_rebuild() &&
count(module_implements('node_grants')) == 0) {
3550
    node_access_rebuild();
3560
  }
3571
}
358
359
/**
360
 * @defgroup hooks Hooks
361
 * @{
362
 * Allow modules to interact with the Drupal core.
363
 *
364
 * Drupal's module system is based on the concept of "hooks". A hook is a
PHP
365
 * function that is named foo_bar(), where "foo" is the name of the module
(whose
366
 * filename is thus foo.module) and "bar" is the name of the hook. Each
hook has
367
 * a defined set of parameters and a specified result type.
368
 *
369
 * To extend Drupal, a module need simply implement a hook. When Drupal
wishes to
370
 * allow intervention from modules, it determines which modules implement a
hook
371
 * and call that hook in all enabled modules that implement it.
372
 *
373
 * The available hooks to implement are explained here in the Hooks section
of
374
 * the developer documentation. The string "hook" is used as a placeholder
for
375
 * the module name is the hook definitions. For example, if the module file
is
376
 * called example.module, then hook_help() as implemented by that module
would be
377
 * defined as example_help().
378
 */
379
380
/**
381
 * Determine whether a module implements a hook.
382
 *
383
 * @param $module
384
 *   The name of the module (without the .module extension).
385
 * @param $hook
386
 *   The name of the hook (e.g. "help" or "menu").
387
 * @return
388
 *   TRUE if the module is both installed and enabled, and the hook is
389
 *   implemented in that module.
390
 */
3912367
function module_hook($module, $hook) {
3922482
  $function = $module . '_' . $hook;
3932482
  if (defined('MAINTENANCE_MODE')) {
3940
    return function_exists($function);
3950
  }
396
  else {
3972482
    return drupal_function_exists($function);
398
  }
3990
}
400
401
/**
402
 * Determine which modules are implementing a hook.
403
 *
404
 * @param $hook
405
 *   The name of the hook (e.g. "help" or "menu"). Special cases:
406
 *     MODULE_IMPLEMENTS_CLEAR_CACHE: Force the stored list of hook
407
 *   implementations to be regenerated (such as after enabling a new
module,
408
 *     before processing hook_enable).
409
 *     MODULE_IMPLEMENTS_WRITE_CACHE: Write the stored list of hook
410
 *     implementations into the cache_registry table.
411
 * @param $sort
412
 *   By default, modules are ordered by weight and filename.  By setting
this
413
 *   option to TRUE, modules will be ordered by module name.
414
 * @return
415
 *   An array with the names of the modules which are implementing this
hook.
416
 *   All enabled modules are taken into consideration and the files
containing
417
 *   the implementations are loaded as necessary.
418
 */
4192367
function module_implements($hook, $sort = FALSE) {
4202496
  static $implementations = array(), $sorted_implementations = array(),
$loaded = array(), $cached_hooks = 0;
421
4222496
  if (defined('MAINTENANCE_MODE')) {
4230
    return _module_implements_maintenance($hook, $sort);
4240
  }
4252496
  if ($hook === MODULE_IMPLEMENTS_CLEAR_CACHE) {
426146
    $implementations = array();
427146
    $sorted_implementations = array();
428146
    $loaded = array();
429146
    $cached_hooks = 0;
430146
    cache_clear_all('hooks', 'cache_registry');
431146
    return;
4320
  }
4332496
  if ($hook === MODULE_IMPLEMENTS_WRITE_CACHE) {
434
    // Only write this to cache if we loaded new implementations.
4351756
    if (count($implementations) > $cached_hooks) {
436551
      cache_set('hooks', $implementations, 'cache_registry');
437551
    }
4381756
    return;
4390
  }
440
4412496
  if (!isset($loaded[$hook])) {
4422496
    if (empty($implementations) && ($cache = cache_get('hooks',
'cache_registry'))) {
4432252
      $implementations = $cache->data;
4442252
      $cached_hooks = count($implementations);
4452252
    }
4462496
    if (!isset($implementations[$hook])) {
4471634
      $implementations[$hook] = db_query("SELECT module FROM {registry}
WHERE type = 'function' AND suffix = :hook ORDER BY weight, module",
array(':hook' => $hook))->fetchCol();
4481634
    }
4492496
    foreach ($implementations[$hook] as $module) {
4502496
      $function = $module . '_' . $hook;
4512496
      if (!function_exists($function)) {
4526
        drupal_function_exists($function);
4536
      }
4542496
    }
4552496
    $loaded[$hook] = TRUE;
4562496
  }
457
4582496
  if ($sort) {
459718
    if (!isset($sorted_implementations[$hook])) {
460718
      $sorted_implementations[$hook] = $implementations[$hook];
461718
      sort($sorted_implementations[$hook]);
462718
    }
463718
    return $sorted_implementations[$hook];
4640
  }
465
  else {
4662496
    return $implementations[$hook];
467
  }
4680
}
469
470
/**
471
 * This is the maintenance version of module_implements for internal use
only.
472
 *
473
 * This function is called whenever MAINTENANCE_MODE is defined and is a
474
 * safe code path for Drupal installation or upgrade because it does not
use
475
 * the database, instead it uses module_list. @see module_list $fixed_list
on
476
 * how to make module_list also DB independent.
477
 *
478
 * @param $hook
479
 *   The name of the hook (e.g. "help" or "menu").
480
 * @param $sort
481
 *   By default, modules are ordered by weight and filename, settings this
482
 *   option to TRUE, module list will be ordered by module name.
483
 * @return
484
 *   An array with the names of the modules which are implementing this
hook.
485
 *   Only enabled and already loaded modules are taken into consideration.
486
 */
4872367
function _module_implements_maintenance($hook, $sort = FALSE) {
4880
  $implementations = array();
4890
  foreach (module_list() as $module) {
4900
    $function = $module . '_' . $hook;
4910
    if (function_exists($function)) {
4920
      $implementations[] = $module;
4930
    }
4940
    if ($sort) {
4950
      sort($implementations);
4960
    }
4970
  }
4980
  return $implementations;
4990
}
500
501
/**
502
 * Invoke a hook in a particular module.
503
 *
504
 * @param $module
505
 *   The name of the module (without the .module extension).
506
 * @param $hook
507
 *   The name of the hook to invoke.
508
 * @param ...
509
 *   Arguments to pass to the hook implementation.
510
 * @return
511
 *   The return value of the hook implementation.
512
 */
5132367
function module_invoke() {
5142482
  $args = func_get_args();
5152482
  $module = $args[0];
5162482
  $hook = $args[1];
5172482
  unset($args[0], $args[1]);
5182482
  if (module_hook($module, $hook)) {
5192482
    return call_user_func_array($module . '_' . $hook, $args);
5200
  }
521311
}
522
/**
523
 * Invoke a hook in all enabled modules that implement it.
524
 *
525
 * @param $hook
526
 *   The name of the hook to invoke.
527
 * @param ...
528
 *   Arguments to pass to the hook.
529
 * @return
530
 *   An array of return values of the hook implementations. If modules
return
531
 *   arrays from their implementations, those are merged into one array.
532
 */
5332367
function module_invoke_all() {
5342496
  $args = func_get_args();
5352496
  $hook = $args[0];
5362496
  unset($args[0]);
5372496
  $return = array();
5382496
  foreach (module_implements($hook) as $module) {
5392496
    $function = $module . '_' . $hook;
5402496
    if (drupal_function_exists($function)) {
5412496
      $result = call_user_func_array($function, $args);
5422496
      if (isset($result) && is_array($result)) {
543859
        $return = array_merge_recursive($return, $result);
544859
      }
5452496
      elseif (isset($result)) {
5460
        $return[] = $result;
5470
      }
5482496
    }
5492496
  }
550
5512496
  return $return;
5520
}
553
554
/**
555
 * @} End of "defgroup hooks".
556
 */
557
558
/**
559
 * Array of modules required by core.
560
 */
5612367
function drupal_required_modules() {
562134
  $files = drupal_system_listing('/\.info$/', 'modules', 'name', 0);
563134
  $required = array();
564134
  foreach ($files as $name => $file) {
565134
    $info = drupal_parse_info_file($file->filename);
566134
    if (!empty($info) && !empty($info['required']) && $info['required']) {
567134
      $required[] = $name;
568134
    }
569134
  }
570134
  return $required;
5710
}
5722367