InstallStorage.php 8.53 KB
Newer Older
1 2 3 4
<?php

namespace Drupal\Core\Config;

5
use Drupal\Core\Extension\ExtensionDiscovery;
6
use Drupal\Core\Extension\Extension;
7

8
/**
9
 * Storage used by the Drupal installer.
10
 *
11 12 13 14 15 16 17
 * This storage performs a full filesystem scan to discover all available
 * extensions and reads from all default config directories that exist.
 *
 * This special implementation MUST NOT be used anywhere else than the early
 * installer environment.
 *
 * @see \Drupal\Core\DependencyInjection\InstallServiceProvider
18 19 20
 */
class InstallStorage extends FileStorage {

21 22 23 24 25
  /**
   * Extension sub-directory containing default configuration for installation.
   */
  const CONFIG_INSTALL_DIRECTORY = 'config/install';

26 27 28 29 30
  /**
   * Extension sub-directory containing optional configuration for installation.
   */
  const CONFIG_OPTIONAL_DIRECTORY = 'config/optional';

31 32 33 34 35
  /**
   * Extension sub-directory containing configuration schema.
   */
  const CONFIG_SCHEMA_DIRECTORY = 'config/schema';

36 37 38 39 40 41 42
  /**
   * Folder map indexed by configuration name.
   *
   * @var array
   */
  protected $folders;

43
  /**
44 45 46
   * The directory to scan in each extension to scan for files.
   *
   * @var string
47
   */
48 49 50 51 52 53 54
  protected $directory;

  /**
   * Constructs an InstallStorage object.
   *
   * @param string $directory
   *   The directory to scan in each extension to scan for files. Defaults to
55 56 57 58
   *   'config/install'.
   * @param string $collection
   *   (optional) The collection to store configuration in. Defaults to the
   *   default collection.
59
   */
60
  public function __construct($directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION) {
61
    parent::__construct($directory, $collection);
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  }

  /**
   * Overrides Drupal\Core\Config\FileStorage::getFilePath().
   *
   * Returns the path to the configuration file.
   *
   * Determines the owner and path to the default configuration file of a
   * requested config object name located in the installation profile, a module,
   * or a theme (in this order).
   *
   * @return string
   *   The path to the configuration file.
   *
   * @todo Improve this when figuring out how we want to handle configuration in
77 78 79
   *   installation profiles. For instance, a config object actually has to be
   *   searched in the profile first (whereas the profile is never the owner);
   *   only afterwards check for a corresponding module or theme.
80 81
   */
  public function getFilePath($name) {
82 83 84
    $folders = $this->getAllFolders();
    if (isset($folders[$name])) {
      return $folders[$name] . '/' . $name . '.' . $this->getFileExtension();
85 86 87
    }
    // If any code in the early installer requests a configuration object that
    // does not exist anywhere as default config, then that must be mistake.
88
    throw new StorageException("Missing configuration file: $name");
89 90
  }

91 92 93 94 95 96 97
  /**
   * {@inheritdoc}
   */
  public function exists($name) {
    return array_key_exists($name, $this->getAllFolders());
  }

98 99 100
  /**
   * Overrides Drupal\Core\Config\FileStorage::write().
   *
101
   * @throws \Drupal\Core\Config\StorageException
102 103
   */
  public function write($name, array $data) {
104
    throw new StorageException('Write operation is not allowed.');
105 106 107 108 109
  }

  /**
   * Overrides Drupal\Core\Config\FileStorage::delete().
   *
110
   * @throws \Drupal\Core\Config\StorageException
111 112
   */
  public function delete($name) {
113
    throw new StorageException('Delete operation is not allowed.');
114 115 116 117 118
  }

  /**
   * Overrides Drupal\Core\Config\FileStorage::rename().
   *
119
   * @throws \Drupal\Core\Config\StorageException
120 121
   */
  public function rename($name, $new_name) {
122
    throw new StorageException('Rename operation is not allowed.');
123 124 125
  }

  /**
126
   * {@inheritdoc}
127 128
   */
  public function listAll($prefix = '') {
129 130 131 132 133 134 135 136 137
    $names = array_keys($this->getAllFolders());
    if (!$prefix) {
      return $names;
    }
    else {
      $return = array();
      foreach ($names as $index => $name) {
        if (strpos($name, $prefix) === 0 ) {
          $return[$index] = $names[$index];
138 139
        }
      }
140
      return $return;
141
    }
142
  }
143

144 145 146 147 148 149 150 151
  /**
   * Returns a map of all config object names and their folders.
   *
   * @return array
   *   An array mapping config object names with directories.
   */
  protected function getAllFolders() {
    if (!isset($this->folders)) {
152
      $this->folders = array();
153 154 155 156 157
      $this->folders += $this->getCoreNames();
      // Perform an ExtensionDiscovery scan as we cannot use drupal_get_path()
      // yet because the system module may not yet be enabled during install.
      // @todo Remove as part of https://www.drupal.org/node/2186491
      $listing = new ExtensionDiscovery(\Drupal::root());
158
      if ($profile = drupal_get_profile()) {
159 160
        $profile_list = $listing->scan('profile');
        if (isset($profile_list[$profile])) {
161 162 163 164 165
          // Prime the drupal_get_filename() static cache with the profile info
          // file location so we can use drupal_get_path() on the active profile
          // during the module scan.
          // @todo Remove as part of https://www.drupal.org/node/2186491
          drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
166 167
          $this->folders += $this->getComponentNames(array($profile_list[$profile]));
        }
168
      }
169 170 171
      // @todo Remove as part of https://www.drupal.org/node/2186491
      $this->folders += $this->getComponentNames($listing->scan('module'));
      $this->folders += $this->getComponentNames($listing->scan('theme'));
172
    }
173 174
    return $this->folders;
  }
175

176 177 178
  /**
   * Get all configuration names and folders for a list of modules or themes.
   *
179 180
   * @param \Drupal\Core\Extension\Extension[] $list
   *   An associative array of Extension objects, keyed by extension name.
181 182 183 184
   *
   * @return array
   *   Folders indexed by configuration name.
   */
185
  public function getComponentNames(array $list) {
186
    $extension = '.' . $this->getFileExtension();
187
    $pattern = '/' . preg_quote($extension, '/') . '$/';
188
    $folders = array();
189 190 191 192
    foreach ($list as $extension_object) {
      // We don't have to use ExtensionDiscovery here because our list of
      // extensions was already obtained through an ExtensionDiscovery scan.
      $directory = $this->getComponentFolder($extension_object);
193 194 195 196 197 198 199
      if (is_dir($directory)) {
        // glob() directly calls into libc glob(), which is not aware of PHP
        // stream wrappers. Same for \GlobIterator (which additionally requires
        // an absolute realpath() on Windows).
        // @see https://github.com/mikey179/vfsStream/issues/2
        $files = scandir($directory);

200
        foreach ($files as $file) {
201
          if ($file[0] !== '.' && preg_match($pattern, $file)) {
202 203
            $folders[basename($file, $extension)] = $directory;
          }
204 205 206
        }
      }
    }
207 208
    return $folders;
  }
209

210 211 212 213 214 215 216 217
  /**
   * Get all configuration names and folders for Drupal core.
   *
   * @return array
   *   Folders indexed by configuration name.
   */
  public function getCoreNames() {
    $extension = '.' . $this->getFileExtension();
218
    $pattern = '/' . preg_quote($extension, '/') . '$/';
219 220
    $folders = array();
    $directory = $this->getCoreFolder();
221 222 223 224 225 226 227
    if (is_dir($directory)) {
      // glob() directly calls into libc glob(), which is not aware of PHP
      // stream wrappers. Same for \GlobIterator (which additionally requires an
      // absolute realpath() on Windows).
      // @see https://github.com/mikey179/vfsStream/issues/2
      $files = scandir($directory);

228
      foreach ($files as $file) {
229
        if ($file[0] !== '.' && preg_match($pattern, $file)) {
230 231
          $folders[basename($file, $extension)] = $directory;
        }
232 233 234 235 236
      }
    }
    return $folders;
  }

237 238 239
  /**
   * Get folder inside each component that contains the files.
   *
240 241
   * @param \Drupal\Core\Extension\Extension $extension
   *   The Extension object for the component.
242 243 244 245
   *
   * @return string
   *   The configuration folder name for this component.
   */
246 247 248 249 250 251 252 253 254 255 256 257
  protected function getComponentFolder(Extension $extension) {
    return $extension->getPath() . '/' . $this->getCollectionDirectory();
  }

  /**
   * Get folder inside Drupal core that contains the files.
   *
   * @return string
   *   The configuration folder name for core.
   */
  protected function getCoreFolder() {
    return drupal_get_path('core', 'core') . '/' . $this->getCollectionDirectory();
258
  }
259 260 261 262

  /**
   * Overrides Drupal\Core\Config\FileStorage::deleteAll().
   *
263
   * @throws \Drupal\Core\Config\StorageException
264 265
   */
  public function deleteAll($prefix = '') {
266 267 268 269 270 271 272 273
    throw new StorageException('Delete operation is not allowed.');
  }

  /**
   * Resets the static cache.
   */
  public function reset() {
    $this->folders = NULL;
274 275
  }

276
}