Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / lib / Drupal / Component / PhpStorage / FileStorage.php
1 <?php
2
3 namespace Drupal\Component\PhpStorage;
4
5 /**
6  * Stores the code as regular PHP files.
7  */
8 class FileStorage implements PhpStorageInterface {
9
10   /**
11    * The directory where the files should be stored.
12    *
13    * @var string
14    */
15   protected $directory;
16
17   /**
18    * Constructs this FileStorage object.
19    *
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..
25    */
26   public function __construct(array $configuration) {
27     $this->directory = $configuration['directory'] . '/' . $configuration['bin'];
28   }
29
30   /**
31    * {@inheritdoc}
32    */
33   public function exists($name) {
34     return file_exists($this->getFullPath($name));
35   }
36
37   /**
38    * {@inheritdoc}
39    */
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;
44   }
45
46   /**
47    * {@inheritdoc}
48    */
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);
54   }
55
56   /**
57    * Returns the standard .htaccess lines that Drupal writes to file directories.
58    *
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.
63    *
64    * @return string
65    *   The desired contents of the .htaccess file.
66    *
67    * @see file_create_htaccess()
68    */
69   public static function htaccessLines($private = TRUE) {
70     $lines = <<<EOF
71 # Turn off all options we don't need.
72 Options -Indexes -ExecCGI -Includes -MultiViews
73
74 # Set the catch-all handler to prevent scripts from being executed.
75 SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
76 <Files *>
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
79 </Files>
80
81 # If we know how to do it safely, disable the PHP engine entirely.
82 <IfModule mod_php5.c>
83   php_flag engine off
84 </IfModule>
85 EOF;
86
87     if ($private) {
88       $lines = <<<EOF
89 # Deny all requests from Apache 2.4+.
90 <IfModule mod_authz_core.c>
91   Require all denied
92 </IfModule>
93
94 # Deny all requests from Apache 2.0-2.2.
95 <IfModule !mod_authz_core.c>
96   Deny from all
97 </IfModule>
98
99 $lines
100 EOF;
101     }
102
103     return $lines;
104   }
105
106   /**
107    * Ensures the directory exists, has the right permissions, and a .htaccess.
108    *
109    * For compatibility with open_basedir, the requested directory is created
110    * using a recursion logic that is based on the relative directory path/tree:
111    * It works from the end of the path recursively back towards the root
112    * directory, until an existing parent directory is found. From there, the
113    * subdirectories are created.
114    *
115    * @param string $directory
116    *   The directory path.
117    * @param int $mode
118    *   The mode, permissions, the directory should have.
119    */
120   protected function ensureDirectory($directory, $mode = 0777) {
121     if ($this->createDirectory($directory, $mode)) {
122       $htaccess_path = $directory . '/.htaccess';
123       if (!file_exists($htaccess_path) && file_put_contents($htaccess_path, static::htaccessLines())) {
124         @chmod($htaccess_path, 0444);
125       }
126     }
127   }
128
129   /**
130    * Ensures the requested directory exists and has the right permissions.
131    *
132    * For compatibility with open_basedir, the requested directory is created
133    * using a recursion logic that is based on the relative directory path/tree:
134    * It works from the end of the path recursively back towards the root
135    * directory, until an existing parent directory is found. From there, the
136    * subdirectories are created.
137    *
138    * @param string $directory
139    *   The directory path.
140    * @param int $mode
141    *   The mode, permissions, the directory should have.
142    *
143    * @return bool
144    *   TRUE if the directory exists or has been created, FALSE otherwise.
145    */
146   protected function createDirectory($directory, $mode = 0777) {
147     // If the directory exists already, there's nothing to do.
148     if (is_dir($directory)) {
149       return TRUE;
150     }
151
152     // If the parent directory doesn't exist, try to create it.
153     $parent_exists = is_dir($parent = dirname($directory));
154     if (!$parent_exists) {
155       $parent_exists = $this->createDirectory($parent, $mode);
156     }
157
158     // If parent exists, try to create the directory and ensure to set its
159     // permissions, because mkdir() obeys the umask of the current process.
160     if ($parent_exists) {
161       // We hide warnings and ignore the return because there may have been a
162       // race getting here and the directory could already exist.
163       @mkdir($directory);
164       // Only try to chmod() if the subdirectory could be created.
165       if (is_dir($directory)) {
166         // Avoid writing permissions if possible.
167         if (fileperms($directory) !== $mode) {
168           return chmod($directory, $mode);
169         }
170         return TRUE;
171       }
172       else {
173         // Something failed and the directory doesn't exist.
174         trigger_error('mkdir(): Permission Denied', E_USER_WARNING);
175       }
176     }
177     return FALSE;
178   }
179
180   /**
181    * {@inheritdoc}
182    */
183   public function delete($name) {
184     $path = $this->getFullPath($name);
185     if (file_exists($path)) {
186       return $this->unlink($path);
187     }
188     return FALSE;
189   }
190
191   /**
192    * {@inheritdoc}
193    */
194   public function getFullPath($name) {
195     return $this->directory . '/' . $name;
196   }
197
198   /**
199    * {@inheritdoc}
200    */
201   public function writeable() {
202     return TRUE;
203   }
204
205   /**
206    * {@inheritdoc}
207    */
208   public function deleteAll() {
209     return $this->unlink($this->directory);
210   }
211
212   /**
213    * Deletes files and/or directories in the specified path.
214    *
215    * If the specified path is a directory the method will
216    * call itself recursively to process the contents. Once the contents have
217    * been removed the directory will also be removed.
218    *
219    * @param string $path
220    *   A string containing either a file or directory path.
221    *
222    * @return bool
223    *   TRUE for success or if path does not exist, FALSE in the event of an
224    *   error.
225    */
226   protected function unlink($path) {
227     if (file_exists($path)) {
228       if (is_dir($path)) {
229         // Ensure the folder is writable.
230         @chmod($path, 0777);
231         foreach (new \DirectoryIterator($path) as $fileinfo) {
232           if (!$fileinfo->isDot()) {
233             $this->unlink($fileinfo->getPathName());
234           }
235         }
236         return @rmdir($path);
237       }
238       // Windows needs the file to be writable.
239       @chmod($path, 0700);
240       return @unlink($path);
241     }
242     // If there's nothing to delete return TRUE anyway.
243     return TRUE;
244   }
245
246   /**
247    * {@inheritdoc}
248    */
249   public function listAll() {
250     $names = [];
251     if (file_exists($this->directory)) {
252       foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
253         if (!$fileinfo->isDot()) {
254           $name = $fileinfo->getFilename();
255           if ($name != '.htaccess') {
256             $names[] = $name;
257           }
258         }
259       }
260     }
261     return $names;
262   }
263
264   /**
265    * {@inheritdoc}
266    */
267   public function garbageCollection() {
268   }
269
270 }