3 namespace Drupal\Component\PhpStorage;
6 * Stores the code as regular PHP files.
8 class FileStorage implements PhpStorageInterface {
11 * The directory where the files should be stored.
18 * Constructs this FileStorage object.
20 * @param array $configuration
21 * An associative array, containing at least these two keys:
22 * - directory: The directory where the files should be stored.
23 * - bin: The storage bin. Multiple storage objects can be instantiated with
24 * the same configuration, but for different bins..
26 public function __construct(array $configuration) {
27 $this->directory = $configuration['directory'] . '/' . $configuration['bin'];
33 public function exists($name) {
34 return file_exists($this->getFullPath($name));
40 public function load($name) {
41 // The FALSE returned on failure is enough for the caller to handle this,
42 // we do not want a warning too.
43 return (@include_once $this->getFullPath($name)) !== FALSE;
49 public function save($name, $code) {
50 $path = $this->getFullPath($name);
51 $directory = dirname($path);
52 $this->ensureDirectory($directory);
53 return (bool) file_put_contents($path, $code);
57 * Returns the standard .htaccess lines that Drupal writes to file directories.
59 * @param bool $private
60 * (optional) Set to FALSE to return the .htaccess lines for an open and
61 * public directory. The default is TRUE, which returns the .htaccess lines
62 * for a private and protected directory.
65 * The desired contents of the .htaccess file.
67 * @see file_create_htaccess()
69 public static function htaccessLines($private = TRUE) {
71 # Turn off all options we don't need.
72 Options -Indexes -ExecCGI -Includes -MultiViews
74 # Set the catch-all handler to prevent scripts from being executed.
75 SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
77 # Override the handler again if we're run later in the evaluation list.
78 SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
81 # If we know how to do it safely, disable the PHP engine entirely.
89 # Deny all requests from Apache 2.4+.
90 <IfModule mod_authz_core.c>
94 # Deny all requests from Apache 2.0-2.2.
95 <IfModule !mod_authz_core.c>
106 * Ensures the directory exists, has the right permissions, and a .htaccess.
108 * For compatibility with open_basedir, the requested directory is created
109 * using a recursion logic that is based on the relative directory path/tree:
110 * It works from the end of the path recursively back towards the root
111 * directory, until an existing parent directory is found. From there, the
112 * subdirectories are created.
114 * @param string $directory
115 * The directory path.
117 * The mode, permissions, the directory should have.
119 protected function ensureDirectory($directory, $mode = 0777) {
120 if ($this->createDirectory($directory, $mode)) {
121 $htaccess_path = $directory . '/.htaccess';
122 if (!file_exists($htaccess_path) && file_put_contents($htaccess_path, static::htaccessLines())) {
123 @chmod($htaccess_path, 0444);
129 * Ensures the requested directory exists and has the right permissions.
131 * For compatibility with open_basedir, the requested directory is created
132 * using a recursion logic that is based on the relative directory path/tree:
133 * It works from the end of the path recursively back towards the root
134 * directory, until an existing parent directory is found. From there, the
135 * subdirectories are created.
137 * @param string $directory
138 * The directory path.
140 * The mode, permissions, the directory should have.
141 * @param bool $is_backwards_recursive
145 * TRUE if the directory exists or has been created, FALSE otherwise.
147 protected function createDirectory($directory, $mode = 0777, $is_backwards_recursive = FALSE) {
148 // If the directory exists already, there's nothing to do.
149 if (is_dir($directory)) {
152 // Otherwise, try to create the directory and ensure to set its permissions,
153 // because mkdir() obeys the umask of the current process.
154 if (is_dir($parent = dirname($directory))) {
155 // If the parent directory exists, then the backwards recursion must end,
156 // regardless of whether the subdirectory could be created.
157 if ($status = mkdir($directory)) {
158 // Only try to chmod() if the subdirectory could be created.
159 $status = chmod($directory, $mode);
161 return $is_backwards_recursive ? TRUE : $status;
163 // If the parent directory and the requested directory does not exist and
164 // could not be created above, walk the requested directory path back up
165 // until an existing directory is hit, and from there, recursively create
166 // the sub-directories. Only if that recursion succeeds, create the final,
167 // originally requested subdirectory.
168 return $this->createDirectory($parent, $mode, TRUE) && mkdir($directory) && chmod($directory, $mode);
174 public function delete($name) {
175 $path = $this->getFullPath($name);
176 if (file_exists($path)) {
177 return $this->unlink($path);
185 public function getFullPath($name) {
186 return $this->directory . '/' . $name;
192 public function writeable() {
199 public function deleteAll() {
200 return $this->unlink($this->directory);
204 * Deletes files and/or directories in the specified path.
206 * If the specified path is a directory the method will
207 * call itself recursively to process the contents. Once the contents have
208 * been removed the directory will also be removed.
210 * @param string $path
211 * A string containing either a file or directory path.
214 * TRUE for success or if path does not exist, FALSE in the event of an
217 protected function unlink($path) {
218 if (file_exists($path)) {
220 // Ensure the folder is writable.
222 foreach (new \DirectoryIterator($path) as $fileinfo) {
223 if (!$fileinfo->isDot()) {
224 $this->unlink($fileinfo->getPathName());
227 return @rmdir($path);
229 // Windows needs the file to be writable.
231 return @unlink($path);
233 // If there's nothing to delete return TRUE anyway.
240 public function listAll() {
242 if (file_exists($this->directory)) {
243 foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
244 if (!$fileinfo->isDot()) {
245 $name = $fileinfo->getFilename();
246 if ($name != '.htaccess') {
258 public function garbageCollection() {