InstallStorage.php 8.5 KB

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