Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * API for handling file uploads and server file management.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Component\FileSystem\FileSystem as ComponentFileSystem;
|
Chris@0
|
9 use Drupal\Component\Utility\Unicode;
|
Chris@0
|
10 use Drupal\Component\Utility\UrlHelper;
|
Chris@0
|
11 use Drupal\Component\PhpStorage\FileStorage;
|
Chris@0
|
12 use Drupal\Component\Utility\Bytes;
|
Chris@0
|
13 use Drupal\Core\File\FileSystem;
|
Chris@0
|
14 use Drupal\Core\Site\Settings;
|
Chris@0
|
15 use Drupal\Core\StreamWrapper\PublicStream;
|
Chris@0
|
16 use Drupal\Core\StreamWrapper\PrivateStream;
|
Chris@0
|
17
|
Chris@0
|
18 /**
|
Chris@0
|
19 * Default mode for new directories. See drupal_chmod().
|
Chris@0
|
20 *
|
Chris@0
|
21 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
22 * Use \Drupal\Core\File\FileSystem::CHMOD_DIRECTORY.
|
Chris@0
|
23 *
|
Chris@0
|
24 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
25 */
|
Chris@0
|
26 const FILE_CHMOD_DIRECTORY = FileSystem::CHMOD_DIRECTORY;
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Default mode for new files. See drupal_chmod().
|
Chris@0
|
30 *
|
Chris@0
|
31 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
32 * Use \Drupal\Core\File\FileSystem::CHMOD_FILE.
|
Chris@0
|
33 *
|
Chris@0
|
34 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
35 */
|
Chris@0
|
36 const FILE_CHMOD_FILE = FileSystem::CHMOD_FILE;
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * @defgroup file File interface
|
Chris@0
|
40 * @{
|
Chris@0
|
41 * Common file handling functions.
|
Chris@0
|
42 */
|
Chris@0
|
43
|
Chris@0
|
44 /**
|
Chris@0
|
45 * Flag used by file_prepare_directory() -- create directory if not present.
|
Chris@0
|
46 */
|
Chris@0
|
47 const FILE_CREATE_DIRECTORY = 1;
|
Chris@0
|
48
|
Chris@0
|
49 /**
|
Chris@0
|
50 * Flag used by file_prepare_directory() -- file permissions may be changed.
|
Chris@0
|
51 */
|
Chris@0
|
52 const FILE_MODIFY_PERMISSIONS = 2;
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Flag for dealing with existing files: Appends number until name is unique.
|
Chris@0
|
56 */
|
Chris@0
|
57 const FILE_EXISTS_RENAME = 0;
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Flag for dealing with existing files: Replace the existing file.
|
Chris@0
|
61 */
|
Chris@0
|
62 const FILE_EXISTS_REPLACE = 1;
|
Chris@0
|
63
|
Chris@0
|
64 /**
|
Chris@0
|
65 * Flag for dealing with existing files: Do nothing and return FALSE.
|
Chris@0
|
66 */
|
Chris@0
|
67 const FILE_EXISTS_ERROR = 2;
|
Chris@0
|
68
|
Chris@0
|
69 /**
|
Chris@0
|
70 * Indicates that the file is permanent and should not be deleted.
|
Chris@0
|
71 *
|
Chris@0
|
72 * Temporary files older than the system.file.temporary_maximum_age
|
Chris@0
|
73 * configuration value will be, if clean-up not disabled, removed during cron
|
Chris@0
|
74 * runs, but permanent files will not be removed during the file garbage
|
Chris@0
|
75 * collection process.
|
Chris@0
|
76 */
|
Chris@0
|
77 const FILE_STATUS_PERMANENT = 1;
|
Chris@0
|
78
|
Chris@0
|
79 /**
|
Chris@0
|
80 * Returns the scheme of a URI (e.g. a stream).
|
Chris@0
|
81 *
|
Chris@0
|
82 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
83 * Use \Drupal\Core\File\FileSystem::uriScheme().
|
Chris@0
|
84 *
|
Chris@0
|
85 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
86 */
|
Chris@0
|
87 function file_uri_scheme($uri) {
|
Chris@0
|
88 return \Drupal::service('file_system')->uriScheme($uri);
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 /**
|
Chris@0
|
92 * Checks that the scheme of a stream URI is valid.
|
Chris@0
|
93 *
|
Chris@0
|
94 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
95 * Use \Drupal\Core\File\FileSystem::validScheme().
|
Chris@0
|
96 *
|
Chris@0
|
97 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
98 */
|
Chris@0
|
99 function file_stream_wrapper_valid_scheme($scheme) {
|
Chris@0
|
100 return \Drupal::service('file_system')->validScheme($scheme);
|
Chris@0
|
101 }
|
Chris@0
|
102
|
Chris@0
|
103
|
Chris@0
|
104 /**
|
Chris@0
|
105 * Returns the part of a URI after the schema.
|
Chris@0
|
106 *
|
Chris@0
|
107 * @param string $uri
|
Chris@0
|
108 * A stream, referenced as "scheme://target" or "data:target".
|
Chris@0
|
109 *
|
Chris@0
|
110 * @return string|bool
|
Chris@0
|
111 * A string containing the target (path), or FALSE if none.
|
Chris@0
|
112 * For example, the URI "public://sample/test.txt" would return
|
Chris@0
|
113 * "sample/test.txt".
|
Chris@0
|
114 *
|
Chris@0
|
115 * @see file_uri_scheme()
|
Chris@0
|
116 */
|
Chris@0
|
117 function file_uri_target($uri) {
|
Chris@0
|
118 // Remove the scheme from the URI and remove erroneous leading or trailing,
|
Chris@0
|
119 // forward-slashes and backslashes.
|
Chris@0
|
120 $target = trim(preg_replace('/^[\w\-]+:\/\/|^data:/', '', $uri), '\/');
|
Chris@0
|
121
|
Chris@0
|
122 // If nothing was replaced, the URI doesn't have a valid scheme.
|
Chris@0
|
123 return $target !== $uri ? $target : FALSE;
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 /**
|
Chris@0
|
127 * Gets the default file stream implementation.
|
Chris@0
|
128 *
|
Chris@0
|
129 * @return string
|
Chris@0
|
130 * 'public', 'private' or any other file scheme defined as the default.
|
Chris@0
|
131 */
|
Chris@0
|
132 function file_default_scheme() {
|
Chris@0
|
133 return \Drupal::config('system.file')->get('default_scheme');
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 /**
|
Chris@0
|
137 * Normalizes a URI by making it syntactically correct.
|
Chris@0
|
138 *
|
Chris@0
|
139 * A stream is referenced as "scheme://target".
|
Chris@0
|
140 *
|
Chris@0
|
141 * The following actions are taken:
|
Chris@0
|
142 * - Remove trailing slashes from target
|
Chris@0
|
143 * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://".
|
Chris@0
|
144 *
|
Chris@0
|
145 * @param string $uri
|
Chris@0
|
146 * String reference containing the URI to normalize.
|
Chris@0
|
147 *
|
Chris@0
|
148 * @return string
|
Chris@0
|
149 * The normalized URI.
|
Chris@0
|
150 */
|
Chris@0
|
151 function file_stream_wrapper_uri_normalize($uri) {
|
Chris@0
|
152 $scheme = \Drupal::service('file_system')->uriScheme($uri);
|
Chris@0
|
153
|
Chris@0
|
154 if (file_stream_wrapper_valid_scheme($scheme)) {
|
Chris@0
|
155 $target = file_uri_target($uri);
|
Chris@0
|
156
|
Chris@0
|
157 if ($target !== FALSE) {
|
Chris@0
|
158 $uri = $scheme . '://' . $target;
|
Chris@0
|
159 }
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 return $uri;
|
Chris@0
|
163 }
|
Chris@0
|
164
|
Chris@0
|
165 /**
|
Chris@0
|
166 * Creates a web-accessible URL for a stream to an external or local file.
|
Chris@0
|
167 *
|
Chris@0
|
168 * Compatibility: normal paths and stream wrappers.
|
Chris@0
|
169 *
|
Chris@0
|
170 * There are two kinds of local files:
|
Chris@0
|
171 * - "managed files", i.e. those stored by a Drupal-compatible stream wrapper.
|
Chris@0
|
172 * These are files that have either been uploaded by users or were generated
|
Chris@0
|
173 * automatically (for example through CSS aggregation).
|
Chris@0
|
174 * - "shipped files", i.e. those outside of the files directory, which ship as
|
Chris@0
|
175 * part of Drupal core or contributed modules or themes.
|
Chris@0
|
176 *
|
Chris@0
|
177 * @param string $uri
|
Chris@0
|
178 * The URI to a file for which we need an external URL, or the path to a
|
Chris@0
|
179 * shipped file.
|
Chris@0
|
180 *
|
Chris@0
|
181 * @return string
|
Chris@0
|
182 * A string containing a URL that may be used to access the file.
|
Chris@0
|
183 * If the provided string already contains a preceding 'http', 'https', or
|
Chris@0
|
184 * '/', nothing is done and the same string is returned. If a stream wrapper
|
Chris@0
|
185 * could not be found to generate an external URL, then FALSE is returned.
|
Chris@0
|
186 *
|
Chris@0
|
187 * @see https://www.drupal.org/node/515192
|
Chris@0
|
188 * @see file_url_transform_relative()
|
Chris@0
|
189 */
|
Chris@0
|
190 function file_create_url($uri) {
|
Chris@0
|
191 // Allow the URI to be altered, e.g. to serve a file from a CDN or static
|
Chris@0
|
192 // file server.
|
Chris@0
|
193 \Drupal::moduleHandler()->alter('file_url', $uri);
|
Chris@0
|
194
|
Chris@0
|
195 $scheme = \Drupal::service('file_system')->uriScheme($uri);
|
Chris@0
|
196
|
Chris@0
|
197 if (!$scheme) {
|
Chris@0
|
198 // Allow for:
|
Chris@0
|
199 // - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
|
Chris@0
|
200 // - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
|
Chris@0
|
201 // http://example.com/bar.jpg by the browser when viewing a page over
|
Chris@0
|
202 // HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
|
Chris@0
|
203 // Both types of relative URIs are characterized by a leading slash, hence
|
Chris@0
|
204 // we can use a single check.
|
Chris@0
|
205 if (Unicode::substr($uri, 0, 1) == '/') {
|
Chris@0
|
206 return $uri;
|
Chris@0
|
207 }
|
Chris@0
|
208 else {
|
Chris@0
|
209 // If this is not a properly formatted stream, then it is a shipped file.
|
Chris@0
|
210 // Therefore, return the urlencoded URI with the base URL prepended.
|
Chris@0
|
211 $options = UrlHelper::parse($uri);
|
Chris@0
|
212 $path = $GLOBALS['base_url'] . '/' . UrlHelper::encodePath($options['path']);
|
Chris@0
|
213 // Append the query.
|
Chris@0
|
214 if ($options['query']) {
|
Chris@0
|
215 $path .= '?' . UrlHelper::buildQuery($options['query']);
|
Chris@0
|
216 }
|
Chris@0
|
217
|
Chris@0
|
218 // Append fragment.
|
Chris@0
|
219 if ($options['fragment']) {
|
Chris@0
|
220 $path .= '#' . $options['fragment'];
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 return $path;
|
Chris@0
|
224 }
|
Chris@0
|
225 }
|
Chris@0
|
226 elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
|
Chris@0
|
227 // Check for HTTP and data URI-encoded URLs so that we don't have to
|
Chris@0
|
228 // implement getExternalUrl() for the HTTP and data schemes.
|
Chris@0
|
229 return $uri;
|
Chris@0
|
230 }
|
Chris@0
|
231 else {
|
Chris@0
|
232 // Attempt to return an external URL using the appropriate wrapper.
|
Chris@0
|
233 if ($wrapper = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)) {
|
Chris@0
|
234 return $wrapper->getExternalUrl();
|
Chris@0
|
235 }
|
Chris@0
|
236 else {
|
Chris@0
|
237 return FALSE;
|
Chris@0
|
238 }
|
Chris@0
|
239 }
|
Chris@0
|
240 }
|
Chris@0
|
241
|
Chris@0
|
242 /**
|
Chris@0
|
243 * Transforms an absolute URL of a local file to a relative URL.
|
Chris@0
|
244 *
|
Chris@0
|
245 * May be useful to prevent problems on multisite set-ups and prevent mixed
|
Chris@0
|
246 * content errors when using HTTPS + HTTP.
|
Chris@0
|
247 *
|
Chris@0
|
248 * @param string $file_url
|
Chris@0
|
249 * A file URL of a local file as generated by file_create_url().
|
Chris@0
|
250 *
|
Chris@0
|
251 * @return string
|
Chris@0
|
252 * If the file URL indeed pointed to a local file and was indeed absolute,
|
Chris@0
|
253 * then the transformed, relative URL to the local file. Otherwise: the
|
Chris@0
|
254 * original value of $file_url.
|
Chris@0
|
255 *
|
Chris@0
|
256 * @see file_create_url()
|
Chris@0
|
257 */
|
Chris@0
|
258 function file_url_transform_relative($file_url) {
|
Chris@0
|
259 // Unfortunately, we pretty much have to duplicate Symfony's
|
Chris@0
|
260 // Request::getHttpHost() method because Request::getPort() may return NULL
|
Chris@0
|
261 // instead of a port number.
|
Chris@0
|
262 $request = \Drupal::request();
|
Chris@0
|
263 $host = $request->getHost();
|
Chris@0
|
264 $scheme = $request->getScheme();
|
Chris@0
|
265 $port = $request->getPort() ?: 80;
|
Chris@0
|
266 if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
|
Chris@0
|
267 $http_host = $host;
|
Chris@0
|
268 }
|
Chris@0
|
269 else {
|
Chris@0
|
270 $http_host = $host . ':' . $port;
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273 return preg_replace('|^https?://' . preg_quote($http_host, '|') . '|', '', $file_url);
|
Chris@0
|
274 }
|
Chris@0
|
275
|
Chris@0
|
276 /**
|
Chris@0
|
277 * Checks that the directory exists and is writable.
|
Chris@0
|
278 *
|
Chris@0
|
279 * Directories need to have execute permissions to be considered a directory by
|
Chris@0
|
280 * FTP servers, etc.
|
Chris@0
|
281 *
|
Chris@0
|
282 * @param $directory
|
Chris@0
|
283 * A string reference containing the name of a directory path or URI. A
|
Chris@0
|
284 * trailing slash will be trimmed from a path.
|
Chris@0
|
285 * @param $options
|
Chris@0
|
286 * A bitmask to indicate if the directory should be created if it does
|
Chris@0
|
287 * not exist (FILE_CREATE_DIRECTORY) or made writable if it is read-only
|
Chris@0
|
288 * (FILE_MODIFY_PERMISSIONS).
|
Chris@0
|
289 *
|
Chris@0
|
290 * @return
|
Chris@0
|
291 * TRUE if the directory exists (or was created) and is writable. FALSE
|
Chris@0
|
292 * otherwise.
|
Chris@0
|
293 */
|
Chris@0
|
294 function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS) {
|
Chris@0
|
295 if (!file_stream_wrapper_valid_scheme(\Drupal::service('file_system')->uriScheme($directory))) {
|
Chris@0
|
296 // Only trim if we're not dealing with a stream.
|
Chris@0
|
297 $directory = rtrim($directory, '/\\');
|
Chris@0
|
298 }
|
Chris@0
|
299
|
Chris@0
|
300 // Check if directory exists.
|
Chris@0
|
301 if (!is_dir($directory)) {
|
Chris@0
|
302 // Let mkdir() recursively create directories and use the default directory
|
Chris@0
|
303 // permissions.
|
Chris@0
|
304 if ($options & FILE_CREATE_DIRECTORY) {
|
Chris@0
|
305 return @drupal_mkdir($directory, NULL, TRUE);
|
Chris@0
|
306 }
|
Chris@0
|
307 return FALSE;
|
Chris@0
|
308 }
|
Chris@0
|
309 // The directory exists, so check to see if it is writable.
|
Chris@0
|
310 $writable = is_writable($directory);
|
Chris@0
|
311 if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) {
|
Chris@0
|
312 return drupal_chmod($directory);
|
Chris@0
|
313 }
|
Chris@0
|
314
|
Chris@0
|
315 return $writable;
|
Chris@0
|
316 }
|
Chris@0
|
317
|
Chris@0
|
318 /**
|
Chris@0
|
319 * Creates a .htaccess file in each Drupal files directory if it is missing.
|
Chris@0
|
320 */
|
Chris@0
|
321 function file_ensure_htaccess() {
|
Chris@0
|
322 file_save_htaccess('public://', FALSE);
|
Chris@0
|
323 $private_path = PrivateStream::basePath();
|
Chris@0
|
324 if (!empty($private_path)) {
|
Chris@0
|
325 file_save_htaccess('private://', TRUE);
|
Chris@0
|
326 }
|
Chris@0
|
327 file_save_htaccess('temporary://', TRUE);
|
Chris@0
|
328
|
Chris@0
|
329 // If a staging directory exists then it should contain a .htaccess file.
|
Chris@0
|
330 // @todo https://www.drupal.org/node/2696103 catch a more specific exception
|
Chris@0
|
331 // and simplify this code.
|
Chris@0
|
332 try {
|
Chris@0
|
333 $staging = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
|
Chris@0
|
334 }
|
Chris@0
|
335 catch (\Exception $e) {
|
Chris@0
|
336 $staging = FALSE;
|
Chris@0
|
337 }
|
Chris@0
|
338 if ($staging) {
|
Chris@0
|
339 // Note that we log an error here if we can't write the .htaccess file. This
|
Chris@0
|
340 // can occur if the staging directory is read-only. If it is then it is the
|
Chris@0
|
341 // user's responsibility to create the .htaccess file.
|
Chris@0
|
342 file_save_htaccess($staging, TRUE);
|
Chris@0
|
343 }
|
Chris@0
|
344 }
|
Chris@0
|
345
|
Chris@0
|
346 /**
|
Chris@0
|
347 * Creates a .htaccess file in the given directory.
|
Chris@0
|
348 *
|
Chris@0
|
349 * @param string $directory
|
Chris@0
|
350 * The directory.
|
Chris@0
|
351 * @param bool $private
|
Chris@0
|
352 * (Optional) FALSE indicates that $directory should be a web-accessible
|
Chris@0
|
353 * directory. Defaults to TRUE which indicates a private directory.
|
Chris@0
|
354 * @param bool $force_overwrite
|
Chris@0
|
355 * (Optional) Set to TRUE to attempt to overwrite the existing .htaccess file
|
Chris@0
|
356 * if one is already present. Defaults to FALSE.
|
Chris@0
|
357 */
|
Chris@0
|
358 function file_save_htaccess($directory, $private = TRUE, $force_overwrite = FALSE) {
|
Chris@0
|
359 if (\Drupal::service('file_system')->uriScheme($directory)) {
|
Chris@0
|
360 $htaccess_path = file_stream_wrapper_uri_normalize($directory . '/.htaccess');
|
Chris@0
|
361 }
|
Chris@0
|
362 else {
|
Chris@0
|
363 $directory = rtrim($directory, '/\\');
|
Chris@0
|
364 $htaccess_path = $directory . '/.htaccess';
|
Chris@0
|
365 }
|
Chris@0
|
366
|
Chris@0
|
367 if (file_exists($htaccess_path) && !$force_overwrite) {
|
Chris@0
|
368 // Short circuit if the .htaccess file already exists.
|
Chris@0
|
369 return TRUE;
|
Chris@0
|
370 }
|
Chris@0
|
371 $htaccess_lines = FileStorage::htaccessLines($private);
|
Chris@0
|
372
|
Chris@0
|
373 // Write the .htaccess file.
|
Chris@0
|
374 if (file_exists($directory) && is_writable($directory) && file_put_contents($htaccess_path, $htaccess_lines)) {
|
Chris@0
|
375 return drupal_chmod($htaccess_path, 0444);
|
Chris@0
|
376 }
|
Chris@0
|
377 else {
|
Chris@0
|
378 $variables = ['%directory' => $directory, '@htaccess' => $htaccess_lines];
|
Chris@0
|
379 \Drupal::logger('security')->error("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <pre><code>@htaccess</code></pre>", $variables);
|
Chris@0
|
380 return FALSE;
|
Chris@0
|
381 }
|
Chris@0
|
382 }
|
Chris@0
|
383
|
Chris@0
|
384 /**
|
Chris@0
|
385 * Returns the standard .htaccess lines that Drupal writes to file directories.
|
Chris@0
|
386 *
|
Chris@0
|
387 * @param bool $private
|
Chris@0
|
388 * (Optional) Set to FALSE to return the .htaccess lines for a web-accessible
|
Chris@0
|
389 * public directory. The default is TRUE, which returns the .htaccess lines
|
Chris@0
|
390 * for a private directory that should not be web-accessible.
|
Chris@0
|
391 *
|
Chris@0
|
392 * @return string
|
Chris@0
|
393 * The desired contents of the .htaccess file.
|
Chris@0
|
394 *
|
Chris@0
|
395 * @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
|
Chris@0
|
396 * Use \Drupal\Component\PhpStorage\FileStorage::htaccessLines().
|
Chris@0
|
397 *
|
Chris@0
|
398 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
399 */
|
Chris@0
|
400 function file_htaccess_lines($private = TRUE) {
|
Chris@0
|
401 return FileStorage::htaccessLines($private);
|
Chris@0
|
402 }
|
Chris@0
|
403
|
Chris@0
|
404 /**
|
Chris@0
|
405 * Determines whether the URI has a valid scheme for file API operations.
|
Chris@0
|
406 *
|
Chris@0
|
407 * There must be a scheme and it must be a Drupal-provided scheme like
|
Chris@0
|
408 * 'public', 'private', 'temporary', or an extension provided with
|
Chris@0
|
409 * hook_stream_wrappers().
|
Chris@0
|
410 *
|
Chris@0
|
411 * @param $uri
|
Chris@0
|
412 * The URI to be tested.
|
Chris@0
|
413 *
|
Chris@0
|
414 * @return
|
Chris@0
|
415 * TRUE if the URI is allowed.
|
Chris@0
|
416 */
|
Chris@0
|
417 function file_valid_uri($uri) {
|
Chris@0
|
418 // Assert that the URI has an allowed scheme. Bare paths are not allowed.
|
Chris@0
|
419 $uri_scheme = \Drupal::service('file_system')->uriScheme($uri);
|
Chris@0
|
420 if (!file_stream_wrapper_valid_scheme($uri_scheme)) {
|
Chris@0
|
421 return FALSE;
|
Chris@0
|
422 }
|
Chris@0
|
423 return TRUE;
|
Chris@0
|
424 }
|
Chris@0
|
425
|
Chris@0
|
426 /**
|
Chris@0
|
427 * Copies a file to a new location without database changes or hook invocation.
|
Chris@0
|
428 *
|
Chris@0
|
429 * This is a powerful function that in many ways performs like an advanced
|
Chris@0
|
430 * version of copy().
|
Chris@0
|
431 * - Checks if $source and $destination are valid and readable/writable.
|
Chris@0
|
432 * - If file already exists in $destination either the call will error out,
|
Chris@0
|
433 * replace the file or rename the file based on the $replace parameter.
|
Chris@0
|
434 * - If the $source and $destination are equal, the behavior depends on the
|
Chris@0
|
435 * $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME
|
Chris@0
|
436 * will rename the file until the $destination is unique.
|
Chris@0
|
437 * - Works around a PHP bug where copy() does not properly support streams if
|
Chris@0
|
438 * safe_mode or open_basedir are enabled.
|
Chris@0
|
439 * @see https://bugs.php.net/bug.php?id=60456
|
Chris@0
|
440 *
|
Chris@0
|
441 * @param $source
|
Chris@0
|
442 * A string specifying the filepath or URI of the source file.
|
Chris@0
|
443 * @param $destination
|
Chris@0
|
444 * A URI containing the destination that $source should be copied to. The
|
Chris@0
|
445 * URI may be a bare filepath (without a scheme). If this value is omitted,
|
Chris@0
|
446 * Drupal's default files scheme will be used, usually "public://".
|
Chris@0
|
447 * @param $replace
|
Chris@0
|
448 * Replace behavior when the destination file already exists:
|
Chris@0
|
449 * - FILE_EXISTS_REPLACE - Replace the existing file.
|
Chris@0
|
450 * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
Chris@0
|
451 * unique.
|
Chris@0
|
452 * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
Chris@0
|
453 *
|
Chris@0
|
454 * @return
|
Chris@0
|
455 * The path to the new file, or FALSE in the event of an error.
|
Chris@0
|
456 *
|
Chris@0
|
457 * @see file_copy()
|
Chris@0
|
458 */
|
Chris@0
|
459 function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
|
Chris@0
|
460 if (!file_unmanaged_prepare($source, $destination, $replace)) {
|
Chris@0
|
461 return FALSE;
|
Chris@0
|
462 }
|
Chris@0
|
463 // Attempt to resolve the URIs. This is necessary in certain configurations
|
Chris@0
|
464 // (see above).
|
Chris@0
|
465 $file_system = \Drupal::service('file_system');
|
Chris@0
|
466 $real_source = $file_system->realpath($source) ?: $source;
|
Chris@0
|
467 $real_destination = $file_system->realpath($destination) ?: $destination;
|
Chris@0
|
468 // Perform the copy operation.
|
Chris@0
|
469 if (!@copy($real_source, $real_destination)) {
|
Chris@0
|
470 \Drupal::logger('file')->error('The specified file %file could not be copied to %destination.', ['%file' => $source, '%destination' => $destination]);
|
Chris@0
|
471 return FALSE;
|
Chris@0
|
472 }
|
Chris@0
|
473 // Set the permissions on the new file.
|
Chris@0
|
474 drupal_chmod($destination);
|
Chris@0
|
475 return $destination;
|
Chris@0
|
476 }
|
Chris@0
|
477
|
Chris@0
|
478 /**
|
Chris@0
|
479 * Internal function that prepares the destination for a file_unmanaged_copy or
|
Chris@0
|
480 * file_unmanaged_move operation.
|
Chris@0
|
481 *
|
Chris@0
|
482 * - Checks if $source and $destination are valid and readable/writable.
|
Chris@0
|
483 * - Checks that $source is not equal to $destination; if they are an error
|
Chris@0
|
484 * is reported.
|
Chris@0
|
485 * - If file already exists in $destination either the call will error out,
|
Chris@0
|
486 * replace the file or rename the file based on the $replace parameter.
|
Chris@0
|
487 *
|
Chris@0
|
488 * @param $source
|
Chris@0
|
489 * A string specifying the filepath or URI of the source file.
|
Chris@0
|
490 * @param $destination
|
Chris@0
|
491 * A URI containing the destination that $source should be moved/copied to.
|
Chris@0
|
492 * The URI may be a bare filepath (without a scheme) and in that case the
|
Chris@0
|
493 * default scheme (file://) will be used. If this value is omitted, Drupal's
|
Chris@0
|
494 * default files scheme will be used, usually "public://".
|
Chris@0
|
495 * @param $replace
|
Chris@0
|
496 * Replace behavior when the destination file already exists:
|
Chris@0
|
497 * - FILE_EXISTS_REPLACE - Replace the existing file.
|
Chris@0
|
498 * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
Chris@0
|
499 * unique.
|
Chris@0
|
500 * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
Chris@0
|
501 *
|
Chris@0
|
502 * @return
|
Chris@0
|
503 * TRUE, or FALSE in the event of an error.
|
Chris@0
|
504 *
|
Chris@0
|
505 * @see file_unmanaged_copy()
|
Chris@0
|
506 * @see file_unmanaged_move()
|
Chris@0
|
507 */
|
Chris@0
|
508 function file_unmanaged_prepare($source, &$destination = NULL, $replace = FILE_EXISTS_RENAME) {
|
Chris@0
|
509 $original_source = $source;
|
Chris@0
|
510 $logger = \Drupal::logger('file');
|
Chris@0
|
511 $file_system = \Drupal::service('file_system');
|
Chris@0
|
512
|
Chris@0
|
513 // Assert that the source file actually exists.
|
Chris@0
|
514 if (!file_exists($source)) {
|
Chris@0
|
515 // @todo Replace drupal_set_message() calls with exceptions instead.
|
Chris@0
|
516 drupal_set_message(t('The specified file %file could not be moved/copied because no file by that name exists. Please check that you supplied the correct filename.', ['%file' => $original_source]), 'error');
|
Chris@0
|
517 if (($realpath = $file_system->realpath($original_source)) !== FALSE) {
|
Chris@0
|
518 $logger->notice('File %file (%realpath) could not be moved/copied because it does not exist.', ['%file' => $original_source, '%realpath' => $realpath]);
|
Chris@0
|
519 }
|
Chris@0
|
520 else {
|
Chris@0
|
521 $logger->notice('File %file could not be moved/copied because it does not exist.', ['%file' => $original_source]);
|
Chris@0
|
522 }
|
Chris@0
|
523 return FALSE;
|
Chris@0
|
524 }
|
Chris@0
|
525
|
Chris@0
|
526 // Build a destination URI if necessary.
|
Chris@0
|
527 if (!isset($destination)) {
|
Chris@0
|
528 $destination = file_build_uri(drupal_basename($source));
|
Chris@0
|
529 }
|
Chris@0
|
530
|
Chris@0
|
531 // Prepare the destination directory.
|
Chris@0
|
532 if (file_prepare_directory($destination)) {
|
Chris@0
|
533 // The destination is already a directory, so append the source basename.
|
Chris@0
|
534 $destination = file_stream_wrapper_uri_normalize($destination . '/' . drupal_basename($source));
|
Chris@0
|
535 }
|
Chris@0
|
536 else {
|
Chris@0
|
537 // Perhaps $destination is a dir/file?
|
Chris@0
|
538 $dirname = drupal_dirname($destination);
|
Chris@0
|
539 if (!file_prepare_directory($dirname)) {
|
Chris@0
|
540 // The destination is not valid.
|
Chris@0
|
541 $logger->notice('File %file could not be moved/copied because the destination directory %destination is not configured correctly.', ['%file' => $original_source, '%destination' => $dirname]);
|
Chris@0
|
542 drupal_set_message(t('The specified file %file could not be moved/copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', ['%file' => $original_source]), 'error');
|
Chris@0
|
543 return FALSE;
|
Chris@0
|
544 }
|
Chris@0
|
545 }
|
Chris@0
|
546
|
Chris@0
|
547 // Determine whether we can perform this operation based on overwrite rules.
|
Chris@0
|
548 $destination = file_destination($destination, $replace);
|
Chris@0
|
549 if ($destination === FALSE) {
|
Chris@0
|
550 drupal_set_message(t('The file %file could not be moved/copied because a file by that name already exists in the destination directory.', ['%file' => $original_source]), 'error');
|
Chris@0
|
551 $logger->notice('File %file could not be moved/copied because a file by that name already exists in the destination directory (%destination)', ['%file' => $original_source, '%destination' => $destination]);
|
Chris@0
|
552 return FALSE;
|
Chris@0
|
553 }
|
Chris@0
|
554
|
Chris@0
|
555 // Assert that the source and destination filenames are not the same.
|
Chris@0
|
556 $real_source = $file_system->realpath($source);
|
Chris@0
|
557 $real_destination = $file_system->realpath($destination);
|
Chris@0
|
558 if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) {
|
Chris@0
|
559 drupal_set_message(t('The specified file %file was not moved/copied because it would overwrite itself.', ['%file' => $source]), 'error');
|
Chris@0
|
560 $logger->notice('File %file could not be moved/copied because it would overwrite itself.', ['%file' => $source]);
|
Chris@0
|
561 return FALSE;
|
Chris@0
|
562 }
|
Chris@0
|
563 // Make sure the .htaccess files are present.
|
Chris@0
|
564 file_ensure_htaccess();
|
Chris@0
|
565 return TRUE;
|
Chris@0
|
566 }
|
Chris@0
|
567
|
Chris@0
|
568 /**
|
Chris@0
|
569 * Constructs a URI to Drupal's default files location given a relative path.
|
Chris@0
|
570 */
|
Chris@0
|
571 function file_build_uri($path) {
|
Chris@0
|
572 $uri = file_default_scheme() . '://' . $path;
|
Chris@0
|
573 return file_stream_wrapper_uri_normalize($uri);
|
Chris@0
|
574 }
|
Chris@0
|
575
|
Chris@0
|
576 /**
|
Chris@0
|
577 * Determines the destination path for a file.
|
Chris@0
|
578 *
|
Chris@0
|
579 * @param $destination
|
Chris@0
|
580 * A string specifying the desired final URI or filepath.
|
Chris@0
|
581 * @param $replace
|
Chris@0
|
582 * Replace behavior when the destination file already exists.
|
Chris@0
|
583 * - FILE_EXISTS_REPLACE - Replace the existing file.
|
Chris@0
|
584 * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
Chris@0
|
585 * unique.
|
Chris@0
|
586 * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
Chris@0
|
587 *
|
Chris@0
|
588 * @return
|
Chris@0
|
589 * The destination filepath, or FALSE if the file already exists
|
Chris@0
|
590 * and FILE_EXISTS_ERROR is specified.
|
Chris@0
|
591 */
|
Chris@0
|
592 function file_destination($destination, $replace) {
|
Chris@0
|
593 if (file_exists($destination)) {
|
Chris@0
|
594 switch ($replace) {
|
Chris@0
|
595 case FILE_EXISTS_REPLACE:
|
Chris@0
|
596 // Do nothing here, we want to overwrite the existing file.
|
Chris@0
|
597 break;
|
Chris@0
|
598
|
Chris@0
|
599 case FILE_EXISTS_RENAME:
|
Chris@0
|
600 $basename = drupal_basename($destination);
|
Chris@0
|
601 $directory = drupal_dirname($destination);
|
Chris@0
|
602 $destination = file_create_filename($basename, $directory);
|
Chris@0
|
603 break;
|
Chris@0
|
604
|
Chris@0
|
605 case FILE_EXISTS_ERROR:
|
Chris@0
|
606 // Error reporting handled by calling function.
|
Chris@0
|
607 return FALSE;
|
Chris@0
|
608 }
|
Chris@0
|
609 }
|
Chris@0
|
610 return $destination;
|
Chris@0
|
611 }
|
Chris@0
|
612
|
Chris@0
|
613 /**
|
Chris@0
|
614 * Moves a file to a new location without database changes or hook invocation.
|
Chris@0
|
615 *
|
Chris@0
|
616 * This is a powerful function that in many ways performs like an advanced
|
Chris@0
|
617 * version of rename().
|
Chris@0
|
618 * - Checks if $source and $destination are valid and readable/writable.
|
Chris@0
|
619 * - Checks that $source is not equal to $destination; if they are an error
|
Chris@0
|
620 * is reported.
|
Chris@0
|
621 * - If file already exists in $destination either the call will error out,
|
Chris@0
|
622 * replace the file or rename the file based on the $replace parameter.
|
Chris@0
|
623 * - Works around a PHP bug where rename() does not properly support streams if
|
Chris@0
|
624 * safe_mode or open_basedir are enabled.
|
Chris@0
|
625 * @see https://bugs.php.net/bug.php?id=60456
|
Chris@0
|
626 *
|
Chris@0
|
627 * @param $source
|
Chris@0
|
628 * A string specifying the filepath or URI of the source file.
|
Chris@0
|
629 * @param $destination
|
Chris@0
|
630 * A URI containing the destination that $source should be moved to. The
|
Chris@0
|
631 * URI may be a bare filepath (without a scheme) and in that case the default
|
Chris@0
|
632 * scheme (file://) will be used. If this value is omitted, Drupal's default
|
Chris@0
|
633 * files scheme will be used, usually "public://".
|
Chris@0
|
634 * @param $replace
|
Chris@0
|
635 * Replace behavior when the destination file already exists:
|
Chris@0
|
636 * - FILE_EXISTS_REPLACE - Replace the existing file.
|
Chris@0
|
637 * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
Chris@0
|
638 * unique.
|
Chris@0
|
639 * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
Chris@0
|
640 *
|
Chris@0
|
641 * @return
|
Chris@0
|
642 * The path to the new file, or FALSE in the event of an error.
|
Chris@0
|
643 *
|
Chris@0
|
644 * @see file_move()
|
Chris@0
|
645 */
|
Chris@0
|
646 function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
|
Chris@0
|
647 if (!file_unmanaged_prepare($source, $destination, $replace)) {
|
Chris@0
|
648 return FALSE;
|
Chris@0
|
649 }
|
Chris@0
|
650 // Ensure compatibility with Windows.
|
Chris@0
|
651 // @see drupal_unlink()
|
Chris@0
|
652 if ((substr(PHP_OS, 0, 3) == 'WIN') && (!file_stream_wrapper_valid_scheme(file_uri_scheme($source)))) {
|
Chris@0
|
653 chmod($source, 0600);
|
Chris@0
|
654 }
|
Chris@0
|
655 // Attempt to resolve the URIs. This is necessary in certain configurations
|
Chris@0
|
656 // (see above) and can also permit fast moves across local schemes.
|
Chris@0
|
657 $file_system = \Drupal::service('file_system');
|
Chris@0
|
658 $real_source = $file_system->realpath($source) ?: $source;
|
Chris@0
|
659 $real_destination = $file_system->realpath($destination) ?: $destination;
|
Chris@0
|
660 // Perform the move operation.
|
Chris@0
|
661 if (!@rename($real_source, $real_destination)) {
|
Chris@0
|
662 // Fall back to slow copy and unlink procedure. This is necessary for
|
Chris@0
|
663 // renames across schemes that are not local, or where rename() has not been
|
Chris@0
|
664 // implemented. It's not necessary to use drupal_unlink() as the Windows
|
Chris@0
|
665 // issue has already been resolved above.
|
Chris@0
|
666 if (!@copy($real_source, $real_destination) || !@unlink($real_source)) {
|
Chris@0
|
667 \Drupal::logger('file')->error('The specified file %file could not be moved to %destination.', ['%file' => $source, '%destination' => $destination]);
|
Chris@0
|
668 return FALSE;
|
Chris@0
|
669 }
|
Chris@0
|
670 }
|
Chris@0
|
671 // Set the permissions on the new file.
|
Chris@0
|
672 drupal_chmod($destination);
|
Chris@0
|
673 return $destination;
|
Chris@0
|
674 }
|
Chris@0
|
675
|
Chris@0
|
676 /**
|
Chris@0
|
677 * Modifies a filename as needed for security purposes.
|
Chris@0
|
678 *
|
Chris@0
|
679 * Munging a file name prevents unknown file extensions from masking exploit
|
Chris@0
|
680 * files. When web servers such as Apache decide how to process a URL request,
|
Chris@0
|
681 * they use the file extension. If the extension is not recognized, Apache
|
Chris@0
|
682 * skips that extension and uses the previous file extension. For example, if
|
Chris@0
|
683 * the file being requested is exploit.php.pps, and Apache does not recognize
|
Chris@0
|
684 * the '.pps' extension, it treats the file as PHP and executes it. To make
|
Chris@0
|
685 * this file name safe for Apache and prevent it from executing as PHP, the
|
Chris@0
|
686 * .php extension is "munged" into .php_, making the safe file name
|
Chris@0
|
687 * exploit.php_.pps.
|
Chris@0
|
688 *
|
Chris@0
|
689 * Specifically, this function adds an underscore to all extensions that are
|
Chris@0
|
690 * between 2 and 5 characters in length, internal to the file name, and not
|
Chris@0
|
691 * included in $extensions.
|
Chris@0
|
692 *
|
Chris@0
|
693 * Function behavior is also controlled by the configuration
|
Chris@0
|
694 * 'system.file:allow_insecure_uploads'. If it evaluates to TRUE, no alterations
|
Chris@0
|
695 * will be made, if it evaluates to FALSE, the filename is 'munged'. *
|
Chris@0
|
696 * @param $filename
|
Chris@0
|
697 * File name to modify.
|
Chris@0
|
698 * @param $extensions
|
Chris@0
|
699 * A space-separated list of extensions that should not be altered.
|
Chris@0
|
700 * @param $alerts
|
Chris@0
|
701 * If TRUE, drupal_set_message() will be called to display a message if the
|
Chris@0
|
702 * file name was changed.
|
Chris@0
|
703 *
|
Chris@0
|
704 * @return string
|
Chris@0
|
705 * The potentially modified $filename.
|
Chris@0
|
706 */
|
Chris@0
|
707 function file_munge_filename($filename, $extensions, $alerts = TRUE) {
|
Chris@0
|
708 $original = $filename;
|
Chris@0
|
709
|
Chris@0
|
710 // Allow potentially insecure uploads for very savvy users and admin
|
Chris@0
|
711 if (!\Drupal::config('system.file')->get('allow_insecure_uploads')) {
|
Chris@0
|
712 // Remove any null bytes. See
|
Chris@0
|
713 // http://php.net/manual/security.filesystem.nullbytes.php
|
Chris@0
|
714 $filename = str_replace(chr(0), '', $filename);
|
Chris@0
|
715
|
Chris@0
|
716 $whitelist = array_unique(explode(' ', strtolower(trim($extensions))));
|
Chris@0
|
717
|
Chris@0
|
718 // Split the filename up by periods. The first part becomes the basename
|
Chris@0
|
719 // the last part the final extension.
|
Chris@0
|
720 $filename_parts = explode('.', $filename);
|
Chris@0
|
721 // Remove file basename.
|
Chris@0
|
722 $new_filename = array_shift($filename_parts);
|
Chris@0
|
723 // Remove final extension.
|
Chris@0
|
724 $final_extension = array_pop($filename_parts);
|
Chris@0
|
725
|
Chris@0
|
726 // Loop through the middle parts of the name and add an underscore to the
|
Chris@0
|
727 // end of each section that could be a file extension but isn't in the list
|
Chris@0
|
728 // of allowed extensions.
|
Chris@0
|
729 foreach ($filename_parts as $filename_part) {
|
Chris@0
|
730 $new_filename .= '.' . $filename_part;
|
Chris@0
|
731 if (!in_array(strtolower($filename_part), $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
|
Chris@0
|
732 $new_filename .= '_';
|
Chris@0
|
733 }
|
Chris@0
|
734 }
|
Chris@0
|
735 $filename = $new_filename . '.' . $final_extension;
|
Chris@0
|
736
|
Chris@0
|
737 if ($alerts && $original != $filename) {
|
Chris@0
|
738 drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $filename]));
|
Chris@0
|
739 }
|
Chris@0
|
740 }
|
Chris@0
|
741
|
Chris@0
|
742 return $filename;
|
Chris@0
|
743 }
|
Chris@0
|
744
|
Chris@0
|
745 /**
|
Chris@0
|
746 * Undoes the effect of file_munge_filename().
|
Chris@0
|
747 *
|
Chris@0
|
748 * @param $filename
|
Chris@0
|
749 * String with the filename to be unmunged.
|
Chris@0
|
750 *
|
Chris@0
|
751 * @return
|
Chris@0
|
752 * An unmunged filename string.
|
Chris@0
|
753 */
|
Chris@0
|
754 function file_unmunge_filename($filename) {
|
Chris@0
|
755 return str_replace('_.', '.', $filename);
|
Chris@0
|
756 }
|
Chris@0
|
757
|
Chris@0
|
758 /**
|
Chris@0
|
759 * Creates a full file path from a directory and filename.
|
Chris@0
|
760 *
|
Chris@0
|
761 * If a file with the specified name already exists, an alternative will be
|
Chris@0
|
762 * used.
|
Chris@0
|
763 *
|
Chris@0
|
764 * @param $basename
|
Chris@0
|
765 * String filename
|
Chris@0
|
766 * @param $directory
|
Chris@0
|
767 * String containing the directory or parent URI.
|
Chris@0
|
768 *
|
Chris@0
|
769 * @return
|
Chris@0
|
770 * File path consisting of $directory and a unique filename based off
|
Chris@0
|
771 * of $basename.
|
Chris@0
|
772 */
|
Chris@0
|
773 function file_create_filename($basename, $directory) {
|
Chris@0
|
774 // Strip control characters (ASCII value < 32). Though these are allowed in
|
Chris@0
|
775 // some filesystems, not many applications handle them well.
|
Chris@0
|
776 $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
|
Chris@0
|
777 if (substr(PHP_OS, 0, 3) == 'WIN') {
|
Chris@0
|
778 // These characters are not allowed in Windows filenames
|
Chris@0
|
779 $basename = str_replace([':', '*', '?', '"', '<', '>', '|'], '_', $basename);
|
Chris@0
|
780 }
|
Chris@0
|
781
|
Chris@0
|
782 // A URI or path may already have a trailing slash or look like "public://".
|
Chris@0
|
783 if (substr($directory, -1) == '/') {
|
Chris@0
|
784 $separator = '';
|
Chris@0
|
785 }
|
Chris@0
|
786 else {
|
Chris@0
|
787 $separator = '/';
|
Chris@0
|
788 }
|
Chris@0
|
789
|
Chris@0
|
790 $destination = $directory . $separator . $basename;
|
Chris@0
|
791
|
Chris@0
|
792 if (file_exists($destination)) {
|
Chris@0
|
793 // Destination file already exists, generate an alternative.
|
Chris@0
|
794 $pos = strrpos($basename, '.');
|
Chris@0
|
795 if ($pos !== FALSE) {
|
Chris@0
|
796 $name = substr($basename, 0, $pos);
|
Chris@0
|
797 $ext = substr($basename, $pos);
|
Chris@0
|
798 }
|
Chris@0
|
799 else {
|
Chris@0
|
800 $name = $basename;
|
Chris@0
|
801 $ext = '';
|
Chris@0
|
802 }
|
Chris@0
|
803
|
Chris@0
|
804 $counter = 0;
|
Chris@0
|
805 do {
|
Chris@0
|
806 $destination = $directory . $separator . $name . '_' . $counter++ . $ext;
|
Chris@0
|
807 } while (file_exists($destination));
|
Chris@0
|
808 }
|
Chris@0
|
809
|
Chris@0
|
810 return $destination;
|
Chris@0
|
811 }
|
Chris@0
|
812
|
Chris@0
|
813 /**
|
Chris@0
|
814 * Deletes a file and its database record.
|
Chris@0
|
815 *
|
Chris@0
|
816 * Instead of directly deleting a file, it is strongly recommended to delete
|
Chris@0
|
817 * file usages instead. That will automatically mark the file as temporary and
|
Chris@0
|
818 * remove it during cleanup.
|
Chris@0
|
819 *
|
Chris@0
|
820 * @param $fid
|
Chris@0
|
821 * The file id.
|
Chris@0
|
822 *
|
Chris@0
|
823 * @see file_unmanaged_delete()
|
Chris@0
|
824 * @see \Drupal\file\FileUsage\FileUsageBase::delete()
|
Chris@0
|
825 */
|
Chris@0
|
826 function file_delete($fid) {
|
Chris@0
|
827 return file_delete_multiple([$fid]);
|
Chris@0
|
828 }
|
Chris@0
|
829
|
Chris@0
|
830 /**
|
Chris@0
|
831 * Deletes files.
|
Chris@0
|
832 *
|
Chris@0
|
833 * Instead of directly deleting a file, it is strongly recommended to delete
|
Chris@0
|
834 * file usages instead. That will automatically mark the file as temporary and
|
Chris@0
|
835 * remove it during cleanup.
|
Chris@0
|
836 *
|
Chris@0
|
837 * @param $fid
|
Chris@0
|
838 * The file id.
|
Chris@0
|
839 *
|
Chris@0
|
840 * @see file_unmanaged_delete()
|
Chris@0
|
841 * @see \Drupal\file\FileUsage\FileUsageBase::delete()
|
Chris@0
|
842 */
|
Chris@0
|
843 function file_delete_multiple(array $fids) {
|
Chris@0
|
844 entity_delete_multiple('file', $fids);
|
Chris@0
|
845 }
|
Chris@0
|
846
|
Chris@0
|
847 /**
|
Chris@0
|
848 * Deletes a file without database changes or hook invocations.
|
Chris@0
|
849 *
|
Chris@0
|
850 * This function should be used when the file to be deleted does not have an
|
Chris@0
|
851 * entry recorded in the files table.
|
Chris@0
|
852 *
|
Chris@0
|
853 * @param $path
|
Chris@0
|
854 * A string containing a file path or (streamwrapper) URI.
|
Chris@0
|
855 *
|
Chris@0
|
856 * @return
|
Chris@0
|
857 * TRUE for success or path does not exist, or FALSE in the event of an
|
Chris@0
|
858 * error.
|
Chris@0
|
859 *
|
Chris@0
|
860 * @see file_delete()
|
Chris@0
|
861 * @see file_unmanaged_delete_recursive()
|
Chris@0
|
862 */
|
Chris@0
|
863 function file_unmanaged_delete($path) {
|
Chris@0
|
864 if (is_file($path)) {
|
Chris@0
|
865 return drupal_unlink($path);
|
Chris@0
|
866 }
|
Chris@0
|
867 $logger = \Drupal::logger('file');
|
Chris@0
|
868 if (is_dir($path)) {
|
Chris@0
|
869 $logger->error('%path is a directory and cannot be removed using file_unmanaged_delete().', ['%path' => $path]);
|
Chris@0
|
870 return FALSE;
|
Chris@0
|
871 }
|
Chris@0
|
872 // Return TRUE for non-existent file, but log that nothing was actually
|
Chris@0
|
873 // deleted, as the current state is the intended result.
|
Chris@0
|
874 if (!file_exists($path)) {
|
Chris@0
|
875 $logger->notice('The file %path was not deleted because it does not exist.', ['%path' => $path]);
|
Chris@0
|
876 return TRUE;
|
Chris@0
|
877 }
|
Chris@0
|
878 // We cannot handle anything other than files and directories. Log an error
|
Chris@0
|
879 // for everything else (sockets, symbolic links, etc).
|
Chris@0
|
880 $logger->error('The file %path is not of a recognized type so it was not deleted.', ['%path' => $path]);
|
Chris@0
|
881 return FALSE;
|
Chris@0
|
882 }
|
Chris@0
|
883
|
Chris@0
|
884 /**
|
Chris@0
|
885 * Deletes all files and directories in the specified filepath recursively.
|
Chris@0
|
886 *
|
Chris@0
|
887 * If the specified path is a directory then the function will call itself
|
Chris@0
|
888 * recursively to process the contents. Once the contents have been removed the
|
Chris@0
|
889 * directory will also be removed.
|
Chris@0
|
890 *
|
Chris@0
|
891 * If the specified path is a file then it will be passed to
|
Chris@0
|
892 * file_unmanaged_delete().
|
Chris@0
|
893 *
|
Chris@0
|
894 * Note that this only deletes visible files with write permission.
|
Chris@0
|
895 *
|
Chris@0
|
896 * @param $path
|
Chris@0
|
897 * A string containing either an URI or a file or directory path.
|
Chris@0
|
898 * @param callable $callback
|
Chris@0
|
899 * (optional) Callback function to run on each file prior to deleting it and
|
Chris@0
|
900 * on each directory prior to traversing it. For example, can be used to
|
Chris@0
|
901 * modify permissions.
|
Chris@0
|
902 *
|
Chris@0
|
903 * @return
|
Chris@0
|
904 * TRUE for success or if path does not exist, FALSE in the event of an
|
Chris@0
|
905 * error.
|
Chris@0
|
906 *
|
Chris@0
|
907 * @see file_unmanaged_delete()
|
Chris@0
|
908 */
|
Chris@0
|
909 function file_unmanaged_delete_recursive($path, $callback = NULL) {
|
Chris@0
|
910 if (isset($callback)) {
|
Chris@0
|
911 call_user_func($callback, $path);
|
Chris@0
|
912 }
|
Chris@0
|
913 if (is_dir($path)) {
|
Chris@0
|
914 $dir = dir($path);
|
Chris@0
|
915 while (($entry = $dir->read()) !== FALSE) {
|
Chris@0
|
916 if ($entry == '.' || $entry == '..') {
|
Chris@0
|
917 continue;
|
Chris@0
|
918 }
|
Chris@0
|
919 $entry_path = $path . '/' . $entry;
|
Chris@0
|
920 file_unmanaged_delete_recursive($entry_path, $callback);
|
Chris@0
|
921 }
|
Chris@0
|
922 $dir->close();
|
Chris@0
|
923
|
Chris@0
|
924 return drupal_rmdir($path);
|
Chris@0
|
925 }
|
Chris@0
|
926 return file_unmanaged_delete($path);
|
Chris@0
|
927 }
|
Chris@0
|
928
|
Chris@0
|
929
|
Chris@0
|
930 /**
|
Chris@0
|
931 * Moves an uploaded file to a new location.
|
Chris@0
|
932 *
|
Chris@0
|
933 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
934 * Use \Drupal\Core\File\FileSystem::moveUploadedFile().
|
Chris@0
|
935 *
|
Chris@0
|
936 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
937 */
|
Chris@0
|
938 function drupal_move_uploaded_file($filename, $uri) {
|
Chris@0
|
939 return \Drupal::service('file_system')->moveUploadedFile($filename, $uri);
|
Chris@0
|
940 }
|
Chris@0
|
941
|
Chris@0
|
942 /**
|
Chris@0
|
943 * Saves a file to the specified destination without invoking file API.
|
Chris@0
|
944 *
|
Chris@0
|
945 * This function is identical to file_save_data() except the file will not be
|
Chris@0
|
946 * saved to the {file_managed} table and none of the file_* hooks will be
|
Chris@0
|
947 * called.
|
Chris@0
|
948 *
|
Chris@0
|
949 * @param $data
|
Chris@0
|
950 * A string containing the contents of the file.
|
Chris@0
|
951 * @param $destination
|
Chris@0
|
952 * A string containing the destination location. This must be a stream wrapper
|
Chris@0
|
953 * URI. If no value is provided, a randomized name will be generated and the
|
Chris@0
|
954 * file will be saved using Drupal's default files scheme, usually
|
Chris@0
|
955 * "public://".
|
Chris@0
|
956 * @param $replace
|
Chris@0
|
957 * Replace behavior when the destination file already exists:
|
Chris@0
|
958 * - FILE_EXISTS_REPLACE - Replace the existing file.
|
Chris@0
|
959 * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
|
Chris@0
|
960 * unique.
|
Chris@0
|
961 * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
|
Chris@0
|
962 *
|
Chris@0
|
963 * @return
|
Chris@0
|
964 * A string with the path of the resulting file, or FALSE on error.
|
Chris@0
|
965 *
|
Chris@0
|
966 * @see file_save_data()
|
Chris@0
|
967 */
|
Chris@0
|
968 function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
|
Chris@0
|
969 // Write the data to a temporary file.
|
Chris@0
|
970 $temp_name = drupal_tempnam('temporary://', 'file');
|
Chris@0
|
971 if (file_put_contents($temp_name, $data) === FALSE) {
|
Chris@0
|
972 drupal_set_message(t('The file could not be created.'), 'error');
|
Chris@0
|
973 return FALSE;
|
Chris@0
|
974 }
|
Chris@0
|
975
|
Chris@0
|
976 // Move the file to its final destination.
|
Chris@0
|
977 return file_unmanaged_move($temp_name, $destination, $replace);
|
Chris@0
|
978 }
|
Chris@0
|
979
|
Chris@0
|
980 /**
|
Chris@0
|
981 * Finds all files that match a given mask in a given directory.
|
Chris@0
|
982 *
|
Chris@0
|
983 * Directories and files beginning with a dot are excluded; this prevents
|
Chris@0
|
984 * hidden files and directories (such as SVN working directories) from being
|
Chris@0
|
985 * scanned. Use the umask option to skip configuration directories to
|
Chris@0
|
986 * eliminate the possibility of accidentally exposing configuration
|
Chris@0
|
987 * information. Also, you can use the base directory, recurse, and min_depth
|
Chris@0
|
988 * options to improve performance by limiting how much of the filesystem has
|
Chris@0
|
989 * to be traversed.
|
Chris@0
|
990 *
|
Chris@0
|
991 * @param $dir
|
Chris@0
|
992 * The base directory or URI to scan, without trailing slash.
|
Chris@0
|
993 * @param $mask
|
Chris@0
|
994 * The preg_match() regular expression for files to be included.
|
Chris@0
|
995 * @param $options
|
Chris@0
|
996 * An associative array of additional options, with the following elements:
|
Chris@0
|
997 * - 'nomask': The preg_match() regular expression for files to be excluded.
|
Chris@0
|
998 * Defaults to the 'file_scan_ignore_directories' setting.
|
Chris@0
|
999 * - 'callback': The callback function to call for each match. There is no
|
Chris@0
|
1000 * default callback.
|
Chris@0
|
1001 * - 'recurse': When TRUE, the directory scan will recurse the entire tree
|
Chris@0
|
1002 * starting at the provided directory. Defaults to TRUE.
|
Chris@0
|
1003 * - 'key': The key to be used for the returned associative array of files.
|
Chris@0
|
1004 * Possible values are 'uri', for the file's URI; 'filename', for the
|
Chris@0
|
1005 * basename of the file; and 'name' for the name of the file without the
|
Chris@0
|
1006 * extension. Defaults to 'uri'.
|
Chris@0
|
1007 * - 'min_depth': Minimum depth of directories to return files from. Defaults
|
Chris@0
|
1008 * to 0.
|
Chris@0
|
1009 * @param $depth
|
Chris@0
|
1010 * The current depth of recursion. This parameter is only used internally and
|
Chris@0
|
1011 * should not be passed in.
|
Chris@0
|
1012 *
|
Chris@0
|
1013 * @return
|
Chris@0
|
1014 * An associative array (keyed on the chosen key) of objects with 'uri',
|
Chris@0
|
1015 * 'filename', and 'name' properties corresponding to the matched files.
|
Chris@0
|
1016 */
|
Chris@0
|
1017 function file_scan_directory($dir, $mask, $options = [], $depth = 0) {
|
Chris@0
|
1018 // Merge in defaults.
|
Chris@0
|
1019 $options += [
|
Chris@0
|
1020 'callback' => 0,
|
Chris@0
|
1021 'recurse' => TRUE,
|
Chris@0
|
1022 'key' => 'uri',
|
Chris@0
|
1023 'min_depth' => 0,
|
Chris@0
|
1024 ];
|
Chris@0
|
1025 // Normalize $dir only once.
|
Chris@0
|
1026 if ($depth == 0) {
|
Chris@0
|
1027 $dir = file_stream_wrapper_uri_normalize($dir);
|
Chris@0
|
1028 $dir_has_slash = (substr($dir, -1) === '/');
|
Chris@0
|
1029 }
|
Chris@0
|
1030
|
Chris@0
|
1031 // Allow directories specified in settings.php to be ignored. You can use this
|
Chris@0
|
1032 // to not check for files in common special-purpose directories. For example,
|
Chris@0
|
1033 // node_modules and bower_components. Ignoring irrelevant directories is a
|
Chris@0
|
1034 // performance boost.
|
Chris@0
|
1035 if (!isset($options['nomask'])) {
|
Chris@0
|
1036 $ignore_directories = Settings::get('file_scan_ignore_directories', []);
|
Chris@0
|
1037 array_walk($ignore_directories, function (&$value) {
|
Chris@0
|
1038 $value = preg_quote($value, '/');
|
Chris@0
|
1039 });
|
Chris@0
|
1040 $default_nomask = '/^' . implode('|', $ignore_directories) . '$/';
|
Chris@0
|
1041 }
|
Chris@0
|
1042
|
Chris@0
|
1043 $options['key'] = in_array($options['key'], ['uri', 'filename', 'name']) ? $options['key'] : 'uri';
|
Chris@0
|
1044 $files = [];
|
Chris@0
|
1045 // Avoid warnings when opendir does not have the permissions to open a
|
Chris@0
|
1046 // directory.
|
Chris@0
|
1047 if (is_dir($dir)) {
|
Chris@0
|
1048 if ($handle = @opendir($dir)) {
|
Chris@0
|
1049 while (FALSE !== ($filename = readdir($handle))) {
|
Chris@0
|
1050 // Skip this file if it matches the nomask or starts with a dot.
|
Chris@0
|
1051 if ($filename[0] != '.'
|
Chris@0
|
1052 && !(isset($options['nomask']) && preg_match($options['nomask'], $filename))
|
Chris@0
|
1053 && !(!empty($default_nomask) && preg_match($default_nomask, $filename))
|
Chris@0
|
1054 ) {
|
Chris@0
|
1055 if ($depth == 0 && $dir_has_slash) {
|
Chris@0
|
1056 $uri = "$dir$filename";
|
Chris@0
|
1057 }
|
Chris@0
|
1058 else {
|
Chris@0
|
1059 $uri = "$dir/$filename";
|
Chris@0
|
1060 }
|
Chris@0
|
1061 if ($options['recurse'] && is_dir($uri)) {
|
Chris@0
|
1062 // Give priority to files in this folder by merging them in after
|
Chris@0
|
1063 // any subdirectory files.
|
Chris@0
|
1064 $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);
|
Chris@0
|
1065 }
|
Chris@0
|
1066 elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {
|
Chris@0
|
1067 // Always use this match over anything already set in $files with
|
Chris@0
|
1068 // the same $options['key'].
|
Chris@0
|
1069 $file = new stdClass();
|
Chris@0
|
1070 $file->uri = $uri;
|
Chris@0
|
1071 $file->filename = $filename;
|
Chris@0
|
1072 $file->name = pathinfo($filename, PATHINFO_FILENAME);
|
Chris@0
|
1073 $key = $options['key'];
|
Chris@0
|
1074 $files[$file->$key] = $file;
|
Chris@0
|
1075 if ($options['callback']) {
|
Chris@0
|
1076 $options['callback']($uri);
|
Chris@0
|
1077 }
|
Chris@0
|
1078 }
|
Chris@0
|
1079 }
|
Chris@0
|
1080 }
|
Chris@0
|
1081
|
Chris@0
|
1082 closedir($handle);
|
Chris@0
|
1083 }
|
Chris@0
|
1084 else {
|
Chris@0
|
1085 \Drupal::logger('file')->error('@dir can not be opened', ['@dir' => $dir]);
|
Chris@0
|
1086 }
|
Chris@0
|
1087 }
|
Chris@0
|
1088
|
Chris@0
|
1089 return $files;
|
Chris@0
|
1090 }
|
Chris@0
|
1091
|
Chris@0
|
1092 /**
|
Chris@0
|
1093 * Determines the maximum file upload size by querying the PHP settings.
|
Chris@0
|
1094 *
|
Chris@0
|
1095 * @return
|
Chris@0
|
1096 * A file size limit in bytes based on the PHP upload_max_filesize and
|
Chris@0
|
1097 * post_max_size
|
Chris@0
|
1098 */
|
Chris@0
|
1099 function file_upload_max_size() {
|
Chris@0
|
1100 static $max_size = -1;
|
Chris@0
|
1101
|
Chris@0
|
1102 if ($max_size < 0) {
|
Chris@0
|
1103 // Start with post_max_size.
|
Chris@0
|
1104 $max_size = Bytes::toInt(ini_get('post_max_size'));
|
Chris@0
|
1105
|
Chris@0
|
1106 // If upload_max_size is less, then reduce. Except if upload_max_size is
|
Chris@0
|
1107 // zero, which indicates no limit.
|
Chris@0
|
1108 $upload_max = Bytes::toInt(ini_get('upload_max_filesize'));
|
Chris@0
|
1109 if ($upload_max > 0 && $upload_max < $max_size) {
|
Chris@0
|
1110 $max_size = $upload_max;
|
Chris@0
|
1111 }
|
Chris@0
|
1112 }
|
Chris@0
|
1113 return $max_size;
|
Chris@0
|
1114 }
|
Chris@0
|
1115
|
Chris@0
|
1116 /**
|
Chris@0
|
1117 * Sets the permissions on a file or directory.
|
Chris@0
|
1118 *
|
Chris@0
|
1119 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1120 * Use \Drupal\Core\File\FileSystem::chmod().
|
Chris@0
|
1121 *
|
Chris@0
|
1122 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1123 */
|
Chris@0
|
1124 function drupal_chmod($uri, $mode = NULL) {
|
Chris@0
|
1125 return \Drupal::service('file_system')->chmod($uri, $mode);
|
Chris@0
|
1126 }
|
Chris@0
|
1127
|
Chris@0
|
1128 /**
|
Chris@0
|
1129 * Deletes a file.
|
Chris@0
|
1130 *
|
Chris@0
|
1131 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1132 * Use \Drupal\Core\File\FileSystem::unlink().
|
Chris@0
|
1133 *
|
Chris@0
|
1134 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1135 */
|
Chris@0
|
1136 function drupal_unlink($uri, $context = NULL) {
|
Chris@0
|
1137 return \Drupal::service('file_system')->unlink($uri, $context);
|
Chris@0
|
1138 }
|
Chris@0
|
1139
|
Chris@0
|
1140 /**
|
Chris@0
|
1141 * Resolves the absolute filepath of a local URI or filepath.
|
Chris@0
|
1142 *
|
Chris@0
|
1143 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1144 * Use \Drupal\Core\File\FileSystem::realpath().
|
Chris@0
|
1145 *
|
Chris@0
|
1146 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1147 */
|
Chris@0
|
1148 function drupal_realpath($uri) {
|
Chris@0
|
1149 return \Drupal::service('file_system')->realpath($uri);
|
Chris@0
|
1150 }
|
Chris@0
|
1151
|
Chris@0
|
1152 /**
|
Chris@0
|
1153 * Gets the name of the directory from a given path.
|
Chris@0
|
1154 *
|
Chris@0
|
1155 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1156 * Use \Drupal\Core\File\FileSystem::dirname().
|
Chris@0
|
1157 *
|
Chris@0
|
1158 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1159 */
|
Chris@0
|
1160 function drupal_dirname($uri) {
|
Chris@0
|
1161 return \Drupal::service('file_system')->dirname($uri);
|
Chris@0
|
1162 }
|
Chris@0
|
1163
|
Chris@0
|
1164 /**
|
Chris@0
|
1165 * Gets the filename from a given path.
|
Chris@0
|
1166 *
|
Chris@0
|
1167 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1168 * Use \Drupal\Core\File\FileSystem::basename().
|
Chris@0
|
1169 *
|
Chris@0
|
1170 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1171 */
|
Chris@0
|
1172 function drupal_basename($uri, $suffix = NULL) {
|
Chris@0
|
1173 return \Drupal::service('file_system')->basename($uri, $suffix);
|
Chris@0
|
1174 }
|
Chris@0
|
1175
|
Chris@0
|
1176 /**
|
Chris@0
|
1177 * Creates a directory, optionally creating missing components in the path to
|
Chris@0
|
1178 * the directory.
|
Chris@0
|
1179 *
|
Chris@0
|
1180 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1181 * Use \Drupal\Core\File\FileSystem::mkdir().
|
Chris@0
|
1182 *
|
Chris@0
|
1183 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1184 */
|
Chris@0
|
1185 function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
|
Chris@0
|
1186 return \Drupal::service('file_system')->mkdir($uri, $mode, $recursive, $context);
|
Chris@0
|
1187 }
|
Chris@0
|
1188
|
Chris@0
|
1189 /**
|
Chris@0
|
1190 * Removes a directory.
|
Chris@0
|
1191 *
|
Chris@0
|
1192 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1193 * Use \Drupal\Core\File\FileSystem::rmdir().
|
Chris@0
|
1194 *
|
Chris@0
|
1195 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1196 */
|
Chris@0
|
1197 function drupal_rmdir($uri, $context = NULL) {
|
Chris@0
|
1198 return \Drupal::service('file_system')->rmdir($uri, $context);
|
Chris@0
|
1199 }
|
Chris@0
|
1200
|
Chris@0
|
1201 /**
|
Chris@0
|
1202 * Creates a file with a unique filename in the specified directory.
|
Chris@0
|
1203 *
|
Chris@0
|
1204 * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1205 * Use \Drupal\Core\File\FileSystem::tempnam().
|
Chris@0
|
1206 *
|
Chris@0
|
1207 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1208 */
|
Chris@0
|
1209 function drupal_tempnam($directory, $prefix) {
|
Chris@0
|
1210 return \Drupal::service('file_system')->tempnam($directory, $prefix);
|
Chris@0
|
1211 }
|
Chris@0
|
1212
|
Chris@0
|
1213 /**
|
Chris@0
|
1214 * Gets and sets the path of the configured temporary directory.
|
Chris@0
|
1215 *
|
Chris@0
|
1216 * @return mixed|null
|
Chris@0
|
1217 * A string containing the path to the temporary directory.
|
Chris@0
|
1218 */
|
Chris@0
|
1219 function file_directory_temp() {
|
Chris@0
|
1220 $temporary_directory = \Drupal::config('system.file')->get('path.temporary');
|
Chris@0
|
1221 if (empty($temporary_directory)) {
|
Chris@0
|
1222 // Needs set up.
|
Chris@0
|
1223 $config = \Drupal::configFactory()->getEditable('system.file');
|
Chris@0
|
1224 $temporary_directory = ComponentFileSystem::getOsTemporaryDirectory();
|
Chris@0
|
1225
|
Chris@0
|
1226 if (empty($temporary_directory)) {
|
Chris@0
|
1227 // If no directory has been found default to 'files/tmp'.
|
Chris@0
|
1228 $temporary_directory = PublicStream::basePath() . '/tmp';
|
Chris@0
|
1229
|
Chris@0
|
1230 // Windows accepts paths with either slash (/) or backslash (\), but will
|
Chris@0
|
1231 // not accept a path which contains both a slash and a backslash. Since
|
Chris@0
|
1232 // the 'file_public_path' variable may have either format, we sanitize
|
Chris@0
|
1233 // everything to use slash which is supported on all platforms.
|
Chris@0
|
1234 $temporary_directory = str_replace('\\', '/', $temporary_directory);
|
Chris@0
|
1235 }
|
Chris@0
|
1236 // Save the path of the discovered directory. Do not check config schema on
|
Chris@0
|
1237 // save.
|
Chris@0
|
1238 $config->set('path.temporary', (string) $temporary_directory)->save(TRUE);
|
Chris@0
|
1239 }
|
Chris@0
|
1240
|
Chris@0
|
1241 return $temporary_directory;
|
Chris@0
|
1242 }
|
Chris@0
|
1243
|
Chris@0
|
1244 /**
|
Chris@0
|
1245 * Discovers a writable system-appropriate temporary directory.
|
Chris@0
|
1246 *
|
Chris@0
|
1247 * @return mixed
|
Chris@0
|
1248 * A string containing the path to the temporary directory.
|
Chris@0
|
1249 *
|
Chris@0
|
1250 * @deprecated in Drupal 8.3.x-dev, will be removed before Drupal 9.0.0.
|
Chris@0
|
1251 * Use \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory().
|
Chris@0
|
1252 *
|
Chris@0
|
1253 * @see https://www.drupal.org/node/2418133
|
Chris@0
|
1254 */
|
Chris@0
|
1255 function file_directory_os_temp() {
|
Chris@0
|
1256 return ComponentFileSystem::getOsTemporaryDirectory();
|
Chris@0
|
1257 }
|
Chris@0
|
1258
|
Chris@0
|
1259 /**
|
Chris@0
|
1260 * @} End of "defgroup file".
|
Chris@0
|
1261 */
|