module.inc 7.67 KB
Newer Older
1 2
<?php

3 4 5 6 7
/**
 * @file
 * API for loading and interacting with Drupal modules.
 */

8
use Drupal\Core\Extension\ExtensionDiscovery;
9

10
/**
11
 * Builds a list of installed themes.
12 13
 *
 * @param $type
14
 *   The type of list to return:
15
 *   - theme: All installed themes.
16 17
 *
 * @return
18
 *   An associative array of themes, keyed by name.
19 20
 *   For $type 'theme', the array values are objects representing the
 *   respective database row, with the 'info' property already unserialized.
21
 *
22
 * @see \Drupal\Core\Extension\ThemeHandler::listInfo()
23 24 25
 */
function system_list($type) {
  $lists = &drupal_static(__FUNCTION__);
26
  if ($cached = \Drupal::cache('bootstrap')->get('system_list')) {
27
    $lists = $cached->data;
28
  }
29 30 31 32 33
  else {
    $lists = array(
      'theme' => array(),
      'filepaths' => array(),
    );
34 35
    // ThemeHandler maintains the 'system.theme.data' state record.
    $theme_data = \Drupal::state()->get('system.theme.data', array());
36 37
    foreach ($theme_data as $name => $theme) {
      $lists['theme'][$name] = $theme;
38 39 40 41 42
      $lists['filepaths'][] = array(
        'type' => 'theme',
        'name' => $name,
        'filepath' => $theme->getPathname(),
      );
43
    }
44
    \Drupal::cache('bootstrap')->set('system_list', $lists);
45 46
  }
  // To avoid a separate database lookup for the filepath, prime the
47
  // drupal_get_filename() static cache with all enabled themes.
48 49
  foreach ($lists['filepaths'] as $item) {
    system_register($item['type'], $item['name'], $item['filepath']);
50 51 52 53 54
  }

  return $lists[$type];
}

55
/**
56
 * Resets all system_list() caches.
57 58 59
 */
function system_list_reset() {
  drupal_static_reset('system_list');
60
  drupal_static_reset('system_rebuild_module_data');
61
  \Drupal::cache('bootstrap')->delete('system_list');
62 63
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/**
 * Registers an extension in runtime registries for execution.
 *
 * @param string $type
 *   The extension type; e.g., 'module' or 'theme'.
 * @param string $name
 *   The internal name of the extension; e.g., 'node'.
 * @param string $uri
 *   The relative URI of the primary extension file; e.g.,
 *   'core/modules/node/node.module'.
 */
function system_register($type, $name, $uri) {
  drupal_get_filename($type, $name, $uri);
  drupal_classloader_register($name, dirname($uri));
}

Steven Wittens's avatar
Steven Wittens committed
80
/**
81
 * Loads a module's installation hooks.
82 83 84 85 86 87
 *
 * @param $module
 *   The name of the module (without the .module extension).
 *
 * @return
 *   The name of the module's install file, if successful; FALSE otherwise.
Steven Wittens's avatar
Steven Wittens committed
88 89 90
 */
function module_load_install($module) {
  // Make sure the installation API is available
91
  include_once __DIR__ . '/install.inc';
Steven Wittens's avatar
Steven Wittens committed
92

93
  return module_load_include('install', $module);
94 95 96
}

/**
97
 * Loads a module include file.
98
 *
99 100
 * Examples:
 * @code
101
 *   // Load node.admin.inc from the node module.
102
 *   module_load_include('inc', 'node', 'node.admin');
103
 *   // Load content_types.inc from the node module.
104
 *   module_load_include('inc', 'node', 'content_types');
105
 * @endcode
106
 *
107 108 109
 * Do not use this function to load an install file, use module_load_install()
 * instead. Do not use this function in a global context since it requires
 * Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file'
110 111
 * instead.
 *
112 113 114 115 116
 * @param $type
 *   The include file's type (file extension).
 * @param $module
 *   The module to which the include file belongs.
 * @param $name
117 118
 *   (optional) The base file name (without the $type extension). If omitted,
 *   $module is used; i.e., resulting in "$module.$type" by default.
119 120 121
 *
 * @return
 *   The name of the included file, if successful; FALSE otherwise.
122 123 124 125 126
 *
 * @todo The module_handler service has a loadInclude() method which performs
 *   this same task but only for enabled modules. Figure out a way to move this
 *   functionality entirely into the module_handler while keeping the ability to
 *   load the files of disabled modules.
127 128
 */
function module_load_include($type, $module, $name = NULL) {
129
  if (!isset($name)) {
130 131 132
    $name = $module;
  }

133
  if (function_exists('drupal_get_path')) {
134 135 136 137 138
    $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
    if (is_file($file)) {
      require_once $file;
      return $file;
    }
139
  }
140
  return FALSE;
141 142
}

143
/**
144
 * Returns an array of modules required by core.
145 146
 */
function drupal_required_modules() {
147
  $listing = new ExtensionDiscovery(\Drupal::root());
148
  $files = $listing->scan('module');
149
  $required = array();
150

151 152 153 154 155 156
  // Unless called by the installer, an installation profile is required and
  // must always be loaded. drupal_get_profile() also returns the installation
  // profile in the installer, but only after it has been selected.
  if ($profile = drupal_get_profile()) {
    $required[] = $profile;
  }
157

158
  foreach ($files as $name => $file) {
159
    $info = \Drupal::service('info_parser')->parse($file->getPathname());
160 161 162 163
    if (!empty($info) && !empty($info['required']) && $info['required']) {
      $required[] = $name;
    }
  }
164

165
  return $required;
166
}
167

168 169 170 171 172 173 174 175 176 177 178
/**
 * Sets weight of a particular module.
 *
 * The weight of uninstalled modules cannot be changed.
 *
 * @param string $module
 *   The name of the module (without the .module extension).
 * @param int $weight
 *   An integer representing the weight of the module.
 */
function module_set_weight($module, $weight) {
179
  $extension_config = \Drupal::configFactory()->getEditable('core.extension');
180
  if ($extension_config->get("module.$module") !== NULL) {
181 182
    // Pre-cast the $weight to an integer so that we can save this without using
    // schema. This is a performance improvement for module installation.
183
    $extension_config
184
      ->set("module.$module", (int) $weight)
185
      ->set('module', module_config_sort($extension_config->get('module')))
186
      ->save(TRUE);
187 188

    // Prepare the new module list, sorted by weight, including filenames.
189
    // @see \Drupal\Core\Extension\ModuleHandler::install()
190
    $module_handler = \Drupal::moduleHandler();
191 192
    $current_module_filenames = $module_handler->getModuleList();
    $current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
193
    $current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
194 195 196 197 198 199
    $module_filenames = array();
    foreach ($current_modules as $name => $weight) {
      $module_filenames[$name] = $current_module_filenames[$name];
    }
    // Update the module list in the extension handler.
    $module_handler->setModuleList($module_filenames);
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    return;
  }
}

/**
 * Sorts the configured list of enabled modules.
 *
 * The list of enabled modules is expected to be ordered by weight and name.
 * The list is always sorted on write to avoid the overhead on read.
 *
 * @param array $data
 *   An array of module configuration data.
 *
 * @return array
 *   An array of module configuration data sorted by weight and name.
 */
function module_config_sort($data) {
  // PHP array sorting functions such as uasort() do not work with both keys and
  // values at the same time, so we achieve weight and name sorting by computing
  // strings with both information concatenated (weight first, name second) and
  // use that as a regular string sort reference list via array_multisort(),
  // compound of "[sign-as-integer][padded-integer-weight][name]"; e.g., given
  // two modules and weights (spaces added for clarity):
  // - Block with weight -5: 0 0000000000000000005 block
  // - Node  with weight  0: 1 0000000000000000000 node
  $sort = array();
  foreach ($data as $name => $weight) {
    // Prefix negative weights with 0, positive weights with 1.
    // +/- signs cannot be used, since + (ASCII 43) is before - (ASCII 45).
    $prefix = (int) ($weight >= 0);
    // The maximum weight is PHP_INT_MAX, so pad all weights to 19 digits.
    $sort[] = $prefix . sprintf('%019d', abs($weight)) . $name;
  }
  array_multisort($sort, SORT_STRING, $data);
  return $data;
}