PageRenderTime 25ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

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

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