Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / Config / InstallStorage.php
1 <?php
2
3 namespace Drupal\Core\Config;
4
5 use Drupal\Core\Extension\ExtensionDiscovery;
6 use Drupal\Core\Extension\Extension;
7
8 /**
9  * Storage used by the Drupal installer.
10  *
11  * This storage performs a full filesystem scan to discover all available
12  * extensions and reads from all default config directories that exist.
13  *
14  * This special implementation MUST NOT be used anywhere else than the early
15  * installer environment.
16  *
17  * @see \Drupal\Core\DependencyInjection\InstallServiceProvider
18  */
19 class InstallStorage extends FileStorage {
20
21   /**
22    * Extension sub-directory containing default configuration for installation.
23    */
24   const CONFIG_INSTALL_DIRECTORY = 'config/install';
25
26   /**
27    * Extension sub-directory containing optional configuration for installation.
28    */
29   const CONFIG_OPTIONAL_DIRECTORY = 'config/optional';
30
31   /**
32    * Extension sub-directory containing configuration schema.
33    */
34   const CONFIG_SCHEMA_DIRECTORY = 'config/schema';
35
36   /**
37    * Folder map indexed by configuration name.
38    *
39    * @var array
40    */
41   protected $folders;
42
43   /**
44    * The directory to scan in each extension to scan for files.
45    *
46    * @var string
47    */
48   protected $directory;
49
50   /**
51    * Constructs an InstallStorage object.
52    *
53    * @param string $directory
54    *   The directory to scan in each extension to scan for files. Defaults to
55    *   'config/install'.
56    * @param string $collection
57    *   (optional) The collection to store configuration in. Defaults to the
58    *   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    * Overrides Drupal\Core\Config\FileStorage::getFilePath().
66    *
67    * Returns the path to the configuration file.
68    *
69    * Determines the owner and path to the default configuration file of a
70    * requested config object name located in the installation profile, a module,
71    * or a theme (in this order).
72    *
73    * @return string
74    *   The path to the configuration file.
75    *
76    * @todo Improve this when figuring out how we want to handle configuration in
77    *   installation profiles. For instance, a config object actually has to be
78    *   searched in the profile first (whereas the profile is never the owner);
79    *   only afterwards check for a corresponding module or theme.
80    */
81   public function getFilePath($name) {
82     $folders = $this->getAllFolders();
83     if (isset($folders[$name])) {
84       return $folders[$name] . '/' . $name . '.' . $this->getFileExtension();
85     }
86     // If any code in the early installer requests a configuration object that
87     // does not exist anywhere as default config, then that must be mistake.
88     throw new StorageException("Missing configuration file: $name");
89   }
90
91   /**
92    * {@inheritdoc}
93    */
94   public function exists($name) {
95     return array_key_exists($name, $this->getAllFolders());
96   }
97
98   /**
99    * Overrides Drupal\Core\Config\FileStorage::write().
100    *
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    * Overrides Drupal\Core\Config\FileStorage::delete().
109    *
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    * Overrides Drupal\Core\Config\FileStorage::rename().
118    *
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     $names = array_keys($this->getAllFolders());
130     if (!$prefix) {
131       return $names;
132     }
133     else {
134       $return = [];
135       foreach ($names as $index => $name) {
136         if (strpos($name, $prefix) === 0) {
137           $return[$index] = $names[$index];
138         }
139       }
140       return $return;
141     }
142   }
143
144   /**
145    * Returns a map of all config object names and their folders.
146    *
147    * @return array
148    *   An array mapping config object names with directories.
149    */
150   protected function getAllFolders() {
151     if (!isset($this->folders)) {
152       $this->folders = [];
153       $this->folders += $this->getCoreNames();
154       // Perform an ExtensionDiscovery scan as we cannot use drupal_get_path()
155       // yet because the system module may not yet be enabled during install.
156       // @todo Remove as part of https://www.drupal.org/node/2186491
157       $listing = new ExtensionDiscovery(\Drupal::root());
158       if ($profile = drupal_get_profile()) {
159         $profile_list = $listing->scan('profile');
160         if (isset($profile_list[$profile])) {
161           // Prime the drupal_get_filename() static cache with the profile info
162           // file location so we can use drupal_get_path() on the active profile
163           // during the module scan.
164           // @todo Remove as part of https://www.drupal.org/node/2186491
165           drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
166           $this->folders += $this->getComponentNames([$profile_list[$profile]]);
167         }
168       }
169       // @todo Remove as part of https://www.drupal.org/node/2186491
170       $this->folders += $this->getComponentNames($listing->scan('module'));
171       $this->folders += $this->getComponentNames($listing->scan('theme'));
172     }
173     return $this->folders;
174   }
175
176   /**
177    * Get all configuration names and folders for a list of modules or themes.
178    *
179    * @param \Drupal\Core\Extension\Extension[] $list
180    *   An associative array of Extension objects, keyed by extension name.
181    *
182    * @return array
183    *   Folders indexed by configuration name.
184    */
185   public function getComponentNames(array $list) {
186     $extension = '.' . $this->getFileExtension();
187     $pattern = '/' . preg_quote($extension, '/') . '$/';
188     $folders = [];
189     foreach ($list as $extension_object) {
190       // We don't have to use ExtensionDiscovery here because our list of
191       // extensions was already obtained through an ExtensionDiscovery scan.
192       $directory = $this->getComponentFolder($extension_object);
193       if (is_dir($directory)) {
194         // glob() directly calls into libc glob(), which is not aware of PHP
195         // stream wrappers. Same for \GlobIterator (which additionally requires
196         // an absolute realpath() on Windows).
197         // @see https://github.com/mikey179/vfsStream/issues/2
198         $files = scandir($directory);
199
200         foreach ($files as $file) {
201           if ($file[0] !== '.' && preg_match($pattern, $file)) {
202             $folders[basename($file, $extension)] = $directory;
203           }
204         }
205       }
206     }
207     return $folders;
208   }
209
210   /**
211    * Get all configuration names and folders for Drupal core.
212    *
213    * @return array
214    *   Folders indexed by configuration name.
215    */
216   public function getCoreNames() {
217     $extension = '.' . $this->getFileExtension();
218     $pattern = '/' . preg_quote($extension, '/') . '$/';
219     $folders = [];
220     $directory = $this->getCoreFolder();
221     if (is_dir($directory)) {
222       // glob() directly calls into libc glob(), which is not aware of PHP
223       // stream wrappers. Same for \GlobIterator (which additionally requires an
224       // absolute realpath() on Windows).
225       // @see https://github.com/mikey179/vfsStream/issues/2
226       $files = scandir($directory);
227
228       foreach ($files as $file) {
229         if ($file[0] !== '.' && preg_match($pattern, $file)) {
230           $folders[basename($file, $extension)] = $directory;
231         }
232       }
233     }
234     return $folders;
235   }
236
237   /**
238    * Get folder inside each component that contains the files.
239    *
240    * @param \Drupal\Core\Extension\Extension $extension
241    *   The Extension object for the component.
242    *
243    * @return string
244    *   The configuration folder name for this component.
245    */
246   protected function getComponentFolder(Extension $extension) {
247     return $extension->getPath() . '/' . $this->getCollectionDirectory();
248   }
249
250   /**
251    * Get folder inside Drupal core that contains the files.
252    *
253    * @return string
254    *   The configuration folder name for core.
255    */
256   protected function getCoreFolder() {
257     return drupal_get_path('core', 'core') . '/' . $this->getCollectionDirectory();
258   }
259
260   /**
261    * Overrides Drupal\Core\Config\FileStorage::deleteAll().
262    *
263    * @throws \Drupal\Core\Config\StorageException
264    */
265   public function deleteAll($prefix = '') {
266     throw new StorageException('Delete operation is not allowed.');
267   }
268
269   /**
270    * Resets the static cache.
271    */
272   public function reset() {
273     $this->folders = NULL;
274   }
275
276 }