diff --git a/core/core.services.yml b/core/core.services.yml index 9ca753e6a4..a842043e12 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1565,25 +1565,27 @@ services: arguments: [ '@state' ] asset.css.collection_optimizer: class: Drupal\Core\Asset\CssCollectionOptimizer - arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state' ] + arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state', '@file_system'] asset.css.optimizer: class: Drupal\Core\Asset\CssOptimizer asset.css.collection_grouper: class: Drupal\Core\Asset\CssCollectionGrouper asset.css.dumper: class: Drupal\Core\Asset\AssetDumper + arguments: ['@file_system'] asset.js.collection_renderer: class: Drupal\Core\Asset\JsCollectionRenderer arguments: [ '@state' ] asset.js.collection_optimizer: class: Drupal\Core\Asset\JsCollectionOptimizer - arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state' ] + arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state', '@file_system'] asset.js.optimizer: class: Drupal\Core\Asset\JsOptimizer asset.js.collection_grouper: class: Drupal\Core\Asset\JsCollectionGrouper asset.js.dumper: class: Drupal\Core\Asset\AssetDumper + arguments: ['@file_system'] library.discovery: class: Drupal\Core\Asset\LibraryDiscovery arguments: ['@library.discovery.collector'] diff --git a/core/includes/file.inc b/core/includes/file.inc index 27e6eb65b7..2c34b1c4d2 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -6,13 +6,16 @@ */ use Drupal\Component\FileSystem\FileSystem as ComponentFileSystem; -use Drupal\Component\Utility\UrlHelper; use Drupal\Component\PhpStorage\FileStorage; use Drupal\Component\Utility\Bytes; +use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\Exception\FileWriteException; use Drupal\Core\File\FileSystem; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Site\Settings; -use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\StreamWrapper\PrivateStream; +use Drupal\Core\StreamWrapper\PublicStream; /** * Default mode for new directories. See drupal_chmod(). @@ -41,29 +44,44 @@ */ /** - * Flag used by file_prepare_directory() -- create directory if not present. + * Flag used to create a directory if not present. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::CREATE_DIRECTORY. */ -const FILE_CREATE_DIRECTORY = 1; +const FILE_CREATE_DIRECTORY = FileSystemInterface::CREATE_DIRECTORY; /** - * Flag used by file_prepare_directory() -- file permissions may be changed. + * Flag used to indicate file permissions may be changed. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::MODIFY_PERMISSIONS. */ -const FILE_MODIFY_PERMISSIONS = 2; +const FILE_MODIFY_PERMISSIONS = FileSystemInterface::MODIFY_PERMISSIONS; /** * Flag for dealing with existing files: Appends number until name is unique. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::EXISTS_RENAME. */ -const FILE_EXISTS_RENAME = 0; +const FILE_EXISTS_RENAME = FileSystemInterface::EXISTS_RENAME; /** * Flag for dealing with existing files: Replace the existing file. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::EXISTS_REPLACE. */ -const FILE_EXISTS_REPLACE = 1; +const FILE_EXISTS_REPLACE = FileSystemInterface::EXISTS_REPLACE; /** * Flag for dealing with existing files: Do nothing and return FALSE. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::EXISTS_ERROR. */ -const FILE_EXISTS_ERROR = 2; +const FILE_EXISTS_ERROR = FileSystemInterface::EXISTS_ERROR; /** * Indicates that the file is permanent and should not be deleted. @@ -288,29 +306,13 @@ function file_url_transform_relative($file_url) { * @return * TRUE if the directory exists (or was created) and is writable. FALSE * otherwise. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::prepareDirectory(). */ -function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS) { - if (!file_stream_wrapper_valid_scheme(\Drupal::service('file_system')->uriScheme($directory))) { - // Only trim if we're not dealing with a stream. - $directory = rtrim($directory, '/\\'); - } - - // Check if directory exists. - if (!is_dir($directory)) { - // Let mkdir() recursively create directories and use the default directory - // permissions. - if ($options & FILE_CREATE_DIRECTORY) { - return @drupal_mkdir($directory, NULL, TRUE); - } - return FALSE; - } - // The directory exists, so check to see if it is writable. - $writable = is_writable($directory); - if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) { - return drupal_chmod($directory); - } - - return $writable; +function file_prepare_directory(&$directory, $options = FileSystemInterface::MODIFY_PERMISSIONS) { + @trigger_error('file_prepare_directory() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::prepareDirectory(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + return \Drupal::service('file_system')->prepareDirectory($directory, $options); } /** @@ -452,116 +454,39 @@ function file_valid_uri($uri) { * @return * The path to the new file, or FALSE in the event of an error. * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::copy(). + * * @see file_copy() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { - if (!file_unmanaged_prepare($source, $destination, $replace)) { - return FALSE; + @trigger_error('file_unmanaged_copy() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::copy(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + try { + return \Drupal::service('file_system')->copy($source, $destination, $replace); } - // Attempt to resolve the URIs. This is necessary in certain configurations - // (see above). - $file_system = \Drupal::service('file_system'); - $real_source = $file_system->realpath($source) ?: $source; - $real_destination = $file_system->realpath($destination) ?: $destination; - // Perform the copy operation. - if (!@copy($real_source, $real_destination)) { - \Drupal::logger('file')->error('The specified file %file could not be copied to %destination.', ['%file' => $source, '%destination' => $destination]); + catch (FileException $e) { return FALSE; } - // Set the permissions on the new file. - drupal_chmod($destination); - return $destination; } /** * Internal function that prepares the destination for a file_unmanaged_copy or * file_unmanaged_move operation. * - * - Checks if $source and $destination are valid and readable/writable. - * - Checks that $source is not equal to $destination; if they are an error - * is reported. - * - If file already exists in $destination either the call will error out, - * replace the file or rename the file based on the $replace parameter. - * - * @param $source - * A string specifying the filepath or URI of the source file. - * @param $destination - * A URI containing the destination that $source should be moved/copied to. - * The URI may be a bare filepath (without a scheme) and in that case the - * default scheme (file://) will be used. If this value is omitted, Drupal's - * default files scheme will be used, usually "public://". - * @param $replace - * Replace behavior when the destination file already exists: - * - FILE_EXISTS_REPLACE - Replace the existing file. - * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. - * - FILE_EXISTS_ERROR - Do nothing and return FALSE. - * - * @return - * TRUE, or FALSE in the event of an error. + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\UnmanagedFileHandler::prepareDestination() instead. * - * @see file_unmanaged_copy() - * @see file_unmanaged_move() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_prepare($source, &$destination = NULL, $replace = FILE_EXISTS_RENAME) { - $original_source = $source; - $logger = \Drupal::logger('file'); - $file_system = \Drupal::service('file_system'); - - // Assert that the source file actually exists. - if (!file_exists($source)) { - // @todo Replace \Drupal::messenger()->addError() calls with exceptions - // instead. - \Drupal::messenger()->addError(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])); - if (($realpath = $file_system->realpath($original_source)) !== FALSE) { - $logger->notice('File %file (%realpath) could not be moved/copied because it does not exist.', ['%file' => $original_source, '%realpath' => $realpath]); - } - else { - $logger->notice('File %file could not be moved/copied because it does not exist.', ['%file' => $original_source]); - } - return FALSE; - } - - // Build a destination URI if necessary. - if (!isset($destination)) { - $destination = file_build_uri(drupal_basename($source)); - } - - // Prepare the destination directory. - if (file_prepare_directory($destination)) { - // The destination is already a directory, so append the source basename. - $destination = file_stream_wrapper_uri_normalize($destination . '/' . drupal_basename($source)); - } - else { - // Perhaps $destination is a dir/file? - $dirname = drupal_dirname($destination); - if (!file_prepare_directory($dirname)) { - // The destination is not valid. - $logger->notice('File %file could not be moved/copied because the destination directory %destination is not configured correctly.', ['%file' => $original_source, '%destination' => $dirname]); - \Drupal::messenger()->addError(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])); - return FALSE; - } - } - - // Determine whether we can perform this operation based on overwrite rules. - $destination = file_destination($destination, $replace); - if ($destination === FALSE) { - \Drupal::messenger()->addError(t('The file %file could not be moved/copied because a file by that name already exists in the destination directory.', ['%file' => $original_source])); - $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]); - return FALSE; + @trigger_error('file_unmanaged_prepare() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\UnmanagedFileHandler::prepareDestination(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + try { + \Drupal::service('file_system')->prepareDestination($source, $destination, $replace); } - - // Assert that the source and destination filenames are not the same. - $real_source = $file_system->realpath($source); - $real_destination = $file_system->realpath($destination); - if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) { - \Drupal::messenger()->addError(t('The specified file %file was not moved/copied because it would overwrite itself.', ['%file' => $source])); - $logger->notice('File %file could not be moved/copied because it would overwrite itself.', ['%file' => $source]); + catch (FileException $e) { return FALSE; } - // Make sure the .htaccess files are present. - file_ensure_htaccess(); - return TRUE; } /** @@ -587,26 +512,15 @@ function file_build_uri($path) { * @return * The destination filepath, or FALSE if the file already exists * and FILE_EXISTS_ERROR is specified. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename(). + * + * @see https://www.drupal.org/node/3006851 */ function file_destination($destination, $replace) { - if (file_exists($destination)) { - switch ($replace) { - case FILE_EXISTS_REPLACE: - // Do nothing here, we want to overwrite the existing file. - break; - - case FILE_EXISTS_RENAME: - $basename = drupal_basename($destination); - $directory = drupal_dirname($destination); - $destination = file_create_filename($basename, $directory); - break; - - case FILE_EXISTS_ERROR: - // Error reporting handled by calling function. - return FALSE; - } - } - return $destination; + @trigger_error('file_destination() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + return \Drupal::service('file_system')->getDestinationFilename($destination, $replace); } /** @@ -640,36 +554,20 @@ function file_destination($destination, $replace) { * @return * The path to the new file, or FALSE in the event of an error. * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::move(). + * * @see file_move() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { - if (!file_unmanaged_prepare($source, $destination, $replace)) { - return FALSE; - } - // Ensure compatibility with Windows. - // @see drupal_unlink() - if ((substr(PHP_OS, 0, 3) == 'WIN') && (!file_stream_wrapper_valid_scheme(file_uri_scheme($source)))) { - chmod($source, 0600); + @trigger_error('file_unmanaged_move() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::move(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + try { + return \Drupal::service('file_system')->move($source, $destination, $replace); } - // Attempt to resolve the URIs. This is necessary in certain configurations - // (see above) and can also permit fast moves across local schemes. - $file_system = \Drupal::service('file_system'); - $real_source = $file_system->realpath($source) ?: $source; - $real_destination = $file_system->realpath($destination) ?: $destination; - // Perform the move operation. - if (!@rename($real_source, $real_destination)) { - // Fall back to slow copy and unlink procedure. This is necessary for - // renames across schemes that are not local, or where rename() has not been - // implemented. It's not necessary to use drupal_unlink() as the Windows - // issue has already been resolved above. - if (!@copy($real_source, $real_destination) || !@unlink($real_source)) { - \Drupal::logger('file')->error('The specified file %file could not be moved to %destination.', ['%file' => $source, '%destination' => $destination]); - return FALSE; - } + catch (FileException $e) { + return FALSE; } - // Set the permissions on the new file. - drupal_chmod($destination); - return $destination; } /** @@ -768,45 +666,15 @@ function file_unmunge_filename($filename) { * @return * File path consisting of $directory and a unique filename based off * of $basename. + * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::createFilename(). + * + * @see https://www.drupal.org/node/3006851 */ function file_create_filename($basename, $directory) { - // Strip control characters (ASCII value < 32). Though these are allowed in - // some filesystems, not many applications handle them well. - $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename); - if (substr(PHP_OS, 0, 3) == 'WIN') { - // These characters are not allowed in Windows filenames - $basename = str_replace([':', '*', '?', '"', '<', '>', '|'], '_', $basename); - } - - // A URI or path may already have a trailing slash or look like "public://". - if (substr($directory, -1) == '/') { - $separator = ''; - } - else { - $separator = '/'; - } - - $destination = $directory . $separator . $basename; - - if (file_exists($destination)) { - // Destination file already exists, generate an alternative. - $pos = strrpos($basename, '.'); - if ($pos !== FALSE) { - $name = substr($basename, 0, $pos); - $ext = substr($basename, $pos); - } - else { - $name = $basename; - $ext = ''; - } - - $counter = 0; - do { - $destination = $directory . $separator . $name . '_' . $counter++ . $ext; - } while (file_exists($destination)); - } - - return $destination; + @trigger_error('file_create_filename() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::createFilename(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + return \Drupal::service('file_system')->createFilename($basename, $directory); } /** @@ -856,28 +724,21 @@ function file_delete_multiple(array $fids) { * TRUE for success or path does not exist, or FALSE in the event of an * error. * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::delete(). + * * @see file_delete() * @see file_unmanaged_delete_recursive() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_delete($path) { - if (is_file($path)) { - return drupal_unlink($path); + @trigger_error('file_unmanaged_delete() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::delete(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + try { + return \Drupal::service('file_system')->delete($path); } - $logger = \Drupal::logger('file'); - if (is_dir($path)) { - $logger->error('%path is a directory and cannot be removed using file_unmanaged_delete().', ['%path' => $path]); + catch (FileException $e) { return FALSE; } - // Return TRUE for non-existent file, but log that nothing was actually - // deleted, as the current state is the intended result. - if (!file_exists($path)) { - $logger->notice('The file %path was not deleted because it does not exist.', ['%path' => $path]); - return TRUE; - } - // We cannot handle anything other than files and directories. Log an error - // for everything else (sockets, symbolic links, etc). - $logger->error('The file %path is not of a recognized type so it was not deleted.', ['%path' => $path]); - return FALSE; } /** @@ -903,26 +764,21 @@ function file_unmanaged_delete($path) { * TRUE for success or if path does not exist, FALSE in the event of an * error. * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::deleteRecursive(). + * * @see file_unmanaged_delete() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_delete_recursive($path, $callback = NULL) { - if (isset($callback)) { - call_user_func($callback, $path); + @trigger_error('file_unmanaged_delete_recursive() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::deleteRecursive(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $callback = is_callable($callback) ? $callback : NULL; + try { + return \Drupal::service('file_system')->deleteRecursive($path, $callback); } - if (is_dir($path)) { - $dir = dir($path); - while (($entry = $dir->read()) !== FALSE) { - if ($entry == '.' || $entry == '..') { - continue; - } - $entry_path = $path . '/' . $entry; - file_unmanaged_delete_recursive($entry_path, $callback); - } - $dir->close(); - - return drupal_rmdir($path); + catch (FileException $e) { + return FALSE; } - return file_unmanaged_delete($path); } /** @@ -962,18 +818,24 @@ function drupal_move_uploaded_file($filename, $uri) { * @return * A string with the path of the resulting file, or FALSE on error. * + * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0. + * Use \Drupal\Core\File\FileSystemInterface::saveData(). + * * @see file_save_data() + * @see https://www.drupal.org/node/3006851 */ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) { - // Write the data to a temporary file. - $temp_name = drupal_tempnam('temporary://', 'file'); - if (file_put_contents($temp_name, $data) === FALSE) { + @trigger_error('file_unmanaged_save_data() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::saveData(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + try { + return \Drupal::service('file_system')->saveData($data, $destination, $replace); + } + catch (FileWriteException $e) { \Drupal::messenger()->addError(t('The file could not be created.')); return FALSE; } - - // Move the file to its final destination. - return file_unmanaged_move($temp_name, $destination, $replace); + catch (FileException $e) { + return FALSE; + } } /** diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 62e078d54e..31734dbe6b 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -15,6 +15,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormState; use Drupal\Core\Installer\Exception\AlreadyInstalledException; use Drupal\Core\Installer\Exception\InstallerException; @@ -1106,7 +1107,7 @@ function install_base_system(&$install_state) { // configurable. The temporary directory needs to match what is set in each // test types ::prepareEnvironment() step. $temporary_directory = dirname(PublicStream::basePath()) . '/temp'; - file_prepare_directory($temporary_directory, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($temporary_directory, FileSystemInterface::MODIFY_PERMISSIONS | FileSystemInterface::CREATE_DIRECTORY); \Drupal::configFactory()->getEditable('system.file') ->set('path.temporary', $temporary_directory) ->save(); @@ -1915,9 +1916,12 @@ function install_check_translations($langcode, $server_pattern) { $online = FALSE; // First attempt to create or make writable the files directory. - file_prepare_directory($files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $file_system->prepareDirectory($files_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); + // Then, attempt to create or make writable the translations directory. - file_prepare_directory($translations_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $file_system->prepareDirectory($translations_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); // Get values so the requirements errors can be specific. if (drupal_verify_install_file($translations_directory, FILE_EXIST, 'dir')) { diff --git a/core/includes/install.inc b/core/includes/install.inc index b0d33048f5..45a8b141c3 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -7,6 +7,7 @@ use Drupal\Core\Extension\Dependency; use Drupal\Component\Utility\Unicode; +use Drupal\Core\File\FileSystemInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\OpCodeCache; @@ -511,7 +512,7 @@ function drupal_install_config_directories() { // public files directory, which has already been verified to be writable // itself. But if it somehow fails anyway, the installation cannot proceed. // Bail out using a similar error message as in system_requirements(). - if (!file_prepare_directory($config_directories[CONFIG_SYNC_DIRECTORY], FILE_CREATE_DIRECTORY) + if (!\Drupal::service('file_system')->prepareDirectory($config_directories[CONFIG_SYNC_DIRECTORY], FileSystemInterface::CREATE_DIRECTORY) && !file_exists($config_directories[CONFIG_SYNC_DIRECTORY])) { throw new Exception(t('The directory %directory could not be created. To proceed with the installation, either create the directory or ensure that the installer has the permissions to create it automatically. For more information, see the online handbook.', [ '%directory' => config_get_config_directory(CONFIG_SYNC_DIRECTORY), @@ -538,12 +539,13 @@ function drupal_install_config_directories() { * TRUE if the config directory exists and is writable. * * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use - * config_get_config_directory() and file_prepare_directory() instead. + * config_get_config_directory() and + * \Drupal\Core\File\FileSystemInterface::prepareDirectory() instead. * * @see https://www.drupal.org/node/2501187 */ function install_ensure_config_directory($type) { - @trigger_error('install_ensure_config_directory() is deprecated in Drupal 8.1.0 and will be removed before Drupal 9.0.0. Use config_get_config_directory() and file_prepare_directory() instead. See https://www.drupal.org/node/2501187.', E_USER_DEPRECATED); + @trigger_error('install_ensure_config_directory() is deprecated in Drupal 8.1.0 and will be removed before Drupal 9.0.0. Use config_get_config_directory() and \Drupal\Core\File\FileSystemInterface::prepareDirectory() instead. See https://www.drupal.org/node/2501187.', E_USER_DEPRECATED); // The config directory must be defined in settings.php. global $config_directories; if (!isset($config_directories[$type])) { @@ -553,7 +555,7 @@ function install_ensure_config_directory($type) { // directories that the installer creates. else { $config_directory = config_get_config_directory($type); - return file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + return \Drupal::service('file_system')->prepareDirectory($config_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } } diff --git a/core/lib/Drupal/Core/Asset/AssetDumper.php b/core/lib/Drupal/Core/Asset/AssetDumper.php index 227ef0c8b7..3e920c9d24 100644 --- a/core/lib/Drupal/Core/Asset/AssetDumper.php +++ b/core/lib/Drupal/Core/Asset/AssetDumper.php @@ -3,12 +3,31 @@ namespace Drupal\Core\Asset; use Drupal\Component\Utility\Crypt; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; /** * Dumps a CSS or JavaScript asset. */ class AssetDumper implements AssetDumperInterface { + /** + * The file system service. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + + /** + * AssetDumper constructor. + * + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file handler. + */ + public function __construct(FileSystemInterface $fileSystem) { + $this->fileSystem = $fileSystem; + } + /** * {@inheritdoc} * @@ -24,8 +43,13 @@ public function dump($data, $file_extension) { $path = 'public://' . $file_extension; $uri = $path . '/' . $filename; // Create the CSS or JS file. - file_prepare_directory($path, FILE_CREATE_DIRECTORY); - if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) { + $this->fileSystem->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY); + try { + if (!file_exists($uri) && !$this->fileSystem->saveData($data, $uri, FileSystemInterface::EXISTS_REPLACE)) { + return FALSE; + } + } + catch (FileException $e) { return FALSE; } // If CSS/JS gzip compression is enabled and the zlib extension is available @@ -37,7 +61,12 @@ public function dump($data, $file_extension) { // aren't working can set css.gzip to FALSE in order to skip // generating a file that won't be used. if (extension_loaded('zlib') && \Drupal::config('system.performance')->get($file_extension . '.gzip')) { - if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { + try { + if (!file_exists($uri . '.gz') && !$this->fileSystem->saveData(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { + return FALSE; + } + } + catch (FileException $e) { return FALSE; } } diff --git a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php index 30577ef30d..cf8cd73233 100644 --- a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php +++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Asset; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\State\StateInterface; /** @@ -37,6 +38,13 @@ class CssCollectionOptimizer implements AssetCollectionOptimizerInterface { */ protected $state; + /** + * The file system service. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a CssCollectionOptimizer. * @@ -48,12 +56,15 @@ class CssCollectionOptimizer implements AssetCollectionOptimizerInterface { * The dumper for optimized CSS assets. * @param \Drupal\Core\State\StateInterface $state * The state key/value store. + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file system service. */ - public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state) { + public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state, FileSystemInterface $fileSystem) { $this->grouper = $grouper; $this->optimizer = $optimizer; $this->dumper = $dumper; $this->state = $state; + $this->fileSystem = $fileSystem; } /** @@ -178,7 +189,7 @@ public function deleteAll() { $delete_stale = function ($uri) { // Default stale file threshold is 30 days. if (REQUEST_TIME - filemtime($uri) > \Drupal::config('system.performance')->get('stale_file_threshold')) { - file_unmanaged_delete($uri); + $this->fileSystem->delete($uri); } }; file_scan_directory('public://css', '/.*/', ['callback' => $delete_stale]); diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php index e9b8bf2075..7e2079f62d 100644 --- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php +++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Asset; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\State\StateInterface; /** @@ -37,6 +38,13 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface { */ protected $state; + /** + * The file system service. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a JsCollectionOptimizer. * @@ -48,12 +56,15 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface { * The dumper for optimized JS assets. * @param \Drupal\Core\State\StateInterface $state * The state key/value store. + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file system service. */ - public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state) { + public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state, FileSystemInterface $fileSystem) { $this->grouper = $grouper; $this->optimizer = $optimizer; $this->dumper = $dumper; $this->state = $state; + $this->fileSystem = $fileSystem; } /** @@ -180,7 +191,7 @@ public function deleteAll() { $delete_stale = function ($uri) { // Default stale file threshold is 30 days. if (REQUEST_TIME - filemtime($uri) > \Drupal::config('system.performance')->get('stale_file_threshold')) { - file_unmanaged_delete($uri); + $this->fileSystem->delete($uri); } }; file_scan_directory('public://js', '/.*/', ['callback' => $delete_stale]); diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index 42aabb557f..518da73a7f 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -4,6 +4,7 @@ use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Component\Serialization\Exception\InvalidDataTypeException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Serialization\Yaml; /** @@ -44,7 +45,6 @@ class FileStorage implements StorageInterface { public function __construct($directory, $collection = StorageInterface::DEFAULT_COLLECTION) { $this->directory = $directory; $this->collection = $collection; - // Use a NULL File Cache backend by default. This will ensure only the // internal static caching of FileCache is used and thus avoids blowing up // the APCu cache. @@ -76,7 +76,7 @@ public static function getFileExtension() { */ protected function ensureStorage() { $dir = $this->getCollectionDirectory(); - $success = file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $success = \Drupal::service('file_system')->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); // Only create .htaccess file in root directory. if ($dir == $this->directory) { $success = $success && file_save_htaccess($this->directory, TRUE, TRUE); diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index 9afd104ff3..e30230b407 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -6,6 +6,7 @@ */ use Drupal\Core\Database\Database; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Url; use Drupal\Core\Utility\UpdateException; @@ -226,7 +227,7 @@ function hook_modules_installed($modules) { function hook_install() { // Create the styles directory and ensure it's writable. $directory = file_default_scheme() . '://styles'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } /** @@ -282,7 +283,7 @@ function hook_modules_uninstalled($modules) { */ function hook_uninstall() { // Remove the styles directory and generated images. - file_unmanaged_delete_recursive(file_default_scheme() . '://styles'); + \Drupal::service('file_system')->deleteRecursive(file_default_scheme() . '://styles'); } /** diff --git a/core/lib/Drupal/Core/File/Exception/DirectoryNotReadyException.php b/core/lib/Drupal/Core/File/Exception/DirectoryNotReadyException.php new file mode 100644 index 0000000000..7750d7c04d --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/DirectoryNotReadyException.php @@ -0,0 +1,12 @@ +streamWrapperManager->getClass($scheme)); } + /** + * {@inheritdoc} + */ + public function copy($source, $destination = NULL, $replace = self::EXISTS_RENAME) { + $this->prepareDestination($source, $destination, $replace); + + // Perform the copy operation. + if (!@copy($source, $destination)) { + // If the copy failed and realpaths exist, retry the operation using them + // instead. + $real_source = $this->realpath($source) ?: $source; + $real_destination = $this->realpath($destination) ?: $destination; + if ($real_source === FALSE || $real_destination === FALSE || !@copy($real_source, $real_destination)) { + $this->logger->error("The specified file '%source' could not be copied to '%destination'.", [ + '%source' => $source, + '%destination' => $destination, + ]); + throw new FileWriteException(sprintf("The specified file '%s' could not be copied to '%s'.", $source, $destination)); + } + } + + // Set the permissions on the new file. + $this->chmod($destination); + + return $destination; + } + + /** + * {@inheritdoc} + */ + public function delete($path) { + if (is_file($path)) { + if (!$this->unlink($path)) { + $this->logger->error("Failed to unlink file '%path'.", ['%path' => $path]); + throw new FileException(sprintf("Failed to unlink file '%s'.", $path)); + } + return TRUE; + } + + if (is_dir($path)) { + $this->logger->error("Cannot delete '%path' because it is a directory. Use deleteRecursive() instead.", ['%path' => $path]); + throw new NotRegularFileException(sprintf("Cannot delete '%s' because it is a directory. Use deleteRecursive() instead.", $path)); + } + + // Do not throw exception for non-existent file, but return FALSE to + // signal that no action was taken. + if (!file_exists($path)) { + $this->logger->notice('The file %path was not deleted because it does not exist.', ['%path' => $path]); + return TRUE; + } + + // We cannot handle anything other than files and directories. + // Throw an exception for everything else (sockets, symbolic links, etc). + $this->logger->error("The file '%path' is not of a recognized type so it was not deleted.", ['%path' => $path]); + throw new NotRegularFileException(sprintf("The file '%s' is not of a recognized type so it was not deleted.", $path)); + } + + /** + * {@inheritdoc} + */ + public function deleteRecursive($path, callable $callback = NULL) { + if ($callback) { + call_user_func($callback, $path); + } + + if (is_dir($path)) { + $dir = dir($path); + while (($entry = $dir->read()) !== FALSE) { + if ($entry == '.' || $entry == '..') { + continue; + } + $entry_path = $path . '/' . $entry; + $this->deleteRecursive($entry_path, $callback); + } + $dir->close(); + + return $this->rmdir($path); + } + + return $this->delete($path); + } + + /** + * {@inheritdoc} + */ + public function move($source, $destination = NULL, $replace = self::EXISTS_RENAME) { + $this->prepareDestination($source, $destination, $replace); + + // Ensure compatibility with Windows. + // @see \Drupal\Core\File\FileSystemInterface::unlink(). + $scheme = $this->uriScheme($source); + if (!$this->validScheme($scheme) && (substr(PHP_OS, 0, 3) == 'WIN')) { + chmod($source, 0600); + } + // Attempt to resolve the URIs. This is necessary in certain + // configurations (see above) and can also permit fast moves across local + // schemes. + $real_source = $this->realpath($source) ?: $source; + $real_destination = $this->realpath($destination) ?: $destination; + // Perform the move operation. + if (!@rename($real_source, $real_destination)) { + // Fall back to slow copy and unlink procedure. This is necessary for + // renames across schemes that are not local, or where rename() has not + // been implemented. It's not necessary to use drupal_unlink() as the + // Windows issue has already been resolved above. + if (!@copy($real_source, $real_destination)) { + $this->logger->error("The specified file '%source' could not be moved to '%destination'.", [ + '%source' => $source, + '%destination' => $destination, + ]); + throw new FileWriteException(sprintf("The specified file '%s' could not be moved to '%s'.", $source, $destination)); + } + if (!@unlink($real_source)) { + $this->logger->error("The source file '%source' could not be unlinked after copying to '%destination'.", [ + '%source' => $source, + '%destination' => $destination, + ]); + throw new FileException(sprintf("The source file '%s' could not be unlinked after copying to '%s'.", $source, $destination)); + } + } + + // Set the permissions on the new file. + $this->chmod($destination); + + return $destination; + } + + /** + * Prepares the destination for a file copy or move operation. + * + * - Checks if $source and $destination are valid and readable/writable. + * - Checks that $source is not equal to $destination; if they are an error + * is reported. + * - If file already exists in $destination either the call will error out, + * replace the file or rename the file based on the $replace parameter. + * + * @param string $source + * A string specifying the filepath or URI of the source file. + * @param string|null $destination + * A URI containing the destination that $source should be moved/copied to. + * The URI may be a bare filepath (without a scheme) and in that case the + * default scheme (file://) will be used. If this value is omitted, Drupal's + * default files scheme will be used, usually "public://". + * @param int $replace + * Replace behavior when the destination file already exists: + * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename + * is unique. + * - FILE_EXISTS_ERROR - Do nothing and return FALSE. + * + * @deprecated This public method is deprecated in 8.7.0 and will be converted + * into a protected method before 9.0.0. + * + * @see \Drupal\Core\File\UnmanagedFileHandler::copy() + * @see \Drupal\Core\File\UnmanagedFileHandler::move() + */ + public function prepareDestination($source, &$destination = NULL, $replace = self::EXISTS_RENAME) { + $original_source = $source; + + // Assert that the source file actually exists. + if (!file_exists($source)) { + if (($realpath = $this->realpath($original_source)) !== FALSE) { + $this->logger->error("File '%original_source' ('%realpath') could not be copied because it does not exist.", [ + '%original_source' => $original_source, + '%realpath' => $realpath, + ]); + throw new FileNotExistsException(sprintf("File '%s' ('%s') could not be copied because it does not exist.", $original_source, $realpath)); + } + else { + $this->logger->error("File '%original_source' could not be copied because it does not exist.", [ + '%original_source' => $original_source, + ]); + throw new FileNotExistsException(sprintf("File '%s' could not be copied because it does not exist.", $original_source)); + } + } + + // Build a destination URI if necessary. + if (!isset($destination)) { + $destination = file_build_uri($this->basename($source)); + } + + // Prepare the destination directory. + if ($this->prepareDirectory($destination)) { + // The destination is already a directory, so append the source basename. + $destination = file_stream_wrapper_uri_normalize($destination . '/' . $this->basename($source)); + } + else { + // Perhaps $destination is a dir/file? + $dirname = $this->dirname($destination); + if (!$this->prepareDirectory($dirname)) { + $this->logger->error("The specified file '%original_source' could not be copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions.", [ + '%original_source' => $original_source, + ]); + throw new DirectoryNotReadyException(sprintf("The specified file '%s' could not be copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions.", $original_source)); + } + } + + // Determine whether we can perform this operation based on overwrite rules. + $destination = $this->getDestinationFilename($destination, $replace); + if ($destination === FALSE) { + $this->logger->error("File '%original_source' could not be copied because a file by that name already exists in the destination directory ('%destination').", [ + '%original_source' => $original_source, + '%destination' => $destination, + ]); + throw new FileExistsException(sprintf("File '%s' could not be copied because a file by that name already exists in the destination directory ('%s').", $original_source, $destination)); + } + + // Assert that the source and destination filenames are not the same. + $real_source = $this->realpath($source); + $real_destination = $this->realpath($destination); + if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) { + $this->logger->error("File '%source' could not be copied because it would overwrite itself.", [ + '%source' => $source, + ]); + throw new FileException(sprintf("File '%s' could not be copied because it would overwrite itself.", $source)); + } + // Make sure the .htaccess files are present. + // @todo Replace with a service in https://www.drupal.org/project/drupal/issues/2620304. + file_ensure_htaccess(); + } + + /** + * {@inheritdoc} + */ + public function saveData($data, $destination = NULL, $replace = self::EXISTS_RENAME) { + // Write the data to a temporary file. + $temp_name = $this->tempnam('temporary://', 'file'); + if (file_put_contents($temp_name, $data) === FALSE) { + $this->logger->error("Temporary file '%temp_name' could not be created.", ['%temp_name' => $temp_name]); + throw new FileWriteException(sprintf("Temporary file '%s' could not be created.", $temp_name)); + } + + // Move the file to its final destination. + return $this->move($temp_name, $destination, $replace); + } + + /** + * {@inheritdoc} + */ + public function prepareDirectory(&$directory, $options = self::MODIFY_PERMISSIONS) { + if (!$this->validScheme($this->uriScheme($directory))) { + // Only trim if we're not dealing with a stream. + $directory = rtrim($directory, '/\\'); + } + + // Check if directory exists. + if (!is_dir($directory)) { + // Let mkdir() recursively create directories and use the default + // directory permissions. + if ($options & static::CREATE_DIRECTORY) { + return @$this->mkdir($directory, NULL, TRUE); + } + return FALSE; + } + // The directory exists, so check to see if it is writable. + $writable = is_writable($directory); + if (!$writable && ($options & static::MODIFY_PERMISSIONS)) { + return $this->chmod($directory); + } + + return $writable; + } + + /** + * {@inheritdoc} + */ + public function getDestinationFilename($destination, $replace) { + if (file_exists($destination)) { + switch ($replace) { + case FileSystemInterface::EXISTS_REPLACE: + // Do nothing here, we want to overwrite the existing file. + break; + + case FileSystemInterface::EXISTS_RENAME: + $basename = $this->basename($destination); + $directory = $this->dirname($destination); + $destination = $this->createFilename($basename, $directory); + break; + + case FileSystemInterface::EXISTS_ERROR: + // Error reporting handled by calling function. + return FALSE; + } + } + return $destination; + } + + /** + * {@inheritdoc} + */ + public function createFilename($basename, $directory) { + // Strip control characters (ASCII value < 32). Though these are allowed in + // some filesystems, not many applications handle them well. + $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename); + if (substr(PHP_OS, 0, 3) == 'WIN') { + // These characters are not allowed in Windows filenames. + $basename = str_replace([':', '*', '?', '"', '<', '>', '|'], '_', $basename); + } + + // A URI or path may already have a trailing slash or look like "public://". + if (substr($directory, -1) == '/') { + $separator = ''; + } + else { + $separator = '/'; + } + + $destination = $directory . $separator . $basename; + + if (file_exists($destination)) { + // Destination file already exists, generate an alternative. + $pos = strrpos($basename, '.'); + if ($pos !== FALSE) { + $name = substr($basename, 0, $pos); + $ext = substr($basename, $pos); + } + else { + $name = $basename; + $ext = ''; + } + + $counter = 0; + do { + $destination = $directory . $separator . $name . '_' . $counter++ . $ext; + } while (file_exists($destination)); + } + + return $destination; + } + } diff --git a/core/lib/Drupal/Core/File/FileSystemInterface.php b/core/lib/Drupal/Core/File/FileSystemInterface.php index 6682f46361..c35ff2c26e 100644 --- a/core/lib/Drupal/Core/File/FileSystemInterface.php +++ b/core/lib/Drupal/Core/File/FileSystemInterface.php @@ -7,6 +7,31 @@ */ interface FileSystemInterface { + /** + * Flag for dealing with existing files: Appends number until name is unique. + */ + const EXISTS_RENAME = 0; + + /** + * Flag for dealing with existing files: Replace the existing file. + */ + const EXISTS_REPLACE = 1; + + /** + * Flag for dealing with existing files: Do nothing and return FALSE. + */ + const EXISTS_ERROR = 2; + + /** + * Flag used by ::prepareDirectory() -- create directory if not present. + */ + const CREATE_DIRECTORY = 1; + + /** + * Flag used by ::prepareDirectory() -- file permissions may be changed. + */ + const MODIFY_PERMISSIONS = 2; + /** * Moves an uploaded file to a new location. * @@ -237,4 +262,202 @@ public function uriScheme($uri); */ public function validScheme($scheme); + /** + * Copies a file to a new location without invoking the file API. + * + * This is a powerful function that in many ways performs like an advanced + * version of copy(). + * - Checks if $source and $destination are valid and readable/writable. + * - If file already exists in $destination either the call will error out, + * replace the file or rename the file based on the $replace parameter. + * - If the $source and $destination are equal, the behavior depends on the + * $replace parameter. FILE_EXISTS_REPLACE will error out. + * FILE_EXISTS_RENAME will rename the file until the $destination is unique. + * - Provides a fallback using realpaths if the move fails using stream + * wrappers. This can occur because PHP's copy() function does not properly + * support streams if open_basedir is enabled. See + * https://bugs.php.net/bug.php?id=60456 + * + * @param string $source + * A string specifying the filepath or URI of the source file. + * @param string $destination + * A URI containing the destination that $source should be copied to. The + * URI may be a bare filepath (without a scheme). If this value is omitted, + * Drupal's default files scheme will be used, usually "public://". + * @param int $replace + * Replace behavior when the destination file already exists: + * - FileManagerInterface::FILE_EXISTS_REPLACE - Replace the existing file. + * - FileManagerInterface::FILE_EXISTS_RENAME - Append _{incrementing + * number} until the filename is unique. + * - FileManagerInterface::FILE_EXISTS_ERROR - Throw an exception. + * + * @return string + * The path to the new file. + * + * @throws \Drupal\Core\File\Exception\FileException + * Implementation may throw FileException or its subtype on failure. + */ + public function copy($source, $destination = NULL, $replace = self::EXISTS_RENAME); + + /** + * Deletes a file without database changes or hook invocations. + * + * This function should be used when the file to be deleted does not have an + * entry recorded in the files table. + * + * @param string $path + * A string containing a file path or (streamwrapper) URI. + * + * @throws \Drupal\Core\File\Exception\FileException + * Implementation may throw FileException or its subtype on failure. + */ + public function delete($path); + + /** + * Deletes all files and directories in the specified filepath recursively. + * + * If the specified path is a directory then the function is called + * recursively to process the contents. Once the contents have been removed + * the directory is also removed. + * + * If the specified path is a file then it will be processed with delete() + * method. + * + * Note that this only deletes visible files with write permission. + * + * @param string $path + * A string containing either an URI or a file or directory path. + * @param callable|null $callback + * Callback function to run on each file prior to deleting it and on each + * directory prior to traversing it. For example, can be used to modify + * permissions. + * + * @throws \Drupal\Core\File\Exception\FileException + * Implementation may throw FileException or its subtype on failure. + */ + public function deleteRecursive($path, callable $callback = NULL); + + /** + * Moves a file to a new location without database changes or hook invocation. + * + * This is a powerful function that in many ways performs like an advanced + * version of rename(). + * - Checks if $source and $destination are valid and readable/writable. + * - Checks that $source is not equal to $destination; if they are an error + * is reported. + * - If file already exists in $destination either the call will error out, + * replace the file or rename the file based on the $replace parameter. + * - Works around a PHP bug where rename() does not properly support streams + * if safe_mode or open_basedir are enabled. + * + * @param string $source + * A string specifying the filepath or URI of the source file. + * @param string $destination + * A URI containing the destination that $source should be moved to. The + * URI may be a bare filepath (without a scheme) and in that case the + * default scheme (file://) will be used. If this value is omitted, Drupal's + * default files scheme will be used, usually "public://". + * @param int $replace + * Replace behavior when the destination file already exists: + * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename + * is unique. + * - FILE_EXISTS_ERROR - Do nothing and return FALSE. + * + * @return string + * The path to the new file. + * + * @throws \Drupal\Core\File\Exception\FileException + * Implementation may throw FileException or its subtype on failure. + * + * @see https://bugs.php.net/bug.php?id=60456 + */ + public function move($source, $destination = NULL, $replace = self::EXISTS_RENAME); + + /** + * Saves a file to the specified destination without invoking file API. + * + * This function is identical to file_save_data() except the file will not be + * saved to the {file_managed} table and none of the file_* hooks will be + * called. + * + * @param string $data + * A string containing the contents of the file. + * @param string $destination + * A string containing the destination location. This must be a stream + * wrapper URI. If no value is provided, a randomized name will be generated + * and the file will be saved using Drupal's default files scheme, usually + * "public://". + * @param int $replace + * Replace behavior when the destination file already exists: + * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename + * is unique. + * - FILE_EXISTS_ERROR - Do nothing and return FALSE. + * + * @return string + * A string with the path of the resulting file, or FALSE on error. + * + * @throws \Drupal\Core\File\Exception\FileException + * Implementation may throw FileException or its subtype on failure. + * + * @see file_save_data() + */ + public function saveData($data, $destination = NULL, $replace = self::EXISTS_RENAME); + + /** + * Checks that the directory exists and is writable. + * + * Directories need to have execute permissions to be considered a directory + * by FTP servers, etc. + * + * @param string $directory + * A string reference containing the name of a directory path or URI. A + * trailing slash will be trimmed from a path. + * @param int $options + * A bitmask to indicate if the directory should be created if it does + * not exist (FileSystemInterface::CREATE_DIRECTORY) or made writable if it + * is read-only (FileSystemInterface::MODIFY_PERMISSIONS). + * + * @return bool + * TRUE if the directory exists (or was created) and is writable. FALSE + * otherwise. + */ + public function prepareDirectory(&$directory, $options = self::MODIFY_PERMISSIONS); + + /** + * Creates a full file path from a directory and filename. + * + * If a file with the specified name already exists, an alternative will be + * used. + * + * @param string $basename + * The filename. + * @param string $directory + * The directory or parent URI. + * + * @return string + * File path consisting of $directory and a unique filename based off + * of $basename. + */ + public function createFilename($basename, $directory); + + /** + * Determines the destination path for a file. + * + * @param string $destination + * The desired final URI or filepath. + * @param int $replace + * Replace behavior when the destination file already exists. + * - FileSystemInterface::EXISTS_REPLACE - Replace the existing file. + * - FileSystemInterface::EXISTS_RENAME - Append _{incrementing number} + * until the filename is unique. + * - FileSystemInterface::EXISTS_ERROR - Do nothing and return FALSE. + * + * @return string|bool + * The destination filepath, or FALSE if the file already exists + * and FileSystemInterface::EXISTS_ERROR is specified. + */ + public function getDestinationFilename($destination, $replace); + } diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php index 9e8307a557..0ec3388fec 100644 --- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php @@ -8,6 +8,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; use Drupal\Core\Extension\MissingDependencyException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Serialization\Yaml; use Drupal\Core\Session\UserSession; use Drupal\Core\Site\Settings; @@ -318,7 +319,7 @@ protected function initConfig(ContainerInterface $container) { $config = $container->get('config.factory'); // Manually create the private directory. - file_prepare_directory($this->privateFilesDirectory, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($this->privateFilesDirectory, FileSystemInterface::CREATE_DIRECTORY); // Manually configure the test mail collector implementation to prevent // tests from sending out emails and collect them in state instead. @@ -575,7 +576,7 @@ protected function prepareEnvironment() { // Create test directory ahead of installation so fatal errors and debug // information can be logged during installation process. - file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($this->siteDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); // Prepare filesystem directory paths. $this->publicFilesDirectory = $this->siteDirectory . '/files'; diff --git a/core/modules/aggregator/src/Tests/AggregatorTestBase.php b/core/modules/aggregator/src/Tests/AggregatorTestBase.php index 3181eae91a..4a0c291b2e 100644 --- a/core/modules/aggregator/src/Tests/AggregatorTestBase.php +++ b/core/modules/aggregator/src/Tests/AggregatorTestBase.php @@ -288,7 +288,7 @@ public function getValidOpml(array $feeds) { $path = 'public://valid-opml.xml'; // Add the UTF-8 byte order mark. - return file_unmanaged_save_data(chr(239) . chr(187) . chr(191) . $opml, $path); + return \Drupal::service('file_system')->saveData(chr(239) . chr(187) . chr(191) . $opml, $path); } /** @@ -305,7 +305,7 @@ public function getInvalidOpml() { EOF; $path = 'public://invalid-opml.xml'; - return file_unmanaged_save_data($opml, $path); + return \Drupal::service('file_system')->saveData($opml, $path); } /** @@ -327,7 +327,7 @@ public function getEmptyOpml() { EOF; $path = 'public://empty-opml.xml'; - return file_unmanaged_save_data($opml, $path); + return \Drupal::service('file_system')->saveData($opml, $path); } /** diff --git a/core/modules/aggregator/tests/src/Functional/AggregatorTestBase.php b/core/modules/aggregator/tests/src/Functional/AggregatorTestBase.php index 25ca2bf3b3..7e37510dc5 100644 --- a/core/modules/aggregator/tests/src/Functional/AggregatorTestBase.php +++ b/core/modules/aggregator/tests/src/Functional/AggregatorTestBase.php @@ -281,7 +281,7 @@ public function getValidOpml(array $feeds) { $path = 'public://valid-opml.xml'; // Add the UTF-8 byte order mark. - return file_unmanaged_save_data(chr(239) . chr(187) . chr(191) . $opml, $path); + return \Drupal::service('file_system')->saveData(chr(239) . chr(187) . chr(191) . $opml, $path); } /** @@ -298,7 +298,7 @@ public function getInvalidOpml() { EOF; $path = 'public://invalid-opml.xml'; - return file_unmanaged_save_data($opml, $path); + return \Drupal::service('file_system')->saveData($opml, $path); } /** @@ -320,7 +320,7 @@ public function getEmptyOpml() { EOF; $path = 'public://empty-opml.xml'; - return file_unmanaged_save_data($opml, $path); + return \Drupal::service('file_system')->saveData($opml, $path); } /** diff --git a/core/modules/color/color.module b/core/modules/color/color.module index a25be981d4..6be9dbd5c7 100644 --- a/core/modules/color/color.module +++ b/core/modules/color/color.module @@ -5,12 +5,13 @@ * Allows users to change the color scheme of themes. */ -use Drupal\Component\Utility\Color; -use Drupal\Core\Asset\CssOptimizer; use Drupal\Component\Utility\Bytes; +use Drupal\Component\Utility\Color; use Drupal\Component\Utility\Environment; +use Drupal\Core\Asset\CssOptimizer; use Drupal\Core\Block\BlockPluginInterface; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\Element\Textfield; @@ -440,8 +441,10 @@ function color_scheme_form_submit($form, FormStateInterface $form_state) { $id = $theme . '-' . substr(hash('sha256', serialize($palette) . microtime()), 0, 8); $paths['color'] = 'public://color'; $paths['target'] = $paths['color'] . '/' . $id; + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); foreach ($paths as $path) { - file_prepare_directory($path, FILE_CREATE_DIRECTORY); + $file_system->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY); } $paths['target'] = $paths['target'] . '/'; $paths['id'] = $id; @@ -458,7 +461,7 @@ function color_scheme_form_submit($form, FormStateInterface $form_state) { foreach ($info['copy'] as $file) { $base = drupal_basename($file); $source = $paths['source'] . $file; - $filepath = file_unmanaged_copy($source, $paths['target'] . $base); + $filepath = $file_system->copy($source, $paths['target'] . $base); $paths['map'][$file] = $base; $paths['files'][] = $filepath; } @@ -582,7 +585,7 @@ function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) { * Saves the rewritten stylesheet to disk. */ function _color_save_stylesheet($file, $style, &$paths) { - $filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE); + $filepath = \Drupal::service('file_system')->saveData($style, $file, FileSystemInterface::EXISTS_REPLACE); $paths['files'][] = $filepath; // Set standard file permissions for webserver-generated files. diff --git a/core/modules/config/src/Controller/ConfigController.php b/core/modules/config/src/Controller/ConfigController.php index bf47f72d4b..ede195f047 100644 --- a/core/modules/config/src/Controller/ConfigController.php +++ b/core/modules/config/src/Controller/ConfigController.php @@ -88,7 +88,7 @@ public function __construct(StorageInterface $target_storage, StorageInterface $ * Downloads a tarball of the site configuration. */ public function downloadExport() { - file_unmanaged_delete(file_directory_temp() . '/config.tar.gz'); + \Drupal::service('file_system')->delete(file_directory_temp() . '/config.tar.gz'); $archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz'); // Get raw configuration data without overrides. diff --git a/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php b/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php index 5ac93bb8ab..6ca8212226 100644 --- a/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php +++ b/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php @@ -201,7 +201,7 @@ public function testConfigModuleRequirements() { $this->drupalPostForm('admin/modules', ['modules[config][enable]' => TRUE], t('Install')); $directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY); - file_unmanaged_delete_recursive($directory); + \Drupal::service('file_system')->deleteRecursive($directory); $this->drupalGet('/admin/reports/status'); $this->assertRaw(t('The directory %directory does not exist.', ['%directory' => $directory])); } diff --git a/core/modules/field/tests/src/Kernel/FieldImportCreateTest.php b/core/modules/field/tests/src/Kernel/FieldImportCreateTest.php index 5e155824f9..8d5112ef16 100644 --- a/core/modules/field/tests/src/Kernel/FieldImportCreateTest.php +++ b/core/modules/field/tests/src/Kernel/FieldImportCreateTest.php @@ -94,11 +94,13 @@ public function testImportCreate() { // Add the new files to the sync directory. $src_dir = __DIR__ . '/../../modules/field_test_config/sync'; $target_dir = config_get_config_directory(CONFIG_SYNC_DIRECTORY); - $this->assertTrue(file_unmanaged_copy("$src_dir/$field_storage_config_name.yml", "$target_dir/$field_storage_config_name.yml")); - $this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name.yml", "$target_dir/$field_config_name.yml")); - $this->assertTrue(file_unmanaged_copy("$src_dir/$field_storage_config_name_2.yml", "$target_dir/$field_storage_config_name_2.yml")); - $this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2a.yml", "$target_dir/$field_config_name_2a.yml")); - $this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2b.yml", "$target_dir/$field_config_name_2b.yml")); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $this->assertTrue($file_system->copy("$src_dir/$field_storage_config_name.yml", "$target_dir/$field_storage_config_name.yml")); + $this->assertTrue($file_system->copy("$src_dir/$field_config_name.yml", "$target_dir/$field_config_name.yml")); + $this->assertTrue($file_system->copy("$src_dir/$field_storage_config_name_2.yml", "$target_dir/$field_storage_config_name_2.yml")); + $this->assertTrue($file_system->copy("$src_dir/$field_config_name_2a.yml", "$target_dir/$field_config_name_2a.yml")); + $this->assertTrue($file_system->copy("$src_dir/$field_config_name_2b.yml", "$target_dir/$field_config_name_2b.yml")); // Import the content of the sync directory. $this->configImporter()->import(); diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 4fc88435c1..af2787230e 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -7,6 +7,8 @@ use Drupal\Core\Datetime\Entity\DateFormat; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Render\BubbleableMetadata; @@ -155,8 +157,10 @@ function file_load($fid, $reset = FALSE) { * @see hook_file_copy() */ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); if (!file_valid_uri($destination)) { - if (($realpath = \Drupal::service('file_system')->realpath($source->getFileUri())) !== FALSE) { + if (($realpath = $file_system->realpath($source->getFileUri())) !== FALSE) { \Drupal::logger('file')->notice('File %file (%realpath) could not be copied because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', ['%file' => $source->getFileUri(), '%realpath' => $realpath, '%destination' => $destination]); } else { @@ -166,7 +170,8 @@ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_E return FALSE; } - if ($uri = file_unmanaged_copy($source->getFileUri(), $destination, $replace)) { + try { + $uri = $file_system->copy($source->getFileUri(), $destination, $replace); $file = $source->createDuplicate(); $file->setFileUri($uri); $file->setFilename(drupal_basename($uri)); @@ -195,7 +200,9 @@ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_E return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } } /** @@ -226,12 +233,14 @@ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_E * @return \Drupal\file\FileInterface|false * Resulting file entity for success, or FALSE in the event of an error. * - * @see file_unmanaged_move() + * @see \Drupal\Core\File\FileSystemInterface::move() * @see hook_file_move() */ function file_move(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); if (!file_valid_uri($destination)) { - if (($realpath = \Drupal::service('file_system')->realpath($source->getFileUri())) !== FALSE) { + if (($realpath = $file_system->realpath($source->getFileUri())) !== FALSE) { \Drupal::logger('file')->notice('File %file (%realpath) could not be moved because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', ['%file' => $source->getFileUri(), '%realpath' => $realpath, '%destination' => $destination]); } else { @@ -241,7 +250,8 @@ function file_move(FileInterface $source, $destination = NULL, $replace = FILE_E return FALSE; } - if ($uri = file_unmanaged_move($source->getFileUri(), $destination, $replace)) { + try { + $uri = $file_system->move($source->getFileUri(), $destination, $replace); $delete_source = FALSE; $file = clone $source; @@ -274,7 +284,9 @@ function file_move(FileInterface $source, $destination = NULL, $replace = FILE_E return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } } /** @@ -557,7 +569,8 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM return FALSE; } - if ($uri = file_unmanaged_save_data($data, $destination, $replace)) { + try { + $uri = \Drupal::service('file_system')->saveData($data, $destination, $replace); // Create a file entity. $file = File::create([ 'uri' => $uri, @@ -585,7 +598,10 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM $file->save(); return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } + } /** @@ -922,7 +938,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL * * @see file_save_upload() */ -function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $validators = [], $destination = FALSE, $replace = FILE_EXISTS_RENAME) { +function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $validators = [], $destination = FALSE, $replace = FileSystemInterface::EXISTS_REPLACE) { $user = \Drupal::currentUser(); // Check for file upload errors and return FALSE for this file if a lower // level system error occurred. For a complete list of errors: @@ -1022,9 +1038,11 @@ function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $va if (substr($destination, -1) != '/') { $destination .= '/'; } - $file->destination = file_destination($destination . $file->getFilename(), $replace); - // If file_destination() returns FALSE then $replace === FILE_EXISTS_ERROR and - // there's an existing file so we need to bail. + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $file->destination = $file_system->getDestinationFilename($destination . $file->getFilename(), $replace); + // If the destination is FALSE then there is an existing file and $replace is + // set to return an error, so we need to exit. if ($file->destination === FALSE) { \Drupal::messenger()->addError(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', ['%source' => $form_field_name, '%directory' => $destination])); return FALSE; @@ -1055,7 +1073,7 @@ function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $va } $file->setFileUri($file->destination); - if (!\Drupal::service('file_system')->moveUploadedFile($file_info->getRealPath(), $file->getFileUri())) { + if (!$file_system->moveUploadedFile($file_info->getRealPath(), $file->getFileUri())) { \Drupal::messenger()->addError(t('File upload error. Could not move uploaded file.')); \Drupal::logger('file')->notice('Upload error. Could not move uploaded file %file to destination %destination.', ['%file' => $file->getFilename(), '%destination' => $file->getFileUri()]); return FALSE; @@ -1067,7 +1085,7 @@ function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $va // If we are replacing an existing file re-use its database record. // @todo Do not create a new entity in order to update it. See // https://www.drupal.org/node/2241865. - if ($replace == FILE_EXISTS_REPLACE) { + if ($replace == FileSystemInterface::EXISTS_REPLACE) { $existing_files = entity_load_multiple_by_properties('file', ['uri' => $file->getFileUri()]); if (count($existing_files)) { $existing = reset($existing_files); @@ -1360,7 +1378,7 @@ function file_managed_file_save_upload($element, FormStateInterface $form_state) $file_upload = $all_files[$upload_name]; $destination = isset($element['#upload_location']) ? $element['#upload_location'] : NULL; - if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) { + if (isset($destination) && !\Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) { \Drupal::logger('file')->notice('The upload directory %directory for the file field %name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', ['%directory' => $destination, '%name' => $element['#field_name']]); $form_state->setError($element, t('The file could not be uploaded.')); return FALSE; diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php index f768ab79bc..d8aab93836 100644 --- a/core/modules/file/src/Entity/File.php +++ b/core/modules/file/src/Entity/File.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\File\Exception\FileException; use Drupal\file\FileInterface; use Drupal\user\EntityOwnerTrait; @@ -206,7 +207,12 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie // Delete the actual file. Failures due to invalid files and files that // were already deleted are logged to watchdog but ignored, the // corresponding file entity will be deleted. - file_unmanaged_delete($entity->getFileUri()); + try { + \Drupal::service('file_system')->delete($entity->getFileUri()); + } + catch (FileException $e) { + // Ignore and continue. + } } } diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php index 542be122df..202a96682b 100644 --- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php +++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php @@ -8,6 +8,7 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; @@ -325,12 +326,12 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin // Prepare destination. $dirname = static::doGetUploadLocation($settings); - file_prepare_directory($dirname, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($dirname, FileSystemInterface::CREATE_DIRECTORY); // Generate a file entity. $destination = $dirname . '/' . $random->name(10, TRUE) . '.txt'; $data = $random->paragraphs(3); - $file = file_save_data($data, $destination, FILE_EXISTS_ERROR); + $file = file_save_data($data, $destination, FileSystemInterface::EXISTS_ERROR); $values = [ 'target_id' => $file->id(), 'display' => (int) $settings['display_default'], diff --git a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php index 44600369df..7a70557d90 100644 --- a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php +++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php @@ -7,6 +7,7 @@ use Drupal\Core\Config\Config; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\File\Exception\FileException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Session\AccountInterface; @@ -73,7 +74,7 @@ class FileUploadResource extends ResourceBase { /** * The file system service. * - * @var \Drupal\Core\File\FileSystemInterface + * @var \Drupal\Core\File\FileSystem */ protected $fileSystem; @@ -226,7 +227,7 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { $destination = $this->getUploadLocation($field_definition->getSettings()); // Check the destination file path is writable. - if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) { + if (!$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) { throw new HttpException(500, 'Destination file path is not writable'); } @@ -240,7 +241,8 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { $temp_file_path = $this->streamUploadData(); // This will take care of altering $file_uri if a file already exists. - file_unmanaged_prepare($temp_file_path, $file_uri); + // @todo replace with FileSystemInterface::getDestinationFilename() ? + $this->fileSystem->prepareDestination($temp_file_path, $file_uri); // Lock based on the prepared file URI. $lock_id = $this->generateLockIdFromFileUri($file_uri); @@ -265,8 +267,11 @@ public function post(Request $request, $entity_type_id, $bundle, $field_name) { // Move the file to the correct location after validation. Use // FILE_EXISTS_ERROR as the file location has already been determined above - // in file_unmanaged_prepare(). - if (!file_unmanaged_move($temp_file_path, $file_uri, FILE_EXISTS_ERROR)) { + // in FileSystem::prepareDestination(). + try { + $this->fileSystem->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR); + } + catch (FileException $e) { throw new HttpException(500, 'Temporary file could not be moved to file location'); } diff --git a/core/modules/file/tests/file_test/src/Form/FileTestForm.php b/core/modules/file/tests/file_test/src/Form/FileTestForm.php index bbac4d05c9..08c4a57ffd 100644 --- a/core/modules/file/tests/file_test/src/Form/FileTestForm.php +++ b/core/modules/file/tests/file_test/src/Form/FileTestForm.php @@ -2,6 +2,7 @@ namespace Drupal\file_test\Form; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormInterface; use Drupal\Core\Form\FormStateInterface; @@ -29,11 +30,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#type' => 'select', '#title' => t('Replace existing image'), '#options' => [ - FILE_EXISTS_RENAME => t('Appends number until name is unique'), - FILE_EXISTS_REPLACE => t('Replace the existing file'), - FILE_EXISTS_ERROR => t('Fail with an error'), + FileSystemInterface::EXISTS_RENAME => t('Appends number until name is unique'), + FileSystemInterface::EXISTS_REPLACE => t('Replace the existing file'), + FileSystemInterface::EXISTS_ERROR => t('Fail with an error'), ], - '#default_value' => FILE_EXISTS_RENAME, + '#default_value' => FileSystemInterface::EXISTS_RENAME, ]; $form['file_subdir'] = [ '#type' => 'textfield', @@ -79,7 +80,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // form value for the $replace parameter. if (!$form_state->isValueEmpty('file_subdir')) { $destination = 'temporary://' . $form_state->getValue('file_subdir'); - file_prepare_directory($destination, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY); } else { $destination = FALSE; diff --git a/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php b/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php index ef8e0afbfb..10127dc94c 100644 --- a/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php +++ b/core/modules/file/tests/file_test/src/Form/FileTestSaveUploadFromForm.php @@ -2,6 +2,7 @@ namespace Drupal\file_test\Form; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; @@ -70,11 +71,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#type' => 'select', '#title' => $this->t('Replace existing image'), '#options' => [ - FILE_EXISTS_RENAME => $this->t('Appends number until name is unique'), - FILE_EXISTS_REPLACE => $this->t('Replace the existing file'), - FILE_EXISTS_ERROR => $this->t('Fail with an error'), + FileSystemInterface::EXISTS_RENAME => $this->t('Appends number until name is unique'), + FileSystemInterface::EXISTS_REPLACE => $this->t('Replace the existing file'), + FileSystemInterface::EXISTS_ERROR => $this->t('Fail with an error'), ], - '#default_value' => FILE_EXISTS_RENAME, + '#default_value' => FileSystemInterface::EXISTS_RENAME, ]; $form['file_subdir'] = [ '#type' => 'textfield', @@ -121,7 +122,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) { // form value for the $replace parameter. if (!$form_state->isValueEmpty('file_subdir')) { $destination = 'temporary://' . $form_state->getValue('file_subdir'); - file_prepare_directory($destination, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY); } else { $destination = FALSE; diff --git a/core/modules/file/tests/src/Functional/DownloadTest.php b/core/modules/file/tests/src/Functional/DownloadTest.php index 95693ec333..bf25a44cad 100644 --- a/core/modules/file/tests/src/Functional/DownloadTest.php +++ b/core/modules/file/tests/src/Functional/DownloadTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\file\Functional; +use Drupal\Core\File\FileSystemInterface; + /** * Tests for download/file transfer functions. * @@ -143,9 +145,9 @@ private function checkUrl($scheme, $directory, $filename, $expected_url) { // Convert $filename to a valid filename, i.e. strip characters not // supported by the filesystem, and create the file in the specified // directory. - $filepath = file_create_filename($filename, $directory); + $filepath = \Drupal::service('file_system')->createFilename($filename, $directory); $directory_uri = $scheme . '://' . dirname($filepath); - file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY); $file = $this->createFile($filepath, NULL, $scheme); $url = file_create_url($file->getFileUri()); diff --git a/core/modules/file/tests/src/Functional/FileManagedFileElementTest.php b/core/modules/file/tests/src/Functional/FileManagedFileElementTest.php index 94d00ef9f2..7feaa507da 100644 --- a/core/modules/file/tests/src/Functional/FileManagedFileElementTest.php +++ b/core/modules/file/tests/src/Functional/FileManagedFileElementTest.php @@ -168,7 +168,7 @@ public function testFileRemovedFromDisk() { $file = $this->container->get('entity_type.manager')->getStorage('file')->load($fid); $file->setPermanent(); $file->save(); - $this->assertTrue(file_unmanaged_delete($file->getFileUri())); + $this->assertTrue(\Drupal::service('file_system')->delete($file->getFileUri())); $file->save(); $this->assertTrue($file->isPermanent()); $file->delete(); diff --git a/core/modules/file/tests/src/Functional/SaveUploadFormTest.php b/core/modules/file/tests/src/Functional/SaveUploadFormTest.php index 2a43a9997f..a9b3aca204 100644 --- a/core/modules/file/tests/src/Functional/SaveUploadFormTest.php +++ b/core/modules/file/tests/src/Functional/SaveUploadFormTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\file\Functional; +use Drupal\Core\File\FileSystemInterface; use Drupal\file\Entity\File; use Drupal\Tests\TestFileCreationTrait; @@ -302,7 +303,7 @@ public function testExistingRename() { /** @var \Drupal\Core\File\FileSystemInterface $file_system */ $file_system = \Drupal::service('file_system'); $edit = [ - 'file_test_replace' => FILE_EXISTS_RENAME, + 'file_test_replace' => FileSystemInterface::EXISTS_RENAME, 'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()), ]; $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); diff --git a/core/modules/file/tests/src/Functional/SaveUploadTest.php b/core/modules/file/tests/src/Functional/SaveUploadTest.php index 552cfc628e..0e3d6da488 100644 --- a/core/modules/file/tests/src/Functional/SaveUploadTest.php +++ b/core/modules/file/tests/src/Functional/SaveUploadTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\file\Functional; +use Drupal\Core\File\FileSystemInterface; use Drupal\file\Entity\File; use Drupal\Tests\TestFileCreationTrait; @@ -285,7 +286,7 @@ public function testHandleFileMunge() { */ public function testExistingRename() { $edit = [ - 'file_test_replace' => FILE_EXISTS_RENAME, + 'file_test_replace' => FileSystemInterface::EXISTS_RENAME, 'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri()), ]; $this->drupalPostForm('file-test/upload', $edit, t('Submit')); diff --git a/core/modules/file/tests/src/Kernel/FileUrlTest.php b/core/modules/file/tests/src/Kernel/FileUrlTest.php index 573fe25444..de385935c6 100644 --- a/core/modules/file/tests/src/Kernel/FileUrlTest.php +++ b/core/modules/file/tests/src/Kernel/FileUrlTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\file\Kernel; +use Drupal\Core\File\FileSystemInterface; + /** * Tests the file url. * @@ -15,9 +17,9 @@ class FileUrlTest extends FileManagedUnitTestBase { public function testFilesUrlWithDifferentHostName() { $test_base_url = 'http://www.example.com/cdn'; $this->setSetting('file_public_base_url', $test_base_url); - $filepath = file_create_filename('test.txt', ''); + $filepath = \Drupal::service('file_system')->createFilename('test.txt', ''); $directory_uri = 'public://' . dirname($filepath); - file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY); $file = $this->createFile($filepath, NULL, 'public'); $url = file_create_url($file->getFileUri()); $expected_url = $test_base_url . '/' . basename($filepath); diff --git a/core/modules/filter/tests/src/Functional/FilterHtmlImageSecureTest.php b/core/modules/filter/tests/src/Functional/FilterHtmlImageSecureTest.php index 065d4a6f79..0c14a5cee1 100644 --- a/core/modules/filter/tests/src/Functional/FilterHtmlImageSecureTest.php +++ b/core/modules/filter/tests/src/Functional/FilterHtmlImageSecureTest.php @@ -99,7 +99,7 @@ public function testImageSource() { $special_filename = 'tést fïle nàme.png'; $special_image = rawurlencode($special_filename); $special_uri = str_replace($test_images[0]->filename, $special_filename, $test_images[0]->uri); - file_unmanaged_copy($test_images[0]->uri, $special_uri); + \Drupal::service('file_system')->copy($test_images[0]->uri, $special_uri); // Create a list of test image sources. // The keys become the value of the IMG 'src' attribute, the values are the diff --git a/core/modules/image/image.install b/core/modules/image/image.install index 3be79ba270..ca13a9b75c 100644 --- a/core/modules/image/image.install +++ b/core/modules/image/image.install @@ -5,13 +5,15 @@ * Install, update and uninstall functions for the image module. */ +use Drupal\Core\File\FileSystemInterface; + /** * Implements hook_install(). */ function image_install() { // Create the styles directory and ensure it's writable. $directory = file_default_scheme() . '://styles'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } /** @@ -19,7 +21,7 @@ function image_install() { */ function image_uninstall() { // Remove the styles directory and generated images. - file_unmanaged_delete_recursive(file_default_scheme() . '://styles'); + \Drupal::service('file_system')->deleteRecursive(file_default_scheme() . '://styles'); } /** diff --git a/core/modules/image/image.module b/core/modules/image/image.module index c1c216c0d1..34e38a5649 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\file\FileInterface; use Drupal\field\FieldStorageConfigInterface; @@ -434,7 +435,7 @@ function image_field_storage_config_update(FieldStorageConfigInterface $field_st // If the upload destination changed, then move the file. if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) { $directory = $field_storage->getSetting('uri_scheme') . '://default_images/'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); file_move($file_new, $directory . $file_new->getFilename()); } } @@ -472,7 +473,7 @@ function image_field_config_update(FieldConfigInterface $field) { // If the upload destination changed, then move the file. if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) { $directory = $field_storage->getSetting('uri_scheme') . '://default_images/'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); file_move($file_new, $directory . $file_new->getFilename()); } } diff --git a/core/modules/image/src/Controller/QuickEditImageController.php b/core/modules/image/src/Controller/QuickEditImageController.php index 1a41c02e73..2deef51a8e 100644 --- a/core/modules/image/src/Controller/QuickEditImageController.php +++ b/core/modules/image/src/Controller/QuickEditImageController.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Image\ImageFactory; use Drupal\Core\Render\Element\StatusMessages; use Drupal\Core\Render\RendererInterface; @@ -49,6 +50,13 @@ class QuickEditImageController extends ControllerBase { */ protected $entityDisplayRepository; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a new QuickEditImageController. * @@ -58,13 +66,16 @@ class QuickEditImageController extends ControllerBase { * The image factory. * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository * The entity display repository service. */ - public function __construct(RendererInterface $renderer, ImageFactory $image_factory, PrivateTempStoreFactory $temp_store_factory, EntityDisplayRepositoryInterface $entity_display_repository = NULL) { + public function __construct(RendererInterface $renderer, ImageFactory $image_factory, PrivateTempStoreFactory $temp_store_factory, FileSystemInterface $file_system, EntityDisplayRepositoryInterface $entity_display_repository = NULL) { $this->renderer = $renderer; $this->imageFactory = $image_factory; $this->tempStore = $temp_store_factory->get('quickedit'); + $this->fileSystem = $file_system; if (!$entity_display_repository) { @trigger_error('The entity_display.repository service must be passed to QuickEditImageController::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); $entity_display_repository = \Drupal::service('entity_display.repository'); @@ -80,6 +91,7 @@ public static function create(ContainerInterface $container) { $container->get('renderer'), $container->get('image.factory'), $container->get('tempstore.private'), + $container->get('file_system'), $container->get('entity_display.repository') ); } @@ -111,7 +123,7 @@ public function upload(EntityInterface $entity, $field_name, $langcode, $view_mo } // Create the destination directory if it does not already exist. - if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) { + if (isset($destination) && !$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) { return new JsonResponse(['main_error' => $this->t('The destination directory could not be created.'), 'errors' => '']); } diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php index 7992aea57a..5ba743ed09 100644 --- a/core/modules/image/src/Entity/ImageStyle.php +++ b/core/modules/image/src/Entity/ImageStyle.php @@ -6,6 +6,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Routing\RequestHelper; use Drupal\Core\Site\Settings; use Drupal\Core\Url; @@ -250,10 +251,12 @@ public function buildUrl($path, $clean_urls = NULL) { */ public function flush($path = NULL) { // A specific image path has been provided. Flush only that derivative. + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); if (isset($path)) { $derivative_uri = $this->buildUri($path); if (file_exists($derivative_uri)) { - file_unmanaged_delete($derivative_uri); + $file_system->delete($derivative_uri); } return $this; } @@ -262,7 +265,7 @@ public function flush($path = NULL) { $wrappers = $this->getStreamWrapperManager()->getWrappers(StreamWrapperInterface::WRITE_VISIBLE); foreach ($wrappers as $wrapper => $wrapper_data) { if (file_exists($directory = $wrapper . '://styles/' . $this->id())) { - file_unmanaged_delete_recursive($directory); + $file_system->deleteRecursive($directory); } } @@ -292,7 +295,7 @@ public function createDerivative($original_uri, $derivative_uri) { $directory = drupal_dirname($derivative_uri); // Build the destination folder tree if it doesn't already exist. - if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!\Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) { \Drupal::logger('image')->error('Failed to create style directory: %directory', ['%directory' => $directory]); return FALSE; } diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php index b3aa0c9fa7..06770965ac 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; @@ -344,7 +345,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin if (!isset($images[$extension][$min_resolution][$max_resolution]) || count($images[$extension][$min_resolution][$max_resolution]) <= 5) { $tmp_file = drupal_tempnam('temporary://', 'generateImage_'); $destination = $tmp_file . '.' . $extension; - file_unmanaged_move($tmp_file, $destination); + \Drupal::service('file_system')->move($tmp_file, $destination); if ($path = $random->image(\Drupal::service('file_system')->realpath($destination), $min_resolution, $max_resolution)) { $image = File::create(); $image->setFileUri($path); @@ -352,7 +353,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin $image->setMimeType(\Drupal::service('file.mime_type.guesser')->guess($path)); $image->setFileName(drupal_basename($path)); $destination_dir = static::doGetUploadLocation($settings); - file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($destination_dir, FileSystemInterface::CREATE_DIRECTORY); $destination = $destination_dir . '/' . basename($path); $file = file_move($image, $destination); $images[$extension][$min_resolution][$max_resolution][$file->id()] = $file; diff --git a/core/modules/image/tests/src/Functional/FileMoveTest.php b/core/modules/image/tests/src/Functional/FileMoveTest.php index 3cf3be71b8..fd25363582 100644 --- a/core/modules/image/tests/src/Functional/FileMoveTest.php +++ b/core/modules/image/tests/src/Functional/FileMoveTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\image\Functional; +use Drupal\Core\File\FileSystemInterface; use Drupal\file\Entity\File; use Drupal\image\Entity\ImageStyle; use Drupal\Tests\BrowserTestBase; @@ -46,7 +47,7 @@ public function testNormal() { // Clone the object so we don't have to worry about the function changing // our reference copy. $desired_filepath = 'public://' . $this->randomMachineName(); - $result = file_move(clone $file, $desired_filepath, FILE_EXISTS_ERROR); + $result = file_move(clone $file, $desired_filepath, FileSystemInterface::EXISTS_ERROR); // Check if image has been moved. $this->assertTrue(file_exists($result->getFileUri()), 'Make sure image is moved successfully.'); diff --git a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php index 144d03ee17..66a3e54b61 100644 --- a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php +++ b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php @@ -33,7 +33,7 @@ public function createSampleImage(ImageStyleInterface $style) { if (!isset($file_path)) { $files = $this->drupalGetTestFiles('image'); $file = reset($files); - $file_path = file_unmanaged_copy($file->uri); + $file_path = \Drupal::service('file_system')->copy($file->uri); } return $style->buildUrl($file_path) ? $file_path : FALSE; diff --git a/core/modules/image/tests/src/Functional/ImageDimensionsTest.php b/core/modules/image/tests/src/Functional/ImageDimensionsTest.php index 4a11bf956e..4c80bb1774 100644 --- a/core/modules/image/tests/src/Functional/ImageDimensionsTest.php +++ b/core/modules/image/tests/src/Functional/ImageDimensionsTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\image\Functional; +use Drupal\Core\File\FileSystemInterface; use Drupal\image\Entity\ImageStyle; use Drupal\Tests\BrowserTestBase; use Drupal\Tests\TestFileCreationTrait; @@ -35,13 +36,15 @@ public function testImageDimensions() { // Create a working copy of the file. $files = $this->drupalGetTestFiles('image'); $file = reset($files); - $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); // Create a style. /** @var $style \Drupal\image\ImageStyleInterface */ $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); $style->save(); - $generated_uri = 'public://styles/test/public/' . \Drupal::service('file_system')->basename($original_uri); + $generated_uri = 'public://styles/test/public/' . $file_system->basename($original_uri); $url = file_url_transform_relative($style->buildUrl($original_uri)); $variables = [ @@ -257,7 +260,7 @@ public function testImageDimensions() { '#height' => 20, ]; // PNG original image. Should be resized to 100x100. - $generated_uri = 'public://styles/test_uri/public/' . \Drupal::service('file_system')->basename($original_uri); + $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); $url = file_url_transform_relative($style->buildUrl($original_uri)); $this->assertEqual($this->getImageTag($variables), ''); $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.'); @@ -269,8 +272,8 @@ public function testImageDimensions() { $this->assertEqual($image_file->getHeight(), 100); // GIF original image. Should be resized to 50x50. $file = $files[1]; - $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); - $generated_uri = 'public://styles/test_uri/public/' . \Drupal::service('file_system')->basename($original_uri); + $original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); + $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); $url = file_url_transform_relative($style->buildUrl($original_uri)); $variables['#uri'] = $original_uri; $this->assertEqual($this->getImageTag($variables), ''); diff --git a/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php b/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php index 66899fb825..3998b350e7 100644 --- a/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php +++ b/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php @@ -42,7 +42,7 @@ public function testDefaultImages() { for ($i = 1; $i <= 10; $i++) { $filename = $this->randomMachineName() . "$i"; $desired_filepath = 'public://' . $filename; - file_unmanaged_copy($files[0]->uri, $desired_filepath, FILE_EXISTS_ERROR); + \Drupal::service('file_system')->copy($files[0]->uri, $desired_filepath, FILE_EXISTS_ERROR); $file = File::create(['uri' => $desired_filepath, 'filename' => $filename, 'name' => $filename]); $file->save(); } diff --git a/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php b/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php index 169bfa01f8..00b9615db7 100644 --- a/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php +++ b/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php @@ -29,7 +29,7 @@ public function createSampleImage($style, $wrapper) { } // Make sure we have an image in our wrapper testing file directory. - $source_uri = file_unmanaged_copy($file->uri, $wrapper . '://'); + $source_uri = \Drupal::service('file_system')->copy($file->uri, $wrapper . '://'); // Build the derivative image. $derivative_uri = $style->buildUri($source_uri); $derivative = $style->createDerivative($source_uri, $derivative_uri); diff --git a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php index c905c04b4b..69cb5d8c78 100644 --- a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php +++ b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\image\Functional; +use Drupal\Core\File\FileSystemInterface; use Drupal\image\Entity\ImageStyle; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\BrowserTestBase; @@ -134,7 +135,7 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s // Create the directories for the styles. $directory = $scheme . '://styles/' . $this->style->id(); - $status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + $status = \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); $this->assertNotIdentical(FALSE, $status, 'Created the directory for the generated images for the test style.'); // Override the language to build the URL for the correct language. @@ -147,7 +148,9 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s // Create a working copy of the file. $files = $this->drupalGetTestFiles('image'); $file = array_shift($files); - $original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $original_uri = $file_system->copy($file->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); // Let the image_module_test module know about this file, so it can claim // ownership in hook_file_download(). \Drupal::state()->set('image.test_file_download', $original_uri); @@ -231,7 +234,7 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s // Repeat this with a different file that we do not have access to and // make sure that access is denied. $file_noaccess = array_shift($files); - $original_uri_noaccess = file_unmanaged_copy($file_noaccess->uri, $scheme . '://', FILE_EXISTS_RENAME); + $original_uri_noaccess = $file_system->copy($file_noaccess->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); $generated_uri_noaccess = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . drupal_basename($original_uri_noaccess); $this->assertFalse(file_exists($generated_uri_noaccess), 'Generated file does not exist.'); $generate_url_noaccess = $this->style->buildUrl($original_uri_noaccess); @@ -271,7 +274,7 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s // Create another working copy of the file. $files = $this->drupalGetTestFiles('image'); $file = array_shift($files); - $original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME); + $original_uri = $file_system->copy($file->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); // Let the image_module_test module know about this file, so it can claim // ownership in hook_file_download(). \Drupal::state()->set('image.test_file_download', $original_uri); diff --git a/core/modules/image/tests/src/Kernel/ImageItemTest.php b/core/modules/image/tests/src/Kernel/ImageItemTest.php index 9621423823..bd2c53460d 100644 --- a/core/modules/image/tests/src/Kernel/ImageItemTest.php +++ b/core/modules/image/tests/src/Kernel/ImageItemTest.php @@ -67,7 +67,7 @@ protected function setUp() { 'file_extensions' => 'jpg', ], ])->save(); - file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); $this->image = File::create([ 'uri' => 'public://example.jpg', ]); @@ -100,7 +100,7 @@ public function testImageItem() { $this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid()); // Make sure the computed entity reflects updates to the referenced file. - file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example-2.jpg'); + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', 'public://example-2.jpg'); $image2 = File::create([ 'uri' => 'public://example-2.jpg', ]); diff --git a/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php index bffd6545f6..8a1c2c6e74 100644 --- a/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php +++ b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\image\Kernel; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Url; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; @@ -62,7 +63,7 @@ protected function setUp() { 'field_name' => 'image_test', 'bundle' => 'entity_test', ])->save(); - file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); $this->image = File::create([ 'uri' => 'public://example.jpg', ]); @@ -80,7 +81,7 @@ public function testImageFormatterTheme() { // Create an image. $files = $this->drupalGetTestFiles('image'); $file = reset($files); - $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); + $original_uri = \Drupal::service('file_system')->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); // Create a style. $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); @@ -142,7 +143,7 @@ public function testImageStyleTheme() { // Create an image. $files = $this->drupalGetTestFiles('image'); $file = reset($files); - $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); + $original_uri = \Drupal::service('file_system')->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); // Create a style. $style = ImageStyle::create(['name' => 'image_test', 'label' => 'Test']); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 3feee12c5b..1b9031f245 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -5,6 +5,7 @@ * Mass import-export and batch import functionality for Gettext .po files. */ +use Drupal\Core\File\Exception\FileException; use Drupal\Core\Language\LanguageInterface; use Drupal\file\FileInterface; use Drupal\locale\Gettext; @@ -506,8 +507,10 @@ function locale_translate_delete_translation_files(array $projects = [], array $ // Delete all translation files from the translations directory. if ($files = locale_translate_get_interface_translation_files($projects, $langcodes)) { foreach ($files as $file) { - $success = file_unmanaged_delete($file->uri); - if (!$success) { + try { + \Drupal::service('file_system')->delete($file->uri); + } + catch (FileException $e) { $fail = TRUE; } } diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install index 480545ad90..94117df1fa 100644 --- a/core/modules/locale/locale.install +++ b/core/modules/locale/locale.install @@ -5,6 +5,8 @@ * Install, update, and uninstall functions for the Locale module. */ +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Url; /** @@ -17,7 +19,7 @@ function locale_install() { $directory = $site_path . '/files/translations'; \Drupal::configFactory()->getEditable('locale.settings')->set('translation.path', $directory)->save(); } - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } /** @@ -32,7 +34,12 @@ function locale_uninstall() { $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: []; foreach ($locale_javascripts as $langcode => $file_suffix) { if (!empty($file_suffix)) { - file_unmanaged_delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js'); + try { + \Drupal::service('file_system')->delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js'); + } + catch (FileException $e) { + // Ignore and continue. + } } } // Delete the JavaScript translations directory if empty. diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index e0a9b4877d..149d541798 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -15,6 +15,8 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\Xss; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Url; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Form\FormStateInterface; @@ -1298,8 +1300,17 @@ function _locale_rebuild_js($langcode = NULL) { // be saved. $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: []; $changed_hash = !isset($locale_javascripts[$language->getId()]) || ($locale_javascripts[$language->getId()] != $data_hash); + + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + if (!empty($locale_javascripts[$language->getId()]) && (!$data || $changed_hash)) { - file_unmanaged_delete($dir . '/' . $language->getId() . '_' . $locale_javascripts[$language->getId()] . '.js'); + try { + $file_system->delete($dir . '/' . $language->getId() . '_' . $locale_javascripts[$language->getId()] . '.js'); + } + catch (FileException $e) { + // Ignore. + } $locale_javascripts[$language->getId()] = ''; $status = 'deleted'; } @@ -1309,30 +1320,35 @@ function _locale_rebuild_js($langcode = NULL) { $dest = $dir . '/' . $language->getId() . '_' . $data_hash . '.js'; if ($data && ($changed_hash || !file_exists($dest))) { // Ensure that the directory exists and is writable, if possible. - file_prepare_directory($dir, FILE_CREATE_DIRECTORY); + $file_system->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY); // Save the file. - if (file_unmanaged_save_data($data, $dest)) { - $locale_javascripts[$language->getId()] = $data_hash; - // If we deleted a previous version of the file and we replace it with a - // new one we have an update. - if ($status == 'deleted') { - $status = 'updated'; - } - // If the file did not exist previously and the data has changed we have - // a fresh creation. - elseif ($changed_hash) { - $status = 'created'; + try { + if ($file_system->saveData($data, $dest)) { + $locale_javascripts[$language->getId()] = $data_hash; + // If we deleted a previous version of the file and we replace it with a + // new one we have an update. + if ($status == 'deleted') { + $status = 'updated'; + } + // If the file did not exist previously and the data has changed we have + // a fresh creation. + elseif ($changed_hash) { + $status = 'created'; + } + // If the data hash is unchanged the translation was lost and has to be + // rebuilt. + else { + $status = 'rebuilt'; + } } - // If the data hash is unchanged the translation was lost and has to be - // rebuilt. else { - $status = 'rebuilt'; + $locale_javascripts[$language->getId()] = ''; + $status = 'error'; } } - else { - $locale_javascripts[$language->getId()] = ''; - $status = 'error'; + catch (FileException $e) { + // Do nothing. } } diff --git a/core/modules/locale/tests/src/Functional/LocaleExportTest.php b/core/modules/locale/tests/src/Functional/LocaleExportTest.php index 05621f6c13..3d0d1f416e 100644 --- a/core/modules/locale/tests/src/Functional/LocaleExportTest.php +++ b/core/modules/locale/tests/src/Functional/LocaleExportTest.php @@ -33,8 +33,8 @@ protected function setUp() { $this->drupalLogin($this->adminUser); // Copy test po files to the translations directory. - file_unmanaged_copy(__DIR__ . '/../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); - file_unmanaged_copy(__DIR__ . '/../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); + \Drupal::service('file_system')->copy(__DIR__ . '/../../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); + \Drupal::service('file_system')->copy(__DIR__ . '/../../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); } /** diff --git a/core/modules/locale/tests/src/Functional/LocaleImportFunctionalTest.php b/core/modules/locale/tests/src/Functional/LocaleImportFunctionalTest.php index c63c4e4261..22762f7ae0 100644 --- a/core/modules/locale/tests/src/Functional/LocaleImportFunctionalTest.php +++ b/core/modules/locale/tests/src/Functional/LocaleImportFunctionalTest.php @@ -41,8 +41,10 @@ protected function setUp() { parent::setUp(); // Copy test po files to the translations directory. - file_unmanaged_copy(__DIR__ . '/../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); - file_unmanaged_copy(__DIR__ . '/../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $file_system->copy(__DIR__ . '/../../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); + $file_system->copy(__DIR__ . '/../../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); $this->adminUser = $this->drupalCreateUser(['administer languages', 'translate interface', 'access administration pages']); $this->adminUserAccessSiteReports = $this->drupalCreateUser(['administer languages', 'translate interface', 'access administration pages', 'access site reports']); diff --git a/core/modules/locale/tests/src/Functional/LocaleTranslationUiTest.php b/core/modules/locale/tests/src/Functional/LocaleTranslationUiTest.php index ec6a70fe20..1c8ddc0696 100644 --- a/core/modules/locale/tests/src/Functional/LocaleTranslationUiTest.php +++ b/core/modules/locale/tests/src/Functional/LocaleTranslationUiTest.php @@ -263,7 +263,7 @@ public function testJavaScriptTranslation() { $this->assertTrue($result = file_exists($js_file), new FormattableMarkup('JavaScript file created: %file', ['%file' => $result ? $js_file : 'not found'])); // Test JavaScript translation rebuilding. - file_unmanaged_delete($js_file); + \Drupal::service('file_system')->delete($js_file); $this->assertTrue($result = !file_exists($js_file), new FormattableMarkup('JavaScript file deleted: %file', ['%file' => $result ? $js_file : 'found'])); _locale_rebuild_js($langcode); $this->assertTrue($result = file_exists($js_file), new FormattableMarkup('JavaScript file rebuilt: %file', ['%file' => $result ? $js_file : 'not found'])); diff --git a/core/modules/locale/tests/src/Functional/LocaleUpdateBase.php b/core/modules/locale/tests/src/Functional/LocaleUpdateBase.php index 387b05b0a0..63ca5e762e 100644 --- a/core/modules/locale/tests/src/Functional/LocaleUpdateBase.php +++ b/core/modules/locale/tests/src/Functional/LocaleUpdateBase.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\locale\Functional; use Drupal\Core\Database\Database; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\file\Entity\File; use Drupal\Tests\BrowserTestBase; @@ -76,7 +77,7 @@ protected function setUp() { * directory. */ protected function setTranslationsDirectory($path) { - file_prepare_directory($path, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY); $this->config('locale.settings')->set('translation.path', $path)->save(); } @@ -130,7 +131,7 @@ protected function makePoFile($path, $filename, $timestamp = NULL, array $transl } } - file_prepare_directory($path, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY); $file = File::create([ 'uid' => 1, 'filename' => $filename, diff --git a/core/modules/media/media.install b/core/modules/media/media.install index 68beb96397..570d659870 100644 --- a/core/modules/media/media.install +++ b/core/modules/media/media.install @@ -5,11 +5,13 @@ * Install, uninstall and update hooks for Media module. */ +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Url; use Drupal\media\MediaTypeInterface; use Drupal\media\Plugin\media\Source\OEmbedInterface; -use Drupal\user\RoleInterface; use Drupal\user\Entity\Role; +use Drupal\user\RoleInterface; /** * Implements hook_install(). @@ -17,7 +19,9 @@ function media_install() { $source = drupal_get_path('module', 'media') . '/images/icons'; $destination = \Drupal::config('media.settings')->get('icon_base_uri'); - file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $file_system->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $files = file_scan_directory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/'); foreach ($files as $file) { @@ -28,7 +32,13 @@ function media_install() { // referenced somewhere else. Since showing an error that it was not // possible to copy the files is also confusing, we silently do nothing. if (!file_exists($destination . DIRECTORY_SEPARATOR . $file->filename)) { - file_unmanaged_copy($file->uri, $destination, FILE_EXISTS_ERROR); + try { + $file_system->copy($file->uri, $destination, FileSystemInterface::EXISTS_ERROR); + } + catch (FileException $e) { + // Ignore and continue. + } + } } @@ -46,7 +56,7 @@ function media_requirements($phase) { $requirements = []; if ($phase == 'install') { $destination = 'public://media-icons/generic'; - file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $is_writable = is_writable($destination); $is_directory = is_dir($destination); if (!$is_writable || !$is_directory) { diff --git a/core/modules/media/src/Plugin/media/Source/OEmbed.php b/core/modules/media/src/Plugin/media/Source/OEmbed.php index 83ac585244..7b7497dcae 100644 --- a/core/modules/media/src/Plugin/media/Source/OEmbed.php +++ b/core/modules/media/src/Plugin/media/Source/OEmbed.php @@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Url; @@ -114,6 +116,13 @@ class OEmbed extends MediaSourceBase implements OEmbedInterface { */ protected $iFrameUrlHelper; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a new OEmbed instance. * @@ -143,8 +152,10 @@ class OEmbed extends MediaSourceBase implements OEmbedInterface { * The oEmbed URL resolver service. * @param \Drupal\media\IFrameUrlHelper $iframe_url_helper * The iFrame URL helper service. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, FieldTypePluginManagerInterface $field_type_manager, LoggerInterface $logger, MessengerInterface $messenger, ClientInterface $http_client, ResourceFetcherInterface $resource_fetcher, UrlResolverInterface $url_resolver, IFrameUrlHelper $iframe_url_helper) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, FieldTypePluginManagerInterface $field_type_manager, LoggerInterface $logger, MessengerInterface $messenger, ClientInterface $http_client, ResourceFetcherInterface $resource_fetcher, UrlResolverInterface $url_resolver, IFrameUrlHelper $iframe_url_helper, FileSystemInterface $file_system) { parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $field_type_manager, $config_factory); $this->logger = $logger; $this->messenger = $messenger; @@ -152,6 +163,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->resourceFetcher = $resource_fetcher; $this->urlResolver = $url_resolver; $this->iFrameUrlHelper = $iframe_url_helper; + $this->fileSystem = $file_system; } /** @@ -171,7 +183,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('http_client'), $container->get('media.oembed.resource_fetcher'), $container->get('media.oembed.url_resolver'), - $container->get('media.oembed.iframe_url_helper') + $container->get('media.oembed.iframe_url_helper'), + $container->get('file_system') ); } @@ -381,33 +394,28 @@ protected function getLocalThumbnailUri(Resource $resource) { // The local thumbnail doesn't exist yet, so try to download it. First, // ensure that the destination directory is writable, and if it's not, // log an error and bail out. - if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) { $this->logger->warning('Could not prepare thumbnail destination directory @dir for oEmbed media.', [ '@dir' => $directory, ]); return NULL; } - $error_message = 'Could not download remote thumbnail from {url}.'; - $error_context = [ - 'url' => $remote_thumbnail_url, - ]; try { $response = $this->httpClient->get($remote_thumbnail_url); if ($response->getStatusCode() === 200) { - $success = file_unmanaged_save_data((string) $response->getBody(), $local_thumbnail_uri, FILE_EXISTS_REPLACE); - - if ($success) { - return $local_thumbnail_uri; - } - else { - $this->logger->warning($error_message, $error_context); - } + $this->fileSystem->saveData((string) $response->getBody(), $local_thumbnail_uri, FileSystemInterface::EXISTS_REPLACE); + return $local_thumbnail_uri; } } catch (RequestException $e) { $this->logger->warning($e->getMessage()); } + catch (FileException $e) { + $this->logger->warning('Could not download remote thumbnail from {url}.', [ + 'url' => $remote_thumbnail_url, + ]); + } return NULL; } diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceFileTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceFileTest.php index ad509b0236..fd55af8406 100644 --- a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceFileTest.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceFileTest.php @@ -80,7 +80,7 @@ public function testMediaFileSource() { // Test the MIME type icon. $icon_base = \Drupal::config('media.settings')->get('icon_base_uri'); - file_unmanaged_copy($icon_base . '/generic.png', $icon_base . '/text--plain.png'); + \Drupal::service('file_system')->copy($icon_base . '/generic.png', $icon_base . '/text--plain.png'); $this->drupalGet("media/add/{$media_type_id}"); $page->attachFileToField("files[{$source_field_id}_0]", \Drupal::service('file_system')->realpath($test_filepath)); $result = $assert_session->waitForButton('Remove'); diff --git a/core/modules/media_library/src/Form/MediaLibraryUploadForm.php b/core/modules/media_library/src/Form/MediaLibraryUploadForm.php index e897a2178c..db97bb78be 100644 --- a/core/modules/media_library/src/Form/MediaLibraryUploadForm.php +++ b/core/modules/media_library/src/Form/MediaLibraryUploadForm.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\TypedData\FieldItemDataDefinition; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\ElementInfoManagerInterface; @@ -427,7 +428,7 @@ protected function createMediaEntity(FileInterface $file, MediaTypeInterface $ty ]); $source_field = $type->getSource()->getSourceFieldDefinition($type)->getName(); $location = $this->getUploadLocationForType($media->bundle->entity); - if (!file_prepare_directory($location, FILE_CREATE_DIRECTORY)) { + if (!\Drupal::service('file_system')->prepareDirectory($location, FileSystemInterface::CREATE_DIRECTORY)) { throw new \Exception("The destination directory '$location' is not writable"); } $file = file_move($file, $location); diff --git a/core/modules/migrate/src/Plugin/migrate/process/Download.php b/core/modules/migrate/src/Plugin/migrate/process/Download.php index cc5ed859f0..93a03c1907 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/Download.php +++ b/core/modules/migrate/src/Plugin/migrate/process/Download.php @@ -120,21 +120,21 @@ public function transform($value, MigrateExecutableInterface $migrate_executable list($source, $destination) = $value; // Modify the destination filename if necessary. - $final_destination = file_destination($destination, $this->configuration['file_exists']); + $final_destination = $this->fileSystem->getDestinationFilename($destination, $this->configuration['file_exists']); // Reuse if file exists. if (!$final_destination) { return $destination; } - // Try opening the file first, to avoid calling file_prepare_directory() + // Try opening the file first, to avoid calling prepareDirectory() // unnecessarily. We're suppressing fopen() errors because we want to try // to prepare the directory before we give up and fail. $destination_stream = @fopen($final_destination, 'w'); if (!$destination_stream) { // If fopen didn't work, make sure there's a writable directory in place. $dir = $this->fileSystem->dirname($final_destination); - if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!$this->fileSystem->prepareDirectory($dir, FileSystemInterface:: CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) { throw new MigrateException("Could not create or write to directory '$dir'"); } // Let's try that fopen again. diff --git a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php index 88f2212322..f69ab140f3 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php +++ b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\process; +use Drupal\Core\File\Exception\FileException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\StreamWrapper\LocalStream; @@ -141,10 +142,11 @@ public function transform($value, MigrateExecutableInterface $migrate_executable // Check if a writable directory exists, and if not try to create it. $dir = $this->getDirectory($destination); - // If the directory exists and is writable, avoid file_prepare_directory() - // call and write the file to destination. + // If the directory exists and is writable, avoid + // \Drupal\Core\File\FileSystemInterface::prepareDirectory() call and write + // the file to destination. if (!is_dir($dir) || !is_writable($dir)) { - if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!$this->fileSystem->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) { throw new MigrateException("Could not create or write to directory '$dir'"); } } @@ -169,15 +171,24 @@ public function transform($value, MigrateExecutableInterface $migrate_executable * @return string|bool * File destination on success, FALSE on failure. */ - protected function writeFile($source, $destination, $replace = FILE_EXISTS_REPLACE) { + protected function writeFile($source, $destination, $replace = FileSystemInterface::EXISTS_REPLACE) { // Check if there is a destination available for copying. If there isn't, // it already exists at the destination and the replace flag tells us to not // replace it. In that case, return the original destination. - if (!($final_destination = file_destination($destination, $replace))) { + if ($this->fileSystem->getDestinationFilename($destination, $replace) === FALSE) { return $destination; } - $function = 'file_unmanaged_' . ($this->configuration['move'] ? 'move' : 'copy'); - return $function($source, $destination, $replace); + try { + if ($this->configuration['move']) { + return $this->fileSystem->move($source, $destination, $replace); + } + else { + return $this->fileSystem->copy($source, $destination, $replace); + } + } + catch (FileException $e) { + return FALSE; + } } /** @@ -185,7 +196,8 @@ protected function writeFile($source, $destination, $replace = FILE_EXISTS_REPLA * * For URIs like public://foo.txt, the full physical path of public:// * will be returned, since a scheme by itself will trip up certain file - * API functions (such as file_prepare_directory()). + * API functions (such as + * \Drupal\Core\File\FileSystemInterface::prepareDirectory()). * * @param string $uri * The URI or path. diff --git a/core/modules/migrate/src/Plugin/migrate/process/FileProcessBase.php b/core/modules/migrate/src/Plugin/migrate/process/FileProcessBase.php index 8b7d810000..7b2ca1196d 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/FileProcessBase.php +++ b/core/modules/migrate/src/Plugin/migrate/process/FileProcessBase.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\process; +use Drupal\Core\File\FileSystemInterface; use Drupal\migrate\ProcessPluginBase; /** @@ -31,28 +32,28 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi if (array_key_exists('file_exists', $configuration)) { switch ($configuration['file_exists']) { case 'use existing': - $configuration['file_exists'] = FILE_EXISTS_ERROR; + $configuration['file_exists'] = FileSystemInterface::EXISTS_ERROR; break; case 'rename': - $configuration['file_exists'] = FILE_EXISTS_RENAME; + $configuration['file_exists'] = FileSystemInterface::EXISTS_RENAME; break; default: - $configuration['file_exists'] = FILE_EXISTS_REPLACE; + $configuration['file_exists'] = FileSystemInterface::EXISTS_REPLACE; } } if (array_key_exists('reuse', $configuration)) { @trigger_error("Using the key 'reuse' is deprecated, use 'file_exists' => 'use existing' instead. See https://www.drupal.org/node/2981389.", E_USER_DEPRECATED); if (!empty($configuration['reuse'])) { - $configuration['file_exists'] = FILE_EXISTS_ERROR; + $configuration['file_exists'] = FileSystemInterface::EXISTS_ERROR; } } if (array_key_exists('rename', $configuration)) { @trigger_error("Using the key 'rename' is deprecated, use 'file_exists' => 'rename' instead. See https://www.drupal.org/node/2981389.", E_USER_DEPRECATED); if (!empty($configuration['rename'])) { - $configuration['file_exists'] = FILE_EXISTS_RENAME; + $configuration['file_exists'] = FileSystemInterface::EXISTS_RENAME; } } - $configuration += ['file_exists' => FILE_EXISTS_REPLACE]; + $configuration += ['file_exists' => FileSystemInterface::EXISTS_REPLACE]; parent::__construct($configuration, $plugin_id, $plugin_definition); } diff --git a/core/modules/migrate/tests/src/Unit/process/FileCopyTest.php b/core/modules/migrate/tests/src/Unit/process/FileCopyTest.php index 07a953b53c..e2137780c6 100644 --- a/core/modules/migrate/tests/src/Unit/process/FileCopyTest.php +++ b/core/modules/migrate/tests/src/Unit/process/FileCopyTest.php @@ -7,21 +7,6 @@ use Drupal\migrate\Plugin\migrate\process\FileCopy; use Drupal\migrate\Plugin\MigrateProcessInterface; -/** - * Flag for dealing with existing files: Appends number until name is unique. - */ -define('FILE_EXISTS_RENAME', 0); - -/** - * Flag for dealing with existing files: Replace the existing file. - */ -define('FILE_EXISTS_REPLACE', 1); - -/** - * Flag for dealing with existing files: Do nothing and return FALSE. - */ -define('FILE_EXISTS_ERROR', 2); - /** * Tests the file copy process plugin. * @@ -53,8 +38,8 @@ public function testDeprecationNoticeRename($configuration, $expected) { */ public function providerDeprecationNoticeRename() { return [ - [['rename' => TRUE], FILE_EXISTS_RENAME], - [['rename' => FALSE], FILE_EXISTS_REPLACE], + [['rename' => TRUE], FileSystemInterface::EXISTS_RENAME], + [['rename' => FALSE], FileSystemInterface::EXISTS_REPLACE], ]; } @@ -79,8 +64,8 @@ public function testDeprecationNoticeReuse($configuration, $expected) { */ public function providerDeprecationNoticeReuse() { return [ - [['reuse' => TRUE], FILE_EXISTS_ERROR], - [['reuse' => FALSE], FILE_EXISTS_REPLACE], + [['reuse' => TRUE], FileSystemInterface::EXISTS_ERROR], + [['reuse' => FALSE], FileSystemInterface::EXISTS_REPLACE], ]; } @@ -103,11 +88,11 @@ public function testFileProcessBaseConstructor($configuration, $expected) { */ public function providerFileProcessBaseConstructor() { return [ - [['file_exists' => 'replace'], FILE_EXISTS_REPLACE], - [['file_exists' => 'rename'], FILE_EXISTS_RENAME], - [['file_exists' => 'use existing'], FILE_EXISTS_ERROR], - [['file_exists' => 'foobar'], FILE_EXISTS_REPLACE], - [[], FILE_EXISTS_REPLACE], + [['file_exists' => 'replace'], FileSystemInterface::EXISTS_REPLACE], + [['file_exists' => 'rename'], FileSystemInterface::EXISTS_RENAME], + [['file_exists' => 'use existing'], FileSystemInterface::EXISTS_ERROR], + [['file_exists' => 'foobar'], FileSystemInterface::EXISTS_REPLACE], + [[], FileSystemInterface::EXISTS_REPLACE], ]; } diff --git a/core/modules/node/tests/src/Kernel/Config/NodeImportCreateTest.php b/core/modules/node/tests/src/Kernel/Config/NodeImportCreateTest.php index 0b27bc2a22..4df9719065 100644 --- a/core/modules/node/tests/src/Kernel/Config/NodeImportCreateTest.php +++ b/core/modules/node/tests/src/Kernel/Config/NodeImportCreateTest.php @@ -61,7 +61,7 @@ public function testImportCreate() { // Manually add new node type. $src_dir = __DIR__ . '/../../../modules/node_test_config/sync'; $target_dir = config_get_config_directory(CONFIG_SYNC_DIRECTORY); - $this->assertTrue(file_unmanaged_copy("$src_dir/$node_type_config_name.yml", "$target_dir/$node_type_config_name.yml")); + $this->assertTrue(\Drupal::service('file_system')->copy("$src_dir/$node_type_config_name.yml", "$target_dir/$node_type_config_name.yml")); // Import the content of the sync directory. $this->configImporter()->import(); diff --git a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php index 928b2fb48f..f2be85bc53 100644 --- a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php +++ b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php @@ -136,7 +136,7 @@ protected function setUp() { $this->term->save(); // Create image. - file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); $this->image = File::create(['uri' => 'public://example.jpg']); $this->image->save(); diff --git a/core/modules/simpletest/simpletest.install b/core/modules/simpletest/simpletest.install index 4e0ea9b76d..3ecc6046b5 100644 --- a/core/modules/simpletest/simpletest.install +++ b/core/modules/simpletest/simpletest.install @@ -6,6 +6,7 @@ */ use Drupal\Component\Utility\Environment; +use Drupal\Core\File\Exception\FileException; use PHPUnit\Framework\TestCase; /** @@ -189,5 +190,11 @@ function simpletest_uninstall() { simpletest_clean_environment(); } // Delete verbose test output and any other testing framework files. - file_unmanaged_delete_recursive('public://simpletest'); + try { + \Drupal::service('file_system')->deleteRecursive('public://simpletest'); + } + catch (FileException $e) { + // Ignore. + } + } diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 1d21118cac..4219f46781 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -143,7 +143,7 @@ function simpletest_run_tests($test_list) { ->execute(); // Clear out the previous verbose files. - file_unmanaged_delete_recursive('public://simpletest/verbose'); + \Drupal::service('file_system')->deleteRecursive('public://simpletest/verbose'); // Get the info for the first test being run. $first_test = reset($test_list); @@ -699,7 +699,7 @@ function simpletest_clean_temporary_directories() { foreach ($files as $file) { if ($file[0] != '.') { $path = DRUPAL_ROOT . '/sites/simpletest/' . $file; - file_unmanaged_delete_recursive($path, function ($any_path) { + \Drupal::service('file_system')->deleteRecursive($path, function ($any_path) { @chmod($any_path, 0700); }); $count++; diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 832cc82d68..80c8eb0b7f 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -13,6 +13,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Extension\ExtensionDiscovery; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Language\Language; use Drupal\Core\Site\Settings; @@ -149,7 +150,7 @@ protected function prepareConfigDirectories() { $path = $this->siteDirectory . '/config_' . CONFIG_SYNC_DIRECTORY; $GLOBALS['config_directories'][CONFIG_SYNC_DIRECTORY] = $path; // Ensure the directory can be created and is writeable. - if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + if (!\Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) { throw new \RuntimeException("Failed to create '" . CONFIG_SYNC_DIRECTORY . "' config directory $path"); } // Provide the already resolved path for tests. @@ -285,9 +286,7 @@ protected function setUp() { // Tests based on this class are entitled to use Drupal's File and // StreamWrapper APIs. - // @todo Move StreamWrapper management into DrupalKernel. - // @see https://www.drupal.org/node/2028109 - file_prepare_directory($this->publicFilesDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($this->publicFilesDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $this->settingsSet('file_public_path', $this->publicFilesDirectory); $this->streamWrappers = []; $this->registerStreamWrapper('public', 'Drupal\Core\StreamWrapper\PublicStream'); diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index 1c7982f07e..b47d2d7e85 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -7,6 +7,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Database\Database; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Test\TestDatabase; @@ -903,7 +904,7 @@ public function run(array $methods = []) { $this->verbose = TRUE; $this->verboseDirectory = PublicStream::basePath() . '/simpletest/verbose'; $this->verboseDirectoryUrl = file_create_url($this->verboseDirectory); - if (file_prepare_directory($this->verboseDirectory, FILE_CREATE_DIRECTORY) && !file_exists($this->verboseDirectory . '/.htaccess')) { + if (\Drupal::service('file_system')->prepareDirectory($this->verboseDirectory, FileSystemInterface::CREATE_DIRECTORY) && !file_exists($this->verboseDirectory . '/.htaccess')) { file_put_contents($this->verboseDirectory . '/.htaccess', "\nExpiresActive Off\n\n"); } $this->verboseClassName = str_replace("\\", "_", $class); @@ -1125,7 +1126,7 @@ private function prepareEnvironment() { // Create test directory ahead of installation so fatal errors and debug // information can be logged during installation process. - file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($this->siteDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); // Prepare filesystem directory paths. $this->publicFilesDirectory = $this->siteDirectory . '/files'; @@ -1250,7 +1251,7 @@ private function restoreEnvironment() { \Drupal::setContainer($this->originalContainer); // Delete test site directory. - file_unmanaged_delete_recursive($this->siteDirectory, [$this, 'filePreDeleteCallback']); + \Drupal::service('file_system')->deleteRecursive($this->siteDirectory, [$this, 'filePreDeleteCallback']); // Restore original database connection. Database::removeConnection('default'); @@ -1362,11 +1363,13 @@ protected function settingsSet($name, $value) { } /** - * Ensures test files are deletable within file_unmanaged_delete_recursive(). + * Ensures test files are deletable. * * Some tests chmod generated files to be read only. During * TestBase::restoreEnvironment() and other cleanup operations, these files * need to get deleted too. + * + * @see \Drupal\Core\File\FileSystemInterface::deleteRecursive() */ public static function filePreDeleteCallback($path) { // When the webserver runs with the same system user as the test runner, we diff --git a/core/modules/simpletest/tests/src/Functional/FolderTest.php b/core/modules/simpletest/tests/src/Functional/FolderTest.php index e57813fa71..7dff18a3a6 100644 --- a/core/modules/simpletest/tests/src/Functional/FolderTest.php +++ b/core/modules/simpletest/tests/src/Functional/FolderTest.php @@ -21,7 +21,7 @@ class FolderTest extends BrowserTestBase { public function testFolderSetup() { $directory = file_default_scheme() . '://styles'; - $this->assertTrue(file_prepare_directory($directory, FALSE), 'Directory created.'); + $this->assertTrue(\Drupal::service('file_system')->prepareDirectory($directory, FALSE), 'Directory created.'); } } diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php index 907e5ce3d2..323dbe88ba 100644 --- a/core/modules/system/src/Form/ThemeSettingsForm.php +++ b/core/modules/system/src/Form/ThemeSettingsForm.php @@ -3,6 +3,8 @@ namespace Drupal\system\Form; use Drupal\Core\Extension\ThemeHandlerInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\StreamWrapper\PublicStream; @@ -56,6 +58,13 @@ class ThemeSettingsForm extends ConfigFormBase { */ protected $themeManager; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a ThemeSettingsForm object. * @@ -67,14 +76,19 @@ class ThemeSettingsForm extends ConfigFormBase { * The theme handler. * @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mime_type_guesser * The MIME type guesser instance to use. + * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager + * The theme manager. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, MimeTypeGuesserInterface $mime_type_guesser, ThemeManagerInterface $theme_manager) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, MimeTypeGuesserInterface $mime_type_guesser, ThemeManagerInterface $theme_manager, FileSystemInterface $file_system) { parent::__construct($config_factory); $this->moduleHandler = $module_handler; $this->themeHandler = $theme_handler; $this->mimeTypeGuesser = $mime_type_guesser; $this->themeManager = $theme_manager; + $this->fileSystem = $file_system; } /** @@ -86,7 +100,8 @@ public static function create(ContainerInterface $container) { $container->get('module_handler'), $container->get('theme_handler'), $container->get('file.mime_type.guesser'), - $container->get('theme.manager') + $container->get('theme.manager'), + $container->get('file_system') ); } @@ -442,16 +457,26 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // If the user uploaded a new logo or favicon, save it to a permanent location // and use it in place of the default theme-provided file. - if (!empty($values['logo_upload'])) { - $filename = file_unmanaged_copy($values['logo_upload']->getFileUri()); - $values['default_logo'] = 0; - $values['logo_path'] = $filename; + try { + if (!empty($values['logo_upload'])) { + $filename = $this->fileSystem->copy($values['logo_upload']->getFileUri()); + $values['default_logo'] = 0; + $values['logo_path'] = $filename; + } + } + catch (FileException $e) { + // Ignore. + } + try { + if (!empty($values['favicon_upload'])) { + $filename = $this->fileSystem->copy($values['favicon_upload']->getFileUri()); + $values['default_favicon'] = 0; + $values['favicon_path'] = $filename; + $values['toggle_favicon'] = 1; + } } - if (!empty($values['favicon_upload'])) { - $filename = file_unmanaged_copy($values['favicon_upload']->getFileUri()); - $values['default_favicon'] = 0; - $values['favicon_path'] = $filename; - $values['toggle_favicon'] = 1; + catch (FileException $e) { + // Ignore. } unset($values['logo_upload']); unset($values['favicon_upload']); @@ -488,7 +513,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { */ protected function validatePath($path) { // Absolute local file paths are invalid. - if (\Drupal::service('file_system')->realpath($path) == $path) { + if ($this->fileSystem->realpath($path) == $path) { return FALSE; } // A path relative to the Drupal root or a fully qualified URI is valid. diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php index eafda1d5a3..2585d3dab6 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php @@ -4,6 +4,8 @@ use Drupal\Component\Utility\Color; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\ImageToolkit\ImageToolkitBase; use Drupal\Core\ImageToolkit\ImageToolkitOperationManagerInterface; @@ -58,6 +60,13 @@ class GDToolkit extends ImageToolkitBase { */ protected $streamWrapperManager; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a GDToolkit object. * @@ -75,10 +84,13 @@ class GDToolkit extends ImageToolkitBase { * The config factory. * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager * The StreamWrapper manager. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, StreamWrapperManagerInterface $stream_wrapper_manager) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, StreamWrapperManagerInterface $stream_wrapper_manager, FileSystemInterface $file_system) { parent::__construct($configuration, $plugin_id, $plugin_definition, $operation_manager, $logger, $config_factory); $this->streamWrapperManager = $stream_wrapper_manager; + $this->fileSystem = $file_system; } /** @@ -103,7 +115,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('image.toolkit.operation.manager'), $container->get('logger.channel.image'), $container->get('config.factory'), - $container->get('stream_wrapper_manager') + $container->get('stream_wrapper_manager'), + $container->get('file_system') ); } @@ -223,7 +236,7 @@ public function save($destination) { $destination = drupal_tempnam('temporary://', 'gd_'); } // Convert stream wrapper URI to normal path. - $destination = \Drupal::service('file_system')->realpath($destination); + $destination = $this->fileSystem->realpath($destination); } $function = 'image' . image_type_to_extension($this->getType(), FALSE); @@ -243,7 +256,13 @@ public function save($destination) { } // Move temporary local file to remote destination. if (isset($permanent_destination) && $success) { - return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE); + try { + $this->fileSystem->move($destination, $permanent_destination, FileSystemInterface::EXISTS_REPLACE); + return TRUE; + } + catch (FileException $e) { + return FALSE; + } } return $success; } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 76c130d63c..4872412871 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -11,6 +11,7 @@ use Drupal\Component\Utility\OpCodeCache; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\Cache; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Path\AliasStorage; use Drupal\Core\Url; use Drupal\Core\Database\Database; @@ -627,7 +628,7 @@ function system_requirements($phase) { $directory = config_get_config_directory($type); // If we're installing Drupal try and create the config sync directory. if (!is_dir($directory) && $phase == 'install') { - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } if (!is_dir($directory)) { if ($phase == 'install') { @@ -664,7 +665,7 @@ function system_requirements($phase) { continue; } if ($phase == 'install') { - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); } $is_writable = is_writable($directory); $is_directory = is_dir($directory); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 02717b448c..1c05736067 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -11,6 +11,8 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Cache\Cache; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Queue\QueueGarbageCollectionInterface; use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Extension\Extension; @@ -1330,13 +1332,15 @@ function system_time_zones($blank = NULL, $grouped = FALSE) { * object which describes the file. * - If it fails, FALSE. */ -function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FILE_EXISTS_RENAME) { +function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FileSystemInterface::EXISTS_RENAME) { $parsed_url = parse_url($url); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); if (!isset($destination)) { $path = file_build_uri(drupal_basename($parsed_url['path'])); } else { - if (is_dir(\Drupal::service('file_system')->realpath($destination))) { + if (is_dir($file_system->realpath($destination))) { // Prevent URIs with triple slashes when glueing parts together. $path = str_replace('///', '//', "$destination/") . drupal_basename($parsed_url['path']); } @@ -1348,12 +1352,15 @@ function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $repl $data = (string) \Drupal::httpClient() ->get($url) ->getBody(); - $local = $managed ? file_save_data($data, $path, $replace) : file_unmanaged_save_data($data, $path, $replace); + $local = $managed ? file_save_data($data, $path, $replace) : $file_system->saveData($data, $path, $replace); } catch (RequestException $exception) { \Drupal::messenger()->addError(t('Failed to fetch file due to error "%error"', ['%error' => $exception->getMessage()])); return FALSE; } + catch (FileException $e) { + // Ignore. + } if (!$local) { \Drupal::messenger()->addError(t('@remote could not be saved to @path.', ['@remote' => $url, '@path' => $path])); } diff --git a/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php b/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php index cf64f079ad..dfe9a5bb0b 100644 --- a/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php +++ b/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php @@ -33,7 +33,9 @@ public function testFileRetrieving() { $this->assertEqual($retrieved_file, 'public://' . $encoded_filename, 'Sane path for downloaded file returned (public:// scheme).'); $this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (public:// scheme).'); $this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (public:// scheme).'); - file_unmanaged_delete($retrieved_file); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $file_system->delete($retrieved_file); // Test downloading file to a different location. drupal_mkdir($targetdir = 'temporary://' . $this->randomMachineName()); @@ -41,10 +43,10 @@ public function testFileRetrieving() { $this->assertEqual($retrieved_file, "$targetdir/$encoded_filename", 'Sane path for downloaded file returned (temporary:// scheme).'); $this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (temporary:// scheme).'); $this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (temporary:// scheme).'); - file_unmanaged_delete($retrieved_file); + $file_system->delete($retrieved_file); - file_unmanaged_delete_recursive($sourcedir); - file_unmanaged_delete_recursive($targetdir); + $file_system->deleteRecursive($sourcedir); + $file_system->deleteRecursive($targetdir); } } diff --git a/core/modules/update/tests/src/Functional/UpdateDeleteFileIfStaleTest.php b/core/modules/update/tests/src/Functional/UpdateDeleteFileIfStaleTest.php index 33c4ed3f0f..b468485f54 100644 --- a/core/modules/update/tests/src/Functional/UpdateDeleteFileIfStaleTest.php +++ b/core/modules/update/tests/src/Functional/UpdateDeleteFileIfStaleTest.php @@ -27,7 +27,7 @@ protected function setUp() { * Tests the deletion of stale files. */ public function testUpdateDeleteFileIfStale() { - $file_name = file_unmanaged_save_data($this->randomMachineName()); + $file_name = \Drupal::service('file_system')->saveData($this->randomMachineName()); $this->assertNotNull($file_name); // During testing the file change and the stale checking occurs in the same diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index c3f4681e75..ef04cd586c 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -165,7 +165,7 @@ function update_manager_archive_extract($file, $directory) { $extract_location = $directory . '/' . $project; if (file_exists($extract_location)) { - file_unmanaged_delete_recursive($extract_location); + \Drupal::service('file_system')->deleteRecursive($extract_location); } $archiver->extract($directory); diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 25c4d93762..26e2f22a6f 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -826,7 +826,7 @@ function update_delete_file_if_stale($path) { $max_age = \Drupal::config('system.file')->get('temporary_maximum_age'); if (REQUEST_TIME - $filectime > $max_age || (preg_match('/.*-dev\.(tar\.gz|zip)/i', $path) && REQUEST_TIME - $filectime > 300)) { - file_unmanaged_delete_recursive($path); + \Drupal::service('file_system')->deleteRecursive($path); } } } diff --git a/core/profiles/demo_umami/modules/demo_umami_content/src/InstallHelper.php b/core/profiles/demo_umami/modules/demo_umami_content/src/InstallHelper.php index 01e998e9b3..5cc191bf41 100644 --- a/core/profiles/demo_umami/modules/demo_umami_content/src/InstallHelper.php +++ b/core/profiles/demo_umami/modules/demo_umami_content/src/InstallHelper.php @@ -513,7 +513,7 @@ protected function storeCreatedContentUuids(array $uuids) { } /** - * Wrapper around file_unmanaged_copy(). + * Wrapper around \Drupal::service('file_system')->copy(). * * @param string $path * Path to image. @@ -523,7 +523,7 @@ protected function storeCreatedContentUuids(array $uuids) { */ protected function fileUnmanagedCopy($path) { $filename = basename($path); - return file_unmanaged_copy($path, 'public://' . $filename, FILE_EXISTS_REPLACE); + return \Drupal::service('file_system')->copy($path, 'public://' . $filename, FILE_EXISTS_REPLACE); } } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 5dc391e6d5..3bc1378530 100644 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -962,7 +962,7 @@ function simpletest_script_cleanup($test_id, $test_class, $exitcode) { // simpletest_clean_temporary_directories() cannot be used here, since it // would also delete file directories of other tests that are potentially // running concurrently. - file_unmanaged_delete_recursive($test_directory, ['Drupal\simpletest\TestBase', 'filePreDeleteCallback']); + \Drupal::service('file_system')->deleteRecursive($test_directory, ['Drupal\simpletest\TestBase', 'filePreDeleteCallback']); $messages[] = "- Removed test site directory."; } @@ -1565,7 +1565,7 @@ function simpletest_script_open_browser() { // Ensure we have assets verbose directory - tests with no verbose output will // not have created one. $directory = PublicStream::basePath() . '/simpletest/verbose'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $php = new Php(); $uuid = $php->generate(); $filename = $directory . '/results-' . $uuid . '.html'; diff --git a/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php index 467d9262e1..80ef82c070 100644 --- a/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php @@ -3,6 +3,7 @@ namespace Drupal\KernelTests\Core\File; use Drupal\Component\PhpStorage\FileStorage; +use Drupal\Core\File\FileSystemInterface; /** * Tests operations dealing with directories. @@ -59,10 +60,12 @@ public function testFileCheckDirectoryHandling() { $this->assertFalse(is_dir($directory), 'Directory does not exist prior to testing.'); // Non-existent directory. - $this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for non-existing directory.', 'File'); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $this->assertFalse($file_system->prepareDirectory($directory, 0), 'Error reported for non-existing directory.', 'File'); // Make a directory. - $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), 'No error reported when creating a new directory.', 'File'); + $this->assertTrue($file_system->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY), 'No error reported when creating a new directory.', 'File'); // Make sure directory actually exists. $this->assertTrue(is_dir($directory), 'Directory actually exists.', 'File'); @@ -75,11 +78,11 @@ public function testFileCheckDirectoryHandling() { // Make directory read only. @drupal_chmod($directory, 0444); - $this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for a non-writeable directory.', 'File'); + $this->assertFalse($file_system->prepareDirectory($directory, 0), 'Error reported for a non-writeable directory.', 'File'); // Test directory permission modification. $this->setSetting('file_chmod_directory', 0777); - $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), 'No error reported when making directory writeable.', 'File'); + $this->assertTrue($file_system->prepareDirectory($directory, FileSystemInterface::MODIFY_PERMISSIONS), 'No error reported when making directory writeable.', 'File'); } // Test that the directory has the correct permissions. @@ -105,14 +108,16 @@ public function testFileCreateNewFilepath() { $basename = 'xyz.txt'; $directory = 'core/misc'; $original = $directory . '/' . $basename; - $path = file_create_filename($basename, $directory); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $path = $file_system->createFilename($basename, $directory); $this->assertEqual($path, $original, format_string('New filepath %new equals %original.', ['%new' => $path, '%original' => $original]), 'File'); // Then we test against a file that already exists within that directory. $basename = 'druplicon.png'; $original = $directory . '/' . $basename; $expected = $directory . '/druplicon_0.png'; - $path = file_create_filename($basename, $directory); + $path = $file_system->createFilename($basename, $directory); $this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new (expected %expected).', ['%new' => $path, '%original' => $original, '%expected' => $expected]), 'File'); // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix. @@ -122,8 +127,8 @@ public function testFileCreateNewFilepath() { * This will test the filepath for a destination based on passed flags and * whether or not the file exists. * - * If a file exists, file_destination($destination, $replace) will either - * return: + * If a file exists, ::getDestinationFilename($destination, $replace) will + * either return: * - the existing filepath, if $replace is FILE_EXISTS_REPLACE * - a new filepath if FILE_EXISTS_RENAME * - an error (returning FALSE) if FILE_EXISTS_ERROR. @@ -133,20 +138,22 @@ public function testFileCreateNewFilepath() { public function testFileDestination() { // First test for non-existent file. $destination = 'core/misc/xyz.txt'; - $path = file_destination($destination, FILE_EXISTS_REPLACE); - $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.', 'File'); - $path = file_destination($destination, FILE_EXISTS_RENAME); - $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_RENAME.', 'File'); - $path = file_destination($destination, FILE_EXISTS_ERROR); - $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_ERROR.', 'File'); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_REPLACE); + $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FileSystemInterface::EXISTS_REPLACE.', 'File'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_RENAME); + $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FileSystemInterface::EXISTS_RENAME.', 'File'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_ERROR); + $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FileSystemInterface::EXISTS_ERROR.', 'File'); $destination = 'core/misc/druplicon.png'; - $path = file_destination($destination, FILE_EXISTS_REPLACE); - $this->assertEqual($path, $destination, 'Existing filepath destination remains the same with FILE_EXISTS_REPLACE.', 'File'); - $path = file_destination($destination, FILE_EXISTS_RENAME); - $this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File'); - $path = file_destination($destination, FILE_EXISTS_ERROR); - $this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_REPLACE); + $this->assertEqual($path, $destination, 'Existing filepath destination remains the same with FileSystemInterface::EXISTS_REPLACE.', 'File'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_RENAME); + $this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FileSystemInterface::EXISTS_RENAME.', 'File'); + $path = $file_system->getDestinationFilename($destination, FileSystemInterface::EXISTS_ERROR); + $this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FileSystemInterface::EXISTS_ERROR.', 'File'); } /** diff --git a/core/tests/Drupal/KernelTests/Core/File/UnmanagedCopyTest.php b/core/tests/Drupal/KernelTests/Core/File/FileCopyTest.php similarity index 75% rename from core/tests/Drupal/KernelTests/Core/File/UnmanagedCopyTest.php rename to core/tests/Drupal/KernelTests/Core/File/FileCopyTest.php index 655bb5f398..acce6ad98c 100644 --- a/core/tests/Drupal/KernelTests/Core/File/UnmanagedCopyTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/FileCopyTest.php @@ -2,15 +2,18 @@ namespace Drupal\KernelTests\Core\File; -use Drupal\Core\Site\Settings; +use Drupal\Core\File\Exception\FileExistsException; +use Drupal\Core\File\Exception\FileNotExistsException; use Drupal\Core\File\FileSystem; +use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Site\Settings; /** * Tests the unmanaged file copy function. * * @group File */ -class UnmanagedCopyTest extends FileTestBase { +class FileCopyTest extends FileTestBase { /** * Copy a normal file. @@ -21,7 +24,7 @@ public function testNormal() { // Copying to a new name. $desired_filepath = 'public://' . $this->randomMachineName(); - $new_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_ERROR); + $new_filepath = \Drupal::service('file_system')->copy($uri, $desired_filepath, FileSystemInterface::EXISTS_ERROR); $this->assertTrue($new_filepath, 'Copy was successful.'); $this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.'); $this->assertTrue(file_exists($uri), 'Original file remains.'); @@ -31,7 +34,7 @@ public function testNormal() { // Copying with rename. $desired_filepath = 'public://' . $this->randomMachineName(); $this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.'); - $newer_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_RENAME); + $newer_filepath = \Drupal::service('file_system')->copy($uri, $desired_filepath, FileSystemInterface::EXISTS_RENAME); $this->assertTrue($newer_filepath, 'Copy was successful.'); $this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.'); $this->assertTrue(file_exists($uri), 'Original file remains.'); @@ -49,7 +52,8 @@ public function testNonExistent() { // Copy non-existent file $desired_filepath = $this->randomMachineName(); $this->assertFalse(file_exists($desired_filepath), "Randomly named file doesn't exist."); - $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomMachineName()); + $this->expectException(FileNotExistsException::class); + $new_filepath = \Drupal::service('file_system')->copy($desired_filepath, $this->randomMachineName()); $this->assertFalse($new_filepath, 'Copying a missing file fails.'); } @@ -61,7 +65,9 @@ public function testOverwriteSelf() { $uri = $this->createUri(); // Copy the file onto itself with renaming works. - $new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_RENAME); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $new_filepath = $file_system->copy($uri, $uri, FileSystemInterface::EXISTS_RENAME); $this->assertTrue($new_filepath, 'Copying onto itself with renaming works.'); $this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.'); $this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.'); @@ -69,17 +75,18 @@ public function testOverwriteSelf() { $this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE)); // Copy the file onto itself without renaming fails. - $new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_ERROR); + $this->expectException(FileExistsException::class); + $new_filepath = $file_system->copy($uri, $uri, FileSystemInterface::EXISTS_ERROR); $this->assertFalse($new_filepath, 'Copying onto itself without renaming fails.'); $this->assertTrue(file_exists($uri), 'File exists after copying onto itself.'); // Copy the file into same directory without renaming fails. - $new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_ERROR); + $new_filepath = $file_system->copy($uri, drupal_dirname($uri), FileSystemInterface::EXISTS_ERROR); $this->assertFalse($new_filepath, 'Copying onto itself fails.'); $this->assertTrue(file_exists($uri), 'File exists after copying onto itself.'); // Copy the file into same directory with renaming works. - $new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_RENAME); + $new_filepath = $file_system->copy($uri, drupal_dirname($uri), FileSystemInterface::EXISTS_RENAME); $this->assertTrue($new_filepath, 'Copying into same directory works.'); $this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.'); $this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.'); diff --git a/core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteRecursiveTest.php b/core/tests/Drupal/KernelTests/Core/File/FileDeleteRecursiveTest.php similarity index 80% rename from core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteRecursiveTest.php rename to core/tests/Drupal/KernelTests/Core/File/FileDeleteRecursiveTest.php index 05acac413a..1e4bc150d7 100644 --- a/core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteRecursiveTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/FileDeleteRecursiveTest.php @@ -7,7 +7,7 @@ * * @group File */ -class UnmanagedDeleteRecursiveTest extends FileTestBase { +class FileDeleteRecursiveTest extends FileTestBase { /** * Delete a normal file. @@ -18,7 +18,7 @@ public function testSingleFile() { file_put_contents($filepath, ''); // Delete the file. - $this->assertTrue(file_unmanaged_delete_recursive($filepath), 'Function reported success.'); + $this->assertTrue(\Drupal::service('file_system')->deleteRecursive($filepath), 'Function reported success.'); $this->assertFalse(file_exists($filepath), 'Test file has been deleted.'); } @@ -30,7 +30,7 @@ public function testEmptyDirectory() { $directory = $this->createDirectory(); // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.'); + $this->assertTrue(\Drupal::service('file_system')->deleteRecursive($directory), 'Function reported success.'); $this->assertFalse(file_exists($directory), 'Directory has been deleted.'); } @@ -46,7 +46,7 @@ public function testDirectory() { file_put_contents($filepathB, ''); // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.'); + $this->assertTrue(\Drupal::service('file_system')->deleteRecursive($directory), 'Function reported success.'); $this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.'); $this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.'); $this->assertFalse(file_exists($directory), 'Directory has been deleted.'); @@ -65,7 +65,7 @@ public function testSubDirectory() { file_put_contents($filepathB, ''); // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.'); + $this->assertTrue(\Drupal::service('file_system')->deleteRecursive($directory), 'Function reported success.'); $this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.'); $this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.'); $this->assertFalse(file_exists($subdirectory), 'Subdirectory has been deleted.'); diff --git a/core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteTest.php b/core/tests/Drupal/KernelTests/Core/File/FileDeleteTest.php similarity index 55% rename from core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteTest.php rename to core/tests/Drupal/KernelTests/Core/File/FileDeleteTest.php index 6871f1936b..fda21c48f8 100644 --- a/core/tests/Drupal/KernelTests/Core/File/UnmanagedDeleteTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/FileDeleteTest.php @@ -2,12 +2,14 @@ namespace Drupal\KernelTests\Core\File; +use Drupal\Core\File\Exception\NotRegularFileException; + /** * Tests the unmanaged file delete function. * * @group File */ -class UnmanagedDeleteTest extends FileTestBase { +class FileDeleteTest extends FileTestBase { /** * Delete a normal file. @@ -17,7 +19,7 @@ public function testNormal() { $uri = $this->createUri(); // Delete a regular file - $this->assertTrue(file_unmanaged_delete($uri), 'Deleted worked.'); + $this->assertTrue(\Drupal::service('file_system')->delete($uri), 'Deleted worked.'); $this->assertFalse(file_exists($uri), 'Test file has actually been deleted.'); } @@ -26,7 +28,7 @@ public function testNormal() { */ public function testMissing() { // Try to delete a non-existing file - $this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomMachineName()), 'Returns true when deleting a non-existent file.'); + $this->assertTrue(\Drupal::service('file_system')->delete(file_default_scheme() . '/' . $this->randomMachineName()), 'Returns true when deleting a non-existent file.'); } /** @@ -36,8 +38,14 @@ public function testDirectory() { // A directory to operate on. $directory = $this->createDirectory(); - // Try to delete a directory - $this->assertFalse(file_unmanaged_delete($directory), 'Could not delete the delete directory.'); + // Try to delete a directory. + try { + \Drupal::service('file_system')->delete($directory); + $this->fail('Expected NotRegularFileException'); + } + catch (NotRegularFileException $e) { + // Ignore. + } $this->assertTrue(file_exists($directory), 'Directory has not been deleted.'); } diff --git a/core/tests/Drupal/KernelTests/Core/File/UnmanagedMoveTest.php b/core/tests/Drupal/KernelTests/Core/File/FileMoveTest.php similarity index 71% rename from core/tests/Drupal/KernelTests/Core/File/UnmanagedMoveTest.php rename to core/tests/Drupal/KernelTests/Core/File/FileMoveTest.php index 2e3f50c2f0..226903c55e 100644 --- a/core/tests/Drupal/KernelTests/Core/File/UnmanagedMoveTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/FileMoveTest.php @@ -2,6 +2,9 @@ namespace Drupal\KernelTests\Core\File; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\Exception\FileNotExistsException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Site\Settings; use Drupal\Core\File\FileSystem; @@ -10,7 +13,7 @@ * * @group File */ -class UnmanagedMoveTest extends FileTestBase { +class FileMoveTest extends FileTestBase { /** * Move a normal file. @@ -21,7 +24,9 @@ public function testNormal() { // Moving to a new name. $desired_filepath = 'public://' . $this->randomMachineName(); - $new_filepath = file_unmanaged_move($uri, $desired_filepath, FILE_EXISTS_ERROR); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $new_filepath = $file_system->move($uri, $desired_filepath, FileSystemInterface::EXISTS_ERROR); $this->assertTrue($new_filepath, 'Move was successful.'); $this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.'); $this->assertTrue(file_exists($new_filepath), 'File exists at the new location.'); @@ -32,7 +37,7 @@ public function testNormal() { $desired_filepath = 'public://' . $this->randomMachineName(); $this->assertTrue(file_exists($new_filepath), 'File exists before moving.'); $this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.'); - $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME); + $newer_filepath = $file_system->move($new_filepath, $desired_filepath, FileSystemInterface::EXISTS_RENAME); $this->assertTrue($newer_filepath, 'Move was successful.'); $this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.'); $this->assertTrue(file_exists($newer_filepath), 'File exists at the new location.'); @@ -48,8 +53,8 @@ public function testNormal() { */ public function testMissing() { // Move non-existent file. - $new_filepath = file_unmanaged_move($this->randomMachineName(), $this->randomMachineName()); - $this->assertFalse($new_filepath, 'Moving a missing file fails.'); + $this->expectException(FileNotExistsException::class); + \Drupal::service('file_system')->move($this->randomMachineName(), $this->randomMachineName()); } /** @@ -60,12 +65,15 @@ public function testOverwriteSelf() { $uri = $this->createUri(); // Move the file onto itself without renaming shouldn't make changes. - $new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_REPLACE); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $this->expectException(FileException::class); + $new_filepath = $file_system->move($uri, $uri, FileSystemInterface::EXISTS_REPLACE); $this->assertFalse($new_filepath, 'Moving onto itself without renaming fails.'); $this->assertTrue(file_exists($uri), 'File exists after moving onto itself.'); // Move the file onto itself with renaming will result in a new filename. - $new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_RENAME); + $new_filepath = $file_system->move($uri, $uri, FileSystemInterface::EXISTS_RENAME); $this->assertTrue($new_filepath, 'Moving onto itself with renaming works.'); $this->assertFalse(file_exists($uri), 'Original file has been removed.'); $this->assertTrue(file_exists($new_filepath), 'File exists after moving onto itself.'); diff --git a/core/tests/Drupal/KernelTests/Core/File/UnmanagedSaveDataTest.php b/core/tests/Drupal/KernelTests/Core/File/FileSaveDataTest.php similarity index 76% rename from core/tests/Drupal/KernelTests/Core/File/UnmanagedSaveDataTest.php rename to core/tests/Drupal/KernelTests/Core/File/FileSaveDataTest.php index 86ea029315..b418112326 100644 --- a/core/tests/Drupal/KernelTests/Core/File/UnmanagedSaveDataTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/FileSaveDataTest.php @@ -7,7 +7,7 @@ * * @group File */ -class UnmanagedSaveDataTest extends FileTestBase { +class FileSaveDataTest extends FileTestBase { /** * Test the file_unmanaged_save_data() function. @@ -17,13 +17,15 @@ public function testFileSaveData() { $this->setSetting('file_chmod_file', 0777); // No filename. - $filepath = file_unmanaged_save_data($contents); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $filepath = $file_system->saveData($contents); $this->assertTrue($filepath, 'Unnamed file saved correctly.'); $this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), "File was placed in Drupal's files directory."); $this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.'); // Provide a filename. - $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); + $filepath = $file_system->saveData($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); $this->assertTrue($filepath, 'Unnamed file saved correctly.'); $this->assertEqual('asdf.txt', drupal_basename($filepath), 'File was named correctly.'); $this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.'); diff --git a/core/tests/Drupal/KernelTests/Core/File/FileSystemTest.php b/core/tests/Drupal/KernelTests/Core/File/FileSystemTest.php new file mode 100644 index 0000000000..3238643b60 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/File/FileSystemTest.php @@ -0,0 +1,105 @@ +fileSystem = $this->container->get('file_system'); + } + + /** + * @covers ::copy + */ + public function testEnsureFileExistsBeforeCopy() { + // We need to compute the exception message here because it will include + // the 'real' path to the file, which varies with $this->siteDirectory. + $this->setExpectedException( + FileNotExistsException::class, + "File 'public://test.txt' ('{$this->siteDirectory}/files/test.txt') could not be copied because it does not exist" + ); + + $this->fileSystem->copy('public://test.txt', 'public://test-copy.txt'); + } + + /** + * @covers ::copy + */ + public function testDestinationDirectoryFailureOnCopy() { + $this->expectException(DirectoryNotReadyException::class); + $this->expectExceptionMessage("The specified file 'public://test.txt' could not be copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions"); + touch('public://test.txt'); + // public://subdirectory has not been created, so \Drupal::service('file_system')->prepareDirectory() + // will fail, causing copy() to throw DirectoryNotReadyException. + $this->fileSystem->copy('public://test.txt', 'public://subdirectory/test.txt'); + } + + /** + * @covers ::copy + */ + public function testCopyFailureIfFileAlreadyExists() { + $this->expectException(FileExistsException::class); + $this->expectExceptionMessage("File 'public://test.txt' could not be copied because a file by that name already exists in the destination directory ('')"); + $uri = 'public://test.txt'; + touch($uri); + $this->fileSystem->copy($uri, $uri, FileSystemInterface::EXISTS_ERROR); + } + + /** + * @covers ::copy + */ + public function testCopyFailureIfSelfOverwrite() { + $this->expectException(FileException::class); + $this->expectExceptionMessage("'public://test.txt' could not be copied because it would overwrite itself"); + $uri = 'public://test.txt'; + touch($uri); + $this->fileSystem->copy($uri, $uri, FileSystemInterface::EXISTS_REPLACE); + } + + /** + * @covers ::copy + */ + public function testCopySelfRename() { + $uri = 'public://test.txt'; + touch($uri); + $this->fileSystem->copy($uri, $uri); + $this->assertFileExists('public://test_0.txt'); + } + + /** + * @covers ::copy + */ + public function testSuccessfulCopy() { + touch('public://test.txt'); + $this->fileSystem->copy('public://test.txt', 'public://test-copy.txt'); + $this->assertFileExists('public://test-copy.txt'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteRecursiveTest.php b/core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteRecursiveTest.php similarity index 89% rename from core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteRecursiveTest.php rename to core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteRecursiveTest.php index 6be98df4aa..5d1e0ccd1f 100644 --- a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteRecursiveTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteRecursiveTest.php @@ -7,7 +7,7 @@ * * @group File */ -class RemoteFileUnmanagedDeleteRecursiveTest extends UnmanagedDeleteRecursiveTest { +class RemoteFileDeleteRecursiveTest extends FileDeleteRecursiveTest { /** * Modules to enable. diff --git a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteTest.php b/core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteTest.php similarity index 91% rename from core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteTest.php rename to core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteTest.php index f826d4dd29..b3be963c37 100644 --- a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedDeleteTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/RemoteFileDeleteTest.php @@ -7,7 +7,7 @@ * * @group File */ -class RemoteFileUnmanagedDeleteTest extends UnmanagedDeleteTest { +class RemoteFileDeleteTest extends FileDeleteTest { /** * Modules to enable. diff --git a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedMoveTest.php b/core/tests/Drupal/KernelTests/Core/File/RemoteFileMoveTest.php similarity index 91% rename from core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedMoveTest.php rename to core/tests/Drupal/KernelTests/Core/File/RemoteFileMoveTest.php index e41f87e694..1c2f9c2397 100644 --- a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedMoveTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/RemoteFileMoveTest.php @@ -7,7 +7,7 @@ * * @group File */ -class RemoteFileUnmanagedMoveTest extends UnmanagedMoveTest { +class RemoteFileMoveTest extends FileMoveTest { /** * Modules to enable. diff --git a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedSaveDataTest.php b/core/tests/Drupal/KernelTests/Core/File/RemoteFileSaveDataTest.php similarity index 90% rename from core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedSaveDataTest.php rename to core/tests/Drupal/KernelTests/Core/File/RemoteFileSaveDataTest.php index 817da28288..c7878d084c 100644 --- a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedSaveDataTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/RemoteFileSaveDataTest.php @@ -7,7 +7,7 @@ * * @group File */ -class RemoteFileUnmanagedSaveDataTest extends UnmanagedSaveDataTest { +class RemoteFileSaveDataTest extends FileSaveDataTest { /** * Modules to enable. diff --git a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedCopyTest.php b/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedCopyTest.php index b13b92cba5..1d64ab857d 100644 --- a/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedCopyTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/RemoteFileUnmanagedCopyTest.php @@ -7,7 +7,7 @@ * * @group File */ -class RemoteFileUnmanagedCopyTest extends UnmanagedCopyTest { +class RemoteFileUnmanagedCopyTest extends FileCopyTest { /** * Modules to enable. diff --git a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php index dba742df22..0496973a7d 100644 --- a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php +++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php @@ -2,6 +2,7 @@ namespace Drupal\KernelTests\Core\Image; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Image\ImageInterface; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Site\Settings; @@ -274,7 +275,7 @@ public function testManipulations() { // Prepare a directory for test file results. $directory = Settings::get('file_public_path') . '/imagetest'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); foreach ($files as $file) { foreach ($operations as $op => $values) { @@ -450,7 +451,7 @@ public function testResourceDestruction() { public function testGifTransparentImages() { // Prepare a directory for test file results. $directory = Settings::get('file_public_path') . '/imagetest'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY); + \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); // Test loading an indexed GIF image with transparent color set. // Color at top-right pixel should be fully transparent. diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/TypedDataTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/TypedDataTest.php index 4fa3b6779c..760b2d081d 100644 --- a/core/tests/Drupal/KernelTests/Core/TypedData/TypedDataTest.php +++ b/core/tests/Drupal/KernelTests/Core/TypedData/TypedDataTest.php @@ -290,7 +290,7 @@ public function testGetAndSet() { $files = []; for ($i = 0; $i < 3; $i++) { $path = "public://example_$i.png"; - file_unmanaged_copy($this->root . '/core/misc/druplicon.png', $path); + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', $path); $image = File::create(['uri' => $path]); $image->save(); $files[] = $image; diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 204eacb4f5..73a2fd4002 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -415,7 +415,7 @@ protected function setUp() { } /** - * Ensures test files are deletable within file_unmanaged_delete_recursive(). + * Ensures test files are deletable. * * Some tests chmod generated files to be read only. During * BrowserTestBase::cleanupEnvironment() and other cleanup operations, @@ -423,6 +423,8 @@ protected function setUp() { * * @param string $path * The file path. + * + * @see \Drupal\Core\File\FileSystemInterface::deleteRecursive() */ public static function filePreDeleteCallback($path) { // When the webserver runs with the same system user as phpunit, we can @@ -451,7 +453,7 @@ protected function cleanupEnvironment() { } // Delete test site directory. - file_unmanaged_delete_recursive($this->siteDirectory, [$this, 'filePreDeleteCallback']); + \Drupal::service('file_system')->deleteRecursive($this->siteDirectory, [$this, 'filePreDeleteCallback']); } /** diff --git a/core/tests/Drupal/Tests/Core/File/FileSystemDeprecationTest.php b/core/tests/Drupal/Tests/Core/File/FileSystemDeprecationTest.php index 9a55e57e49..c02b7e7298 100644 --- a/core/tests/Drupal/Tests/Core/File/FileSystemDeprecationTest.php +++ b/core/tests/Drupal/Tests/Core/File/FileSystemDeprecationTest.php @@ -26,4 +26,68 @@ public function testDeprecatedFileMoveUploadedFile() { $this->assertNotNull(drupal_move_uploaded_file('', '')); } + /** + * @expectedDeprecation file_unmanaged_copy() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::copy(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedFileCopy() { + $this->assertNotNull(file_unmanaged_copy(NULL)); + } + + /** + * @expectedDeprecation file_unmanaged_delete() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::delete(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedFileDelete() { + $this->assertNotNull(file_unmanaged_delete(NULL)); + } + + /** + * @expectedDeprecation file_unmanaged_delete_recursive() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::deleteRecursive(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedFileDeleteRecursive() { + $this->assertNotNull(file_unmanaged_delete_recursive(NULL)); + } + + /** + * @expectedDeprecation file_unmanaged_move() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::move(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedFileMove() { + $this->assertNotNull(file_unmanaged_move(NULL)); + } + + /** + * @expectedDeprecation file_unmanaged_prepare() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\UnmanagedFileHandler::prepareDestination(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedPrepare() { + $this->assertNotNull(file_unmanaged_prepare(NULL)); + } + + /** + * @expectedDeprecation file_unmanaged_save_data() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::saveData(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedUnmanagedSaveData() { + $this->assertNotNull(file_unmanaged_save_data(NULL)); + } + + /** + * @expectedDeprecation file_prepare_directory() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::prepareDirectory(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedFilePrepareDirectory() { + $dir = NULL; + $this->assertNotNull(file_prepare_directory($dir)); + } + + /** + * @expectedDeprecation file_destination() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedFileDestination() { + $this->assertNotNull(file_destination('', '')); + } + + /** + * @expectedDeprecation file_create_filename() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::createFilename(). See https://www.drupal.org/node/3006851. + */ + public function testDeprecatedFileCreate() { + $this->assertNotNull(file_create_filename('', '')); + } + } diff --git a/core/tests/Drupal/Tests/TestFileCreationTrait.php b/core/tests/Drupal/Tests/TestFileCreationTrait.php index a2f119b65c..ef783db860 100644 --- a/core/tests/Drupal/Tests/TestFileCreationTrait.php +++ b/core/tests/Drupal/Tests/TestFileCreationTrait.php @@ -71,7 +71,7 @@ protected function getTestFiles($type, $size = NULL) { $original = drupal_get_path('module', 'simpletest') . '/files'; $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); foreach ($files as $file) { - file_unmanaged_copy($file->uri, PublicStream::basePath()); + \Drupal::service('file_system')->copy($file->uri, PublicStream::basePath()); } $this->generatedTestFiles = TRUE;