FileStorage.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. namespace Drupal\Component\PhpStorage;
  3. use Drupal\Component\FileSecurity\FileSecurity;
  4. /**
  5. * Stores the code as regular PHP files.
  6. */
  7. class FileStorage implements PhpStorageInterface {
  8. /**
  9. * The directory where the files should be stored.
  10. *
  11. * @var string
  12. */
  13. protected $directory;
  14. /**
  15. * Constructs this FileStorage object.
  16. *
  17. * @param array $configuration
  18. * An associative array, containing at least these two keys:
  19. * - directory: The directory where the files should be stored.
  20. * - bin: The storage bin. Multiple storage objects can be instantiated with
  21. * the same configuration, but for different bins..
  22. */
  23. public function __construct(array $configuration) {
  24. $this->directory = $configuration['directory'] . '/' . $configuration['bin'];
  25. }
  26. /**
  27. * {@inheritdoc}
  28. */
  29. public function exists($name) {
  30. return file_exists($this->getFullPath($name));
  31. }
  32. /**
  33. * {@inheritdoc}
  34. */
  35. public function load($name) {
  36. // The FALSE returned on failure is enough for the caller to handle this,
  37. // we do not want a warning too.
  38. return (@include_once $this->getFullPath($name)) !== FALSE;
  39. }
  40. /**
  41. * {@inheritdoc}
  42. */
  43. public function save($name, $code) {
  44. $path = $this->getFullPath($name);
  45. $directory = dirname($path);
  46. $this->ensureDirectory($directory);
  47. return (bool) file_put_contents($path, $code);
  48. }
  49. /**
  50. * Returns the standard .htaccess lines that Drupal writes to file directories.
  51. *
  52. * @param bool $private
  53. * (optional) Set to FALSE to return the .htaccess lines for an open and
  54. * public directory. The default is TRUE, which returns the .htaccess lines
  55. * for a private and protected directory.
  56. *
  57. * @return string
  58. * The desired contents of the .htaccess file.
  59. *
  60. * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead use
  61. * \Drupal\Component\FileSecurity\FileSecurity.
  62. *
  63. * @see https://www.drupal.org/node/3075098
  64. * @see file_save_htaccess()
  65. */
  66. public static function htaccessLines($private = TRUE) {
  67. @trigger_error("htaccessLines() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Component\FileSecurity\FileSecurity::htaccessLines() instead. See https://www.drupal.org/node/3075098", E_USER_DEPRECATED);
  68. return FileSecurity::htaccessLines($private);
  69. }
  70. /**
  71. * Ensures the directory exists, has the right permissions, and a .htaccess.
  72. *
  73. * For compatibility with open_basedir, the requested directory is created
  74. * using a recursion logic that is based on the relative directory path/tree:
  75. * It works from the end of the path recursively back towards the root
  76. * directory, until an existing parent directory is found. From there, the
  77. * subdirectories are created.
  78. *
  79. * @param string $directory
  80. * The directory path.
  81. * @param int $mode
  82. * The mode, permissions, the directory should have.
  83. */
  84. protected function ensureDirectory($directory, $mode = 0777) {
  85. if ($this->createDirectory($directory, $mode)) {
  86. FileSecurity::writeHtaccess($directory);
  87. }
  88. }
  89. /**
  90. * Ensures the requested directory exists and has the right permissions.
  91. *
  92. * For compatibility with open_basedir, the requested directory is created
  93. * using a recursion logic that is based on the relative directory path/tree:
  94. * It works from the end of the path recursively back towards the root
  95. * directory, until an existing parent directory is found. From there, the
  96. * subdirectories are created.
  97. *
  98. * @param string $directory
  99. * The directory path.
  100. * @param int $mode
  101. * The mode, permissions, the directory should have.
  102. *
  103. * @return bool
  104. * TRUE if the directory exists or has been created, FALSE otherwise.
  105. */
  106. protected function createDirectory($directory, $mode = 0777) {
  107. // If the directory exists already, there's nothing to do.
  108. if (is_dir($directory)) {
  109. return TRUE;
  110. }
  111. // If the parent directory doesn't exist, try to create it.
  112. $parent_exists = is_dir($parent = dirname($directory));
  113. if (!$parent_exists) {
  114. $parent_exists = $this->createDirectory($parent, $mode);
  115. }
  116. // If parent exists, try to create the directory and ensure to set its
  117. // permissions, because mkdir() obeys the umask of the current process.
  118. if ($parent_exists) {
  119. // We hide warnings and ignore the return because there may have been a
  120. // race getting here and the directory could already exist.
  121. @mkdir($directory);
  122. // Only try to chmod() if the subdirectory could be created.
  123. if (is_dir($directory)) {
  124. // Avoid writing permissions if possible.
  125. if (fileperms($directory) !== $mode) {
  126. return chmod($directory, $mode);
  127. }
  128. return TRUE;
  129. }
  130. else {
  131. // Something failed and the directory doesn't exist.
  132. trigger_error('mkdir(): Permission Denied', E_USER_WARNING);
  133. }
  134. }
  135. return FALSE;
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function delete($name) {
  141. $path = $this->getFullPath($name);
  142. if (file_exists($path)) {
  143. return $this->unlink($path);
  144. }
  145. return FALSE;
  146. }
  147. /**
  148. * {@inheritdoc}
  149. */
  150. public function getFullPath($name) {
  151. return $this->directory . '/' . $name;
  152. }
  153. /**
  154. * {@inheritdoc}
  155. */
  156. public function writeable() {
  157. return TRUE;
  158. }
  159. /**
  160. * {@inheritdoc}
  161. */
  162. public function deleteAll() {
  163. return $this->unlink($this->directory);
  164. }
  165. /**
  166. * Deletes files and/or directories in the specified path.
  167. *
  168. * If the specified path is a directory the method will
  169. * call itself recursively to process the contents. Once the contents have
  170. * been removed the directory will also be removed.
  171. *
  172. * @param string $path
  173. * A string containing either a file or directory path.
  174. *
  175. * @return bool
  176. * TRUE for success or if path does not exist, FALSE in the event of an
  177. * error.
  178. */
  179. protected function unlink($path) {
  180. if (file_exists($path)) {
  181. if (is_dir($path)) {
  182. // Ensure the folder is writable.
  183. @chmod($path, 0777);
  184. foreach (new \DirectoryIterator($path) as $fileinfo) {
  185. if (!$fileinfo->isDot()) {
  186. $this->unlink($fileinfo->getPathName());
  187. }
  188. }
  189. return @rmdir($path);
  190. }
  191. // Windows needs the file to be writable.
  192. @chmod($path, 0700);
  193. return @unlink($path);
  194. }
  195. // If there's nothing to delete return TRUE anyway.
  196. return TRUE;
  197. }
  198. /**
  199. * {@inheritdoc}
  200. */
  201. public function listAll() {
  202. $names = [];
  203. if (file_exists($this->directory)) {
  204. foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
  205. if (!$fileinfo->isDot()) {
  206. $name = $fileinfo->getFilename();
  207. if ($name != '.htaccess') {
  208. $names[] = $name;
  209. }
  210. }
  211. }
  212. }
  213. return $names;
  214. }
  215. /**
  216. * {@inheritdoc}
  217. */
  218. public function garbageCollection() {
  219. }
  220. }