PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/web/core/lib/Drupal/Core/Config/FileStorage.php

https://gitlab.com/mohamed_hussein/prodt
PHP | 372 lines | 194 code | 36 blank | 142 comment | 31 complexity | 64cb6e8846679ed9993aaf3139d10839 MD5 | raw file
  1. <?php
  2. namespace Drupal\Core\Config;
  3. use Drupal\Component\FileCache\FileCacheFactory;
  4. use Drupal\Component\FileSecurity\FileSecurity;
  5. use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
  6. use Drupal\Core\File\FileSystemInterface;
  7. use Drupal\Core\Serialization\Yaml;
  8. /**
  9. * Defines the file storage.
  10. */
  11. class FileStorage implements StorageInterface {
  12. /**
  13. * The storage collection.
  14. *
  15. * @var string
  16. */
  17. protected $collection;
  18. /**
  19. * The filesystem path for configuration objects.
  20. *
  21. * @var string
  22. */
  23. protected $directory = '';
  24. /**
  25. * The file cache object.
  26. *
  27. * @var \Drupal\Component\FileCache\FileCacheInterface
  28. */
  29. protected $fileCache;
  30. /**
  31. * Constructs a new FileStorage.
  32. *
  33. * @param string $directory
  34. * A directory path to use for reading and writing of configuration files.
  35. * @param string $collection
  36. * (optional) The collection to store configuration in. Defaults to the
  37. * default collection.
  38. */
  39. public function __construct($directory, $collection = StorageInterface::DEFAULT_COLLECTION) {
  40. $this->directory = $directory;
  41. $this->collection = $collection;
  42. // Use a NULL File Cache backend by default. This will ensure only the
  43. // internal static caching of FileCache is used and thus avoids blowing up
  44. // the APCu cache.
  45. $this->fileCache = FileCacheFactory::get('config', ['cache_backend_class' => NULL]);
  46. }
  47. /**
  48. * Returns the path to the configuration file.
  49. *
  50. * @return string
  51. * The path to the configuration file.
  52. */
  53. public function getFilePath($name) {
  54. return $this->getCollectionDirectory() . '/' . $name . '.' . static::getFileExtension();
  55. }
  56. /**
  57. * Returns the file extension used by the file storage for all configuration files.
  58. *
  59. * @return string
  60. * The file extension.
  61. */
  62. public static function getFileExtension() {
  63. return 'yml';
  64. }
  65. /**
  66. * Check if the directory exists and create it if not.
  67. */
  68. protected function ensureStorage() {
  69. $dir = $this->getCollectionDirectory();
  70. $success = $this->getFileSystem()->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
  71. // Only create .htaccess file in root directory.
  72. if ($dir == $this->directory) {
  73. $success = $success && FileSecurity::writeHtaccess($this->directory);
  74. }
  75. if (!$success) {
  76. throw new StorageException('Failed to create config directory ' . $dir);
  77. }
  78. return $this;
  79. }
  80. /**
  81. * {@inheritdoc}
  82. */
  83. public function exists($name) {
  84. return file_exists($this->getFilePath($name));
  85. }
  86. /**
  87. * Implements Drupal\Core\Config\StorageInterface::read().
  88. *
  89. * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
  90. */
  91. public function read($name) {
  92. if (!$this->exists($name)) {
  93. return FALSE;
  94. }
  95. $filepath = $this->getFilePath($name);
  96. if ($data = $this->fileCache->get($filepath)) {
  97. return $data;
  98. }
  99. $data = file_get_contents($filepath);
  100. try {
  101. $data = $this->decode($data);
  102. }
  103. catch (InvalidDataTypeException $e) {
  104. throw new UnsupportedDataTypeConfigException('Invalid data type in config ' . $name . ', found in file ' . $filepath . ': ' . $e->getMessage());
  105. }
  106. $this->fileCache->set($filepath, $data);
  107. return $data;
  108. }
  109. /**
  110. * {@inheritdoc}
  111. */
  112. public function readMultiple(array $names) {
  113. $list = [];
  114. foreach ($names as $name) {
  115. if ($data = $this->read($name)) {
  116. $list[$name] = $data;
  117. }
  118. }
  119. return $list;
  120. }
  121. /**
  122. * {@inheritdoc}
  123. */
  124. public function write($name, array $data) {
  125. try {
  126. $encoded_data = $this->encode($data);
  127. }
  128. catch (InvalidDataTypeException $e) {
  129. throw new StorageException("Invalid data type in config $name: {$e->getMessage()}");
  130. }
  131. $target = $this->getFilePath($name);
  132. $status = @file_put_contents($target, $encoded_data);
  133. if ($status === FALSE) {
  134. // Try to make sure the directory exists and try writing again.
  135. $this->ensureStorage();
  136. $status = @file_put_contents($target, $encoded_data);
  137. }
  138. if ($status === FALSE) {
  139. throw new StorageException('Failed to write configuration file: ' . $this->getFilePath($name));
  140. }
  141. else {
  142. $this->getFileSystem()->chmod($target);
  143. }
  144. $this->fileCache->set($target, $data);
  145. return TRUE;
  146. }
  147. /**
  148. * {@inheritdoc}
  149. */
  150. public function delete($name) {
  151. if (!$this->exists($name)) {
  152. return FALSE;
  153. }
  154. $this->fileCache->delete($this->getFilePath($name));
  155. return $this->getFileSystem()->unlink($this->getFilePath($name));
  156. }
  157. /**
  158. * {@inheritdoc}
  159. */
  160. public function rename($name, $new_name) {
  161. $status = @rename($this->getFilePath($name), $this->getFilePath($new_name));
  162. if ($status === FALSE) {
  163. return FALSE;
  164. }
  165. $this->fileCache->delete($this->getFilePath($name));
  166. $this->fileCache->delete($this->getFilePath($new_name));
  167. return TRUE;
  168. }
  169. /**
  170. * {@inheritdoc}
  171. */
  172. public function encode($data) {
  173. return Yaml::encode($data);
  174. }
  175. /**
  176. * {@inheritdoc}
  177. */
  178. public function decode($raw) {
  179. $data = Yaml::decode($raw);
  180. // A simple string is valid YAML for any reason.
  181. if (!is_array($data)) {
  182. return FALSE;
  183. }
  184. return $data;
  185. }
  186. /**
  187. * {@inheritdoc}
  188. */
  189. public function listAll($prefix = '') {
  190. $dir = $this->getCollectionDirectory();
  191. if (!is_dir($dir)) {
  192. return [];
  193. }
  194. $extension = '.' . static::getFileExtension();
  195. // glob() directly calls into libc glob(), which is not aware of PHP stream
  196. // wrappers. Same for \GlobIterator (which additionally requires an absolute
  197. // realpath() on Windows).
  198. // @see https://github.com/mikey179/vfsStream/issues/2
  199. $files = scandir($dir);
  200. $names = [];
  201. $pattern = '/^' . preg_quote($prefix, '/') . '.*' . preg_quote($extension, '/') . '$/';
  202. foreach ($files as $file) {
  203. if ($file[0] !== '.' && preg_match($pattern, $file)) {
  204. $names[] = basename($file, $extension);
  205. }
  206. }
  207. return $names;
  208. }
  209. /**
  210. * {@inheritdoc}
  211. */
  212. public function deleteAll($prefix = '') {
  213. $files = $this->listAll($prefix);
  214. $success = !empty($files);
  215. foreach ($files as $name) {
  216. if (!$this->delete($name) && $success) {
  217. $success = FALSE;
  218. }
  219. }
  220. if ($success && $this->collection != StorageInterface::DEFAULT_COLLECTION) {
  221. // Remove empty directories.
  222. if (!(new \FilesystemIterator($this->getCollectionDirectory()))->valid()) {
  223. $this->getFileSystem()->rmdir($this->getCollectionDirectory());
  224. }
  225. }
  226. return $success;
  227. }
  228. /**
  229. * {@inheritdoc}
  230. */
  231. public function createCollection($collection) {
  232. return new static(
  233. $this->directory,
  234. $collection
  235. );
  236. }
  237. /**
  238. * {@inheritdoc}
  239. */
  240. public function getCollectionName() {
  241. return $this->collection;
  242. }
  243. /**
  244. * {@inheritdoc}
  245. */
  246. public function getAllCollectionNames() {
  247. if (!is_dir($this->directory)) {
  248. return [];
  249. }
  250. $collections = $this->getAllCollectionNamesHelper($this->directory);
  251. sort($collections);
  252. return $collections;
  253. }
  254. /**
  255. * Helper function for getAllCollectionNames().
  256. *
  257. * If the file storage has the following subdirectory structure:
  258. * ./another_collection/one
  259. * ./another_collection/two
  260. * ./collection/sub/one
  261. * ./collection/sub/two
  262. * this function will return:
  263. * @code
  264. * array(
  265. * 'another_collection.one',
  266. * 'another_collection.two',
  267. * 'collection.sub.one',
  268. * 'collection.sub.two',
  269. * );
  270. * @endcode
  271. *
  272. * @param string $directory
  273. * The directory to check for sub directories. This allows this
  274. * function to be used recursively to discover all the collections in the
  275. * storage. It is the responsibility of the caller to ensure the directory
  276. * exists.
  277. *
  278. * @return array
  279. * A list of collection names contained within the provided directory.
  280. */
  281. protected function getAllCollectionNamesHelper($directory) {
  282. $collections = [];
  283. $pattern = '/\.' . preg_quote($this->getFileExtension(), '/') . '$/';
  284. foreach (new \DirectoryIterator($directory) as $fileinfo) {
  285. if ($fileinfo->isDir() && !$fileinfo->isDot()) {
  286. $collection = $fileinfo->getFilename();
  287. // Recursively call getAllCollectionNamesHelper() to discover if there
  288. // are subdirectories. Subdirectories represent a dotted collection
  289. // name.
  290. $sub_collections = $this->getAllCollectionNamesHelper($directory . '/' . $collection);
  291. if (!empty($sub_collections)) {
  292. // Build up the collection name by concatenating the subdirectory
  293. // names with the current directory name.
  294. foreach ($sub_collections as $sub_collection) {
  295. $collections[] = $collection . '.' . $sub_collection;
  296. }
  297. }
  298. // Check that the collection is valid by searching it for configuration
  299. // objects. A directory without any configuration objects is not a valid
  300. // collection.
  301. // @see \Drupal\Core\Config\FileStorage::listAll()
  302. foreach (scandir($directory . '/' . $collection) as $file) {
  303. if ($file[0] !== '.' && preg_match($pattern, $file)) {
  304. $collections[] = $collection;
  305. break;
  306. }
  307. }
  308. }
  309. }
  310. return $collections;
  311. }
  312. /**
  313. * Gets the directory for the collection.
  314. *
  315. * @return string
  316. * The directory for the collection.
  317. */
  318. protected function getCollectionDirectory() {
  319. if ($this->collection == StorageInterface::DEFAULT_COLLECTION) {
  320. $dir = $this->directory;
  321. }
  322. else {
  323. $dir = $this->directory . '/' . str_replace('.', '/', $this->collection);
  324. }
  325. return $dir;
  326. }
  327. /**
  328. * Returns file system service.
  329. *
  330. * @return \Drupal\Core\File\FileSystemInterface
  331. * The file system service.
  332. */
  333. private function getFileSystem() {
  334. return \Drupal::service('file_system');
  335. }
  336. }