diff --git a/core/core.services.yml b/core/core.services.yml index 9ca753e6a4485195e90d864a61459d4259791d6f..a842043e12d66e2e04939a03041140db01135bef 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 19e03e1af7475845d3b0e51dda637ead8ae35963..6f4552fc4775675c2576d4c72798f8cc1a98a00b 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,25 +454,20 @@ 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; } /** @@ -500,12 +497,18 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST * @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\FileSystemInterface::getDestinationFilename() 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) { + @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\FileSystemInterface::getDestinationFilename() instead. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); $original_source = $source; $logger = \Drupal::logger('file'); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ $file_system = \Drupal::service('file_system'); // Assert that the source file actually exists. @@ -587,26 +590,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 +632,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 +744,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); } /** @@ -870,28 +816,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; } /** @@ -917,26 +856,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); } /** @@ -976,18 +910,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 ce5ca8ad109d717032c55c61729a228624302e12..18f6a398bf3fea770a2ac4b0e419a929fea73089 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 b0d33048f5d535fca6541b13d165a658919947a6..45a8b141c3befcbdaa9809cd457551633d743250 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 <a href=":handbook_url">online handbook</a>.', [ '%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 227ef0c8b75d6b6488b2687cc4a5a94ae23f5b56..a5228992e11c1bd5794ceff1f519a2741bdf78f0 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 $file_system + * The file handler. + */ + public function __construct(FileSystemInterface $file_system = NULL) { + $this->fileSystem = $file_system; + } + /** * {@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->getFileSystem()->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY); + try { + if (!file_exists($uri) && !$this->getFileSystem()->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,11 +61,30 @@ 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->getFileSystem()->saveData(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { + return FALSE; + } + } + catch (FileException $e) { return FALSE; } } return $uri; } + /** + * Helper method for returning the file system service. + * + * @return \Drupal\Core\File\FileSystemInterface + * The file system service. + */ + private function getFileSystem() { + if (!$this->fileSystem) { + @trigger_error('\Drupal\Core\File\FileSystemInterface is a dependency of this class in Drupal 8.7.0 and will be required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $this->fileSystem = \Drupal::service('file_system'); + } + return $this->fileSystem; + } + } diff --git a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php index 30577ef30df342f1c24dd7a263d283aff5649899..94712188bf16a9cc9eb0ff09a87780b2e1033bf1 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,19 @@ 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 $file_system + * 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 $file_system = NULL) { $this->grouper = $grouper; $this->optimizer = $optimizer; $this->dumper = $dumper; $this->state = $state; + if (!$file_system) { + @trigger_error('The file_system service must be passed to CssCollectionOptimizer::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -178,7 +193,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 e9b8bf2075e48e4035c05eb5a93fa030ed9e5195..a02299ff4960069af8b3f7081f46adbd2ffd18c7 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,19 @@ 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 $file_system + * 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 $file_system = NULL) { $this->grouper = $grouper; $this->optimizer = $optimizer; $this->dumper = $dumper; $this->state = $state; + if (!$file_system) { + @trigger_error('The file_system service must be passed to JsCollectionOptimizer::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -180,7 +195,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 42aabb557f838ec01c818ff4be6d58ff956863dc..518da73a7f388fad38467598278c9ca4c1998e5c 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 9afd104ff39e30f29f2b8d8b566ac01500e8e923..e30230b4078aa27f50042ccec3cf20471c8f6fbe 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 0000000000000000000000000000000000000000..7750d7c04dc77adaa8b76ecc58b902bb9e081c81 --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/DirectoryNotReadyException.php @@ -0,0 +1,12 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Exception thrown when a file's destination directory is not ready. + * + * A directory can be considered not ready when it either does not exist, or + * is not writable. + */ +class DirectoryNotReadyException extends FileException { +} diff --git a/core/lib/Drupal/Core/File/Exception/FileException.php b/core/lib/Drupal/Core/File/Exception/FileException.php new file mode 100644 index 0000000000000000000000000000000000000000..4bb6a0c01efa9b24f548c02b010748098ebd273e --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/FileException.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Base class for exceptions related to file handling operations. + */ +class FileException extends \RuntimeException { +} diff --git a/core/lib/Drupal/Core/File/Exception/FileExistsException.php b/core/lib/Drupal/Core/File/Exception/FileExistsException.php new file mode 100644 index 0000000000000000000000000000000000000000..3b9ff5a8646be1b60466bcb5ee5e2548f784da3d --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/FileExistsException.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Exception thrown when a file unexpectedly exists. + */ +class FileExistsException extends FileException { +} diff --git a/core/lib/Drupal/Core/File/Exception/FileNotExistsException.php b/core/lib/Drupal/Core/File/Exception/FileNotExistsException.php new file mode 100644 index 0000000000000000000000000000000000000000..2df87e07f40365c5e9a8a0a04c9665676629f20d --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/FileNotExistsException.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Exception thrown when a file is expected to exist but does not. + */ +class FileNotExistsException extends FileException { +} diff --git a/core/lib/Drupal/Core/File/Exception/FileWriteException.php b/core/lib/Drupal/Core/File/Exception/FileWriteException.php new file mode 100644 index 0000000000000000000000000000000000000000..d0af7c9d0d6fd27e5f102cd4f7e1c0497b82db32 --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/FileWriteException.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Exception thrown when file cannot be written to disk. + */ +class FileWriteException extends FileException { +} diff --git a/core/lib/Drupal/Core/File/Exception/NotRegularFileException.php b/core/lib/Drupal/Core/File/Exception/NotRegularFileException.php new file mode 100644 index 0000000000000000000000000000000000000000..f085ce87895058c2ad238b434372d72336eb8251 --- /dev/null +++ b/core/lib/Drupal/Core/File/Exception/NotRegularFileException.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Core\File\Exception; + +/** + * Exception thrown when a target is not a regular file (e.g. a directory). + */ +class NotRegularFileException extends FileException { +} diff --git a/core/lib/Drupal/Core/File/FileSystem.php b/core/lib/Drupal/Core/File/FileSystem.php index 9f92d0bd8b225b0854f0af988a2d9bf6f6cbfbdd..2f30ed030351859bdde432891b2aebbf2763048e 100644 --- a/core/lib/Drupal/Core/File/FileSystem.php +++ b/core/lib/Drupal/Core/File/FileSystem.php @@ -2,6 +2,12 @@ namespace Drupal\Core\File; +use Drupal\Core\File\Exception\DirectoryNotReadyException; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\Exception\FileExistsException; +use Drupal\Core\File\Exception\FileNotExistsException; +use Drupal\Core\File\Exception\FileWriteException; +use Drupal\Core\File\Exception\NotRegularFileException; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; use Psr\Log\LoggerInterface; @@ -301,4 +307,331 @@ public function validScheme($scheme) { return class_exists($this->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("The specified file '$source' could not be copied to '$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("Failed to unlink file '$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("Cannot delete '$path' because it is a directory. Use deleteRecursive() instead."); + } + + // 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)) { + $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("The file '$path' is not of a recognized type so it was not deleted."); + } + + /** + * {@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("The specified file '$source' could not be moved to '$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("The source file '$source' could not be unlinked after copying to '$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. + * + * @see \Drupal\Core\File\FileSystemInterface::copy() + * @see \Drupal\Core\File\FileSystemInterface::move() + */ + protected function prepareDestination($source, &$destination, $replace) { + $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("File '$original_source' ('$realpath') could not be copied because it does not exist."); + } + else { + $this->logger->error("File '%original_source' could not be copied because it does not exist.", [ + '%original_source' => $original_source, + ]); + throw new FileNotExistsException("File '$original_source' could not be copied because it does not exist."); + } + } + + // 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("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."); + } + } + + // 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("File '$original_source' could not be copied because a file by that name already exists in the destination directory ('$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("File '$source' could not be copied because it would overwrite itself."); + } + // 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("Temporary file '$temp_name' could not be created."); + } + + // 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 6682f46361e7c006aa2e29c3974e34ed2da3fc12..c35ff2c26eca2d3e8d29499d61c4eefc83600bb7 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 9e8307a5571790dbf70b89edebfb4ed9f37cd8af..0ec3388fec184add5dabf34600f7db36f27d45e4 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 3181eae91ab2698eab4063fc156c579fbef9e02a..4a0c291b2ebda02f557e8c8601505aca4d7fab0f 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 25ca2bf3b33eab5f085ab0ca03f5f612e1b42596..7e37510dc578293c586b3ecdf5a6a4dd2de87557 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 a25be981d4a14bf6c3c514c7348593656646a0ee..140890aff46e58d641df23ab8ab8f48fada4ba7f 100644 --- a/core/modules/color/color.module +++ b/core/modules/color/color.module @@ -5,12 +5,14 @@ * 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\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\Element\Textfield; @@ -440,8 +442,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 +462,12 @@ 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); + try { + $filepath = $file_system->copy($source, $paths['target'] . $base); + } + catch (FileException $e) { + $filepath = FALSE; + } $paths['map'][$file] = $base; $paths['files'][] = $filepath; } @@ -582,7 +591,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 bf47f72d4bc9600f62df6a5096db59cdf3d9bc02..1a72b9951baed01c19acd5c9f85d28327a5f0712 100644 --- a/core/modules/config/src/Controller/ConfigController.php +++ b/core/modules/config/src/Controller/ConfigController.php @@ -7,6 +7,8 @@ use Drupal\Core\Config\StorageInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Diff\DiffFormatter; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Serialization\Yaml; use Drupal\Core\Url; use Drupal\system\FileDownloadController; @@ -53,6 +55,13 @@ class ConfigController implements ContainerInjectionInterface { */ protected $diffFormatter; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * {@inheritdoc} */ @@ -62,7 +71,8 @@ public static function create(ContainerInterface $container) { $container->get('config.storage.sync'), $container->get('config.manager'), new FileDownloadController(), - $container->get('diff.formatter') + $container->get('diff.formatter'), + $container->get('file_system') ); } @@ -72,23 +82,35 @@ public static function create(ContainerInterface $container) { * @param \Drupal\Core\Config\StorageInterface $target_storage * The target storage. * @param \Drupal\Core\Config\StorageInterface $source_storage - * The source storage + * The source storage. + * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager + * The config manager. * @param \Drupal\system\FileDownloadController $file_download_controller * The file download controller. + * @param \Drupal\Core\Diff\DiffFormatter $diff_formatter + * The diff formatter. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller, DiffFormatter $diff_formatter) { + public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller, DiffFormatter $diff_formatter, FileSystemInterface $file_system) { $this->targetStorage = $target_storage; $this->sourceStorage = $source_storage; $this->configManager = $config_manager; $this->fileDownloadController = $file_download_controller; $this->diffFormatter = $diff_formatter; + $this->fileSystem = $file_system; } /** * Downloads a tarball of the site configuration. */ public function downloadExport() { - file_unmanaged_delete(file_directory_temp() . '/config.tar.gz'); + try { + $this->fileSystem->delete(file_directory_temp() . '/config.tar.gz'); + } + catch (FileException $e) { + // Ignore failed deletes. + } $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 ab235ed8e50bea9827b16d981840a86b7fb6935f..15dc91e0f2c8a9c06c43bf2821b248e9ad94add3 100644 --- a/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php +++ b/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Config\StorageInterface; +use Drupal\Core\File\Exception\FileException; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\BrowserTestBase; @@ -201,7 +202,12 @@ 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); + try { + \Drupal::service('file_system')->deleteRecursive($directory); + } + catch (FileException $e) { + // Ignore failed deletes. + } $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 5e155824f913e5507c8f3279513459a03f410047..8d5112ef16dc5d1507e29b03e9cee51800e7e521 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 d220cabc576ea723680c490e459de7de5cab5541..4124b53cb78552016b3bd583c48ea74865020f6b 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; @@ -153,8 +155,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 { @@ -164,7 +168,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)); @@ -193,7 +198,9 @@ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_E return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } } /** @@ -224,12 +231,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 { @@ -239,7 +248,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; @@ -272,7 +282,9 @@ function file_move(FileInterface $source, $destination = NULL, $replace = FILE_E return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } } /** @@ -555,7 +567,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, @@ -583,7 +596,10 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM $file->save(); return $file; } - return FALSE; + catch (FileException $e) { + return FALSE; + } + } /** @@ -920,7 +936,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: @@ -1020,9 +1036,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; @@ -1053,7 +1071,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; @@ -1065,7 +1083,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); @@ -1358,7 +1376,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 f768ab79bc5ecaffc8d3297f05b871c2569621d5..d8aab938361a013f4cc6d118a850f04490ed2e52 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 542be122dfcd07a46d5d9b46af9f8be8db485be4..202a96682b155bf609a449eb2bc6dcda64d705f2 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 44600369df671ea00003d9904d8959b7250ea799..10810581a22b97ddd8af631438da048a783f6e5d 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'); } @@ -239,8 +240,7 @@ 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); + $file_uri = $this->fileSystem->getDestinationFilename($file_uri, FileSystemInterface::EXISTS_RENAME); // Lock based on the prepared file URI. $lock_id = $this->generateLockIdFromFileUri($file_uri); @@ -265,8 +265,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::getDestinationFilename(). + 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 bbac4d05c9afc02a9275f0e70ada5a174a88fbd8..08c4a57ffd7a3a0c03efc103aa9bcddd7ab92b71 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 ef8e0afbfb2aa6c1eeb79ccdd029ce68269a22f2..10127dc94cfefa105d067ef28d1b4be42afe7c47 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 95693ec33315b663aa8f3f1a7ae128dfc72aef14..bf25a44cad35680cf66a222cc14f05b5402f5dc5 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 94d00ef9f2b78ea1182c5b58c98e127210571668..7feaa507dab8464c1a80d86887608fe6db9325e8 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 2a43a9997f9e310bc45d8e31ef1cebefce77daca..a9b3aca2049bab28bcb5261cf8e0695c0152dac0 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 552cfc628e69316f8669a217468f230795a5ff81..0e3d6da488316ed2c2e9c50064c9b5b8c10bc194 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 573fe25444143a762d438af68fb828328ed7b639..de385935c6092e546695e293f4a6ef6c48975445 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 065d4a6f792d7fe212c7ad70916780f3e0301384..0c14a5cee1db982f69a2806c5dfdf28fbf0dba3d 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 3be79ba270a7c58682592c722796586b704b75b0..0342d512f6180fdfa03d079dffabc0bd27e4f3a6 100644 --- a/core/modules/image/image.install +++ b/core/modules/image/image.install @@ -5,13 +5,16 @@ * Install, update and uninstall functions for the image module. */ +use Drupal\Core\File\Exception\FileException; +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 +22,14 @@ function image_install() { */ function image_uninstall() { // Remove the styles directory and generated images. - file_unmanaged_delete_recursive(file_default_scheme() . '://styles'); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + try { + $file_system->deleteRecursive(file_default_scheme() . '://styles'); + } + catch (FileException $e) { + // Ignore failed deletes. + } } /** diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 201e137da6664e88e364d2fc46aa9426687b1863..1a05ae2bb21275b64325914785bbac9bad6b69b8 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 1a41c02e73849de90cebb86bcd0d35d662fa9644..e7e36647b213656e01a822002cf63f4b58efb4b4 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. * @@ -60,8 +68,10 @@ class QuickEditImageController extends ControllerBase { * The tempstore factory. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository * The entity display repository service. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - 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, EntityDisplayRepositoryInterface $entity_display_repository = NULL, FileSystemInterface $file_system = NULL) { $this->renderer = $renderer; $this->imageFactory = $image_factory; $this->tempStore = $temp_store_factory->get('quickedit'); @@ -70,6 +80,11 @@ public function __construct(RendererInterface $renderer, ImageFactory $image_fac $entity_display_repository = \Drupal::service('entity_display.repository'); } $this->entityDisplayRepository = $entity_display_repository; + if (!$file_system) { + @trigger_error('The file_system service must be passed to QuickEditImageController::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -80,7 +95,8 @@ public static function create(ContainerInterface $container) { $container->get('renderer'), $container->get('image.factory'), $container->get('tempstore.private'), - $container->get('entity_display.repository') + $container->get('entity_display.repository'), + $container->get('file_system') ); } @@ -111,7 +127,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 94e3f3c12d3c6104f6219e036b2832e587a2f11b..5a8667dc84b26c955610b1dc462818a30a429404 100644 --- a/core/modules/image/src/Entity/ImageStyle.php +++ b/core/modules/image/src/Entity/ImageStyle.php @@ -7,6 +7,8 @@ use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Routing\RequestHelper; use Drupal\Core\Site\Settings; use Drupal\Core\Url; @@ -251,10 +253,17 @@ 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); + try { + $file_system->delete($derivative_uri); + } + catch (FileException $e) { + // Ignore failed deletes. + } } return $this; } @@ -263,7 +272,12 @@ 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); + try { + $file_system->deleteRecursive($directory); + } + catch (FileException $e) { + // Ignore failed deletes. + } } } @@ -293,7 +307,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 b2f802a776c9a88bb015c0ce0375b288e0a477bd..2c47b0aa4687e79c94e45536f33ceeecb6f75410 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -6,6 +6,8 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; @@ -344,15 +346,22 @@ 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); - if ($path = $random->image(\Drupal::service('file_system')->realpath($destination), $min_resolution, $max_resolution)) { + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + try { + $file_system->move($tmp_file, $destination); + } + catch (FileException $e) { + // Ignore failed move. + } + if ($path = $random->image($file_system->realpath($destination), $min_resolution, $max_resolution)) { $image = File::create(); $image->setFileUri($path); $image->setOwnerId(\Drupal::currentUser()->id()); $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); + $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 3cf3be71b8046a2e4444efcdf7cd8fce7a732263..fd253635822635398e47fca0a66be242801a5636 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 144d03ee179682e6af363663d4dc17eabfd0a37d..66a3e54b61bc8caa82f94c44fdd50df7a25bd0a5 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 4a11bf956eb55af909b3e20062bc7a8b993ced5b..4c80bb1774fcd912de746e0ff003ba96b393daa3 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), '<img src="' . $url . '" width="100" height="100" alt="" class="image-style-test-uri" />'); $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), '<img src="' . $url . '" width="50" height="50" alt="" class="image-style-test-uri" />'); diff --git a/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php b/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php index 66899fb8257ec67e63ad6da91f341ebea829e390..3998b350e7cc789f564e82e0e40f08657a1fedcd 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 169bfa01f82eaffed3e047dc3244b07880471e03..00b9615db7a231f483aeea5ed1aac457116f6363 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 c905c04b4bd17091b38c68483aee4e87ab369b69..69cb5d8c7816b9839fe162265f6120fbc372896f 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 9621423823d566cb4e130d9fe59e842c2f6c9157..bd2c53460d9394ef346860bd5c6a71188f528fe0 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 bffd6545f6d33a90d237aa291800d6e7c77de5da..8a1c2c6e748700fe2cdadfa2b3f4e9898600010d 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 3feee12c5b68d0e0b1ed7ee42359c7297439c2e8..1b9031f245c2ceafb1f60e164e191878813f7993 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 480545ad903353fa3bcdc5a4e092cb16f62f572b..94117df1fa6f97b010b4f0cf7ca3396b82610d49 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 e0a9b4877d2881ea59edade83f132b51cf29d7e3..149d541798d0815443ff43c0ea15f9f615d35941 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 05621f6c13dec094de4058781d73b9630bb43f73..3d0d1f416e1726b97e296552239c97a2366d0716 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 c63c4e4261c051039a95846bb374cf5fa227dacd..22762f7ae0fa785953f382fb49778a9b626ac0c5 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 ec6a70fe20300c3a3654def3d36139c1700fc54e..1c8ddc06962098f817830ea545504a2ce7563d98 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 387b05b0a03d6c737cfc8cccf7e94c892c7e0a03..63ca5e762eaeec6cf196c91bc183bd9efea0cac5 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 68beb9639775995986504766d87a3e5e957b9197..570d65987033be39f413581b89336550eb4acac4 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 83ac5852444ef532f52c2a3330e99a9880ff92f7..afc626448662d8292d56c5da62a8944fbb329b57 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 = NULL) { 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,11 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->resourceFetcher = $resource_fetcher; $this->urlResolver = $url_resolver; $this->iFrameUrlHelper = $iframe_url_helper; + if (!$file_system) { + @trigger_error('The file_system service must be passed to OEmbed::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -171,7 +187,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 +398,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 ad509b02366ef558433ccc091d459ccd9a25001a..fd55af8406714050f7789f5150d9ad4832460fcf 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/FileUploadForm.php b/core/modules/media_library/src/Form/FileUploadForm.php index e89b67e7d150c688c0cfd9e08e29eb5b3f1ea710..407fa469aea511cd43dd83708d0c46f7b0dcb51d 100644 --- a/core/modules/media_library/src/Form/FileUploadForm.php +++ b/core/modules/media_library/src/Form/FileUploadForm.php @@ -6,6 +6,8 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\TypedData\FieldItemDataDefinition; +use Drupal\Core\File\Exception\FileWriteException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\ElementInfoManagerInterface; use Drupal\Core\Render\RendererInterface; @@ -41,6 +43,13 @@ class FileUploadForm extends AddFormBase { */ protected $renderer; + /** + * The file system service. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a new FileUploadForm. * @@ -52,11 +61,14 @@ class FileUploadForm extends AddFormBase { * The element info manager. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer, FileSystemInterface $file_system) { parent::__construct($entity_type_manager, $library_ui_builder); $this->elementInfo = $element_info; $this->renderer = $renderer; + $this->fileSystem = $file_system; } /** @@ -67,7 +79,8 @@ public static function create(ContainerInterface $container) { $container->get('entity_type.manager'), $container->get('media_library.ui_builder'), $container->get('element_info'), - $container->get('renderer') + $container->get('renderer'), + $container->get('file_system') ); } @@ -209,8 +222,8 @@ protected function createMediaFromValue(MediaTypeInterface $media_type, EntitySt // Create a file item to get the upload location. $item = $this->createFileItem($media_type); $upload_location = $item->getUploadLocation(); - if (!file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY)) { - throw new \Exception("The destination directory '$upload_location' is not writable"); + if (!$this->fileSystem->prepareDirectory($upload_location, FileSystemInterface::CREATE_DIRECTORY)) { + throw new FileWriteException("The destination directory '$upload_location' is not writable"); } $file = file_move($file, $upload_location); if (!$file) { diff --git a/core/modules/migrate/src/Plugin/migrate/process/Download.php b/core/modules/migrate/src/Plugin/migrate/process/Download.php index cc5ed859f01301baff095c326f278d39044b45cb..93a03c1907c7508e16f0b8569bc21afbeb34fb3c 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 88f22123224253f4ced725ab106909ce1bb7e770..f69ab140f3983d5001e0f085c215f7b55b45ecb3 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 8b7d81000006865402776839a5a493832f850285..7b2ca1196db4922fa77acc22fb8f97db55427a8c 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 07a953b53c43d639c494d889368036d556187328..e2137780c675c275592f9ef6f4407167d7a99fb5 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 0b27bc2a22c784dbda4147f9aac8dfd97a21862b..4df97190655e2ea07b640326216b08be5cdee389 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 928b2fb48f88b74bbe08f4d6f28a3492223b0992..f2be85bc531ee43cd322255b6f3f3e3c1666650f 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 4e0ea9b76db91e5193e515f0bb2df0b9f3327931..3ecc6046b5e4b03a88cd0d72db567cbf9c28ed1a 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 1d21118cacacd8fa21cd28b6ca7c5942f9f3fcc0..92226508a8ff41d3cf2578bbce83942c357501d7 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -7,6 +7,7 @@ use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Database\Database; +use Drupal\Core\File\Exception\FileException; use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\simpletest\TestBase; @@ -143,7 +144,12 @@ function simpletest_run_tests($test_list) { ->execute(); // Clear out the previous verbose files. - file_unmanaged_delete_recursive('public://simpletest/verbose'); + try { + \Drupal::service('file_system')->deleteRecursive('public://simpletest/verbose'); + } + catch (FileException $e) { + // Ignore failed deletes. + } // Get the info for the first test being run. $first_test = reset($test_list); @@ -699,9 +705,14 @@ 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) { - @chmod($any_path, 0700); - }); + try { + \Drupal::service('file_system')->deleteRecursive($path, function ($any_path) { + @chmod($any_path, 0700); + }); + } + catch (FileException $e) { + // Ignore failed deletes. + } $count++; } } diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 832cc82d6817186eb3d8f0c5b7099745f616b414..80c8eb0b7fb828e1556b4d4c82508f7ca5b848e0 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 1c7982f07e740b90f504dd783bb6232d107e4b62..b47d2d7e850e03257dd9f727cf3d577acd4be303 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', "<IfModule mod_expires.c>\nExpiresActive Off\n</IfModule>\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/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php index 907e5ce3d2de1434d102de360a3b94c34a5433dd..ce242ee4a95bca63bac7fe790d1e2f249905d8c2 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,23 @@ 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 = NULL) { parent::__construct($config_factory); $this->moduleHandler = $module_handler; $this->themeHandler = $theme_handler; $this->mimeTypeGuesser = $mime_type_guesser; $this->themeManager = $theme_manager; + if (!$file_system) { + @trigger_error('The file_system service must be passed to ThemeSettingsForm::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -86,7 +104,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 +461,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 +517,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 eafda1d5a3912e60f40c9c1863b8fc6019f6dc2e..e1512f17203511101509d012c4b8be87d7cf6f0f 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,17 @@ 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 = NULL) { parent::__construct($configuration, $plugin_id, $plugin_definition, $operation_manager, $logger, $config_factory); $this->streamWrapperManager = $stream_wrapper_manager; + if (!$file_system) { + @trigger_error('The file_system service must be passed to GDToolkit::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED); + $file_system = \Drupal::service('file_system'); + } + $this->fileSystem = $file_system; } /** @@ -103,7 +119,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 +240,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 +260,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 32d17e32107dab7fb583bc437ca5881d39f2dfd4..ff6b8fd8ce15af581cd242c18de48a36322d267a 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; @@ -628,7 +629,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') { @@ -665,7 +666,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 02717b448c2dc856a6714a6e6812f6ff2e72513e..91fa9f514217e87b646cf9c42a5c6e60e16f2122 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,16 @@ 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) { + \Drupal::messenger()->addError(t('Failed to save file due to error "%error"', ['%error' => $e->getMessage()])); + return FALSE; + } 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 cf64f079adc3c072f4a9191ba44b202a9cc6d8d1..dfe9a5bb0b334fec6f96b6eb1e9b9151a5d047a0 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 33c4ed3f0fe2246c5af5fc63fcdf82bbb9af5e61..b468485f54f1ee08eab96b22dc05d9271ae7b2c1 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 c3f4681e75ae78572700233df94cf1ef5e0964fa..61edc15cf92d90aa14bf9b2613c7ba7cb432ff5a 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -36,6 +36,7 @@ * root. */ +use Drupal\Core\File\Exception\FileException; use Symfony\Component\HttpFoundation\RedirectResponse; /** @@ -165,7 +166,12 @@ function update_manager_archive_extract($file, $directory) { $extract_location = $directory . '/' . $project; if (file_exists($extract_location)) { - file_unmanaged_delete_recursive($extract_location); + try { + \Drupal::service('file_system')->deleteRecursive($extract_location); + } + catch (FileException $e) { + // Ignore failed deletes. + } } $archiver->extract($directory); diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 25c4d93762aac194f358be1f29e178ac181b4725..a4f32929c0706f5f771035f6d55eb1deacd58229 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -11,6 +11,7 @@ * ability to install contributed modules and themes via an user interface. */ +use Drupal\Core\File\Exception\FileException; use Drupal\Core\Url; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -826,7 +827,12 @@ 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); + try { + \Drupal::service('file_system')->deleteRecursive($path); + } + catch (FileException $e) { + // Ignore failed deletes. + } } } } 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 037680120d1797160d3855a95f3dc8c12a8bb7c6..3793b315bd3ac83c19373bb7127e144018cdb219 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 @@ -2,13 +2,15 @@ namespace Drupal\demo_umami_content; +use Drupal\Component\Utility\Html; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Path\AliasManagerInterface; use Drupal\Core\State\StateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\Component\Utility\Html; /** * Defines a helper class for importing default content. @@ -46,6 +48,13 @@ class InstallHelper implements ContainerInjectionInterface { */ protected $state; + /** + * The file system. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + /** * Constructs a new InstallHelper object. * @@ -57,12 +66,15 @@ class InstallHelper implements ContainerInjectionInterface { * Module handler. * @param \Drupal\Core\State\StateInterface $state * State service. + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file system. */ - public function __construct(AliasManagerInterface $aliasManager, EntityTypeManagerInterface $entityTypeManager, ModuleHandlerInterface $moduleHandler, StateInterface $state) { + public function __construct(AliasManagerInterface $aliasManager, EntityTypeManagerInterface $entityTypeManager, ModuleHandlerInterface $moduleHandler, StateInterface $state, FileSystemInterface $fileSystem) { $this->aliasManager = $aliasManager; $this->entityTypeManager = $entityTypeManager; $this->moduleHandler = $moduleHandler; $this->state = $state; + $this->fileSystem = $fileSystem; } /** @@ -73,7 +85,8 @@ public static function create(ContainerInterface $container) { $container->get('path.alias_manager'), $container->get('entity_type.manager'), $container->get('module_handler'), - $container->get('state') + $container->get('state'), + $container->get('file_system') ); } @@ -513,7 +526,13 @@ protected function getTerm($term_name, $vocabulary_id = 'tags') { * File ID. */ protected function createFileEntity($path) { - $uri = $this->fileUnmanagedCopy($path); + $filename = basename($path); + try { + $uri = $this->fileSystem->copy($path, 'public://' . $filename, FileSystemInterface::EXISTS_REPLACE); + } + catch (FileException $e) { + $uri = FALSE; + } $file = $this->entityTypeManager->getStorage('file')->create([ 'uri' => $uri, 'status' => 1, @@ -535,18 +554,4 @@ protected function storeCreatedContentUuids(array $uuids) { $this->state->set('demo_umami_content_uuids', $uuids); } - /** - * Wrapper around file_unmanaged_copy(). - * - * @param string $path - * Path to image. - * - * @return string|false - * The path to the new file, or FALSE in the event of an error. - */ - protected function fileUnmanagedCopy($path) { - $filename = basename($path); - return file_unmanaged_copy($path, 'public://' . $filename, FILE_EXISTS_REPLACE); - } - } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 5dc391e6d5ce22d0e8282c68ca4208685db28035..7ab79e1177e4c5cbb9ebe05fa9e8985222c30347 100644 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -12,6 +12,7 @@ use Drupal\Core\Composer\Composer; use Drupal\Core\Asset\AttachedAssets; use Drupal\Core\Database\Database; +use Drupal\Core\File\Exception\FileException; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Test\TestDatabase; use Drupal\Core\Test\TestRunnerKernel; @@ -962,8 +963,13 @@ 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']); - $messages[] = "- Removed test site directory."; + try { + \Drupal::service('file_system')->deleteRecursive($test_directory, ['Drupal\simpletest\TestBase', 'filePreDeleteCallback']); + $messages[] = "- Removed test site directory."; + } + catch (FileException $e) { + // Ignore failed deletes. + } } // Clear out all database tables from the test. @@ -1565,7 +1571,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/FunctionalTests/FolderTest.php b/core/tests/Drupal/FunctionalTests/FolderTest.php index d78cf5a1a7e702521ebf354bb8404b5ed80b25b4..34cba951a03bb2b9c503425644af571eca446b88 100644 --- a/core/tests/Drupal/FunctionalTests/FolderTest.php +++ b/core/tests/Drupal/FunctionalTests/FolderTest.php @@ -22,7 +22,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/tests/Drupal/KernelTests/Core/File/DirectoryTest.php b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php index 467d9262e169e71e66dc47ce0f5d29b57bc9691d..80ef82c070f392d6efb127f4531d572c619fa15b 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 655bb5f398a901a14c379408d873cd894a08cdee..3c9a2017729a4829b50394e2c7cc7de7189943ec 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->setExpectedException(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->setExpectedException(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 05acac413af46bf5489a7900ce58ccb856bc81a3..1e4bc150d71c07ff1ee4a9f9655f7d2c9543ae88 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 6871f1936b8dd13baa90cc3998d2850a13a71e21..fda21c48f8f8dc1f245707810949ea87b43050e5 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 2e3f50c2f098d409766a25c9de60adb70bd54413..a2592417e50f6ba946c10e20fdbebea5413d3e67 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->setExpectedException(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->setExpectedException(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 86ea0293158cd353d821f0c81c4a4fa86b79e11f..b418112326505e27d1b1655b6955388129450931 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/FileSystemDeprecationTest.php b/core/tests/Drupal/KernelTests/Core/File/FileSystemDeprecationTest.php index bc0be99de09a457e12622159f2566bf89670accc..1e434c424676b7dda2ffcde1ad164d59148c203c 100644 --- a/core/tests/Drupal/KernelTests/Core/File/FileSystemDeprecationTest.php +++ b/core/tests/Drupal/KernelTests/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\FileSystemInterface::getDestinationFilename() instead. 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/KernelTests/Core/File/FileSystemTest.php b/core/tests/Drupal/KernelTests/Core/File/FileSystemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..354a1b55ff73995a02bd5a7df3f72ef8b3df94b9 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/File/FileSystemTest.php @@ -0,0 +1,102 @@ +<?php + +namespace Drupal\KernelTests\Core\File; + +use Drupal\Core\File\Exception\DirectoryNotReadyException; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\Exception\FileExistsException; +use Drupal\Core\File\Exception\FileNotExistsException; +use Drupal\Core\File\FileSystemInterface; +use Drupal\KernelTests\KernelTestBase; + +/** + * @coversDefaultClass \Drupal\Core\File\FileSystem + * @group File + */ +class FileSystemTest extends KernelTestBase { + + /** + * The file handler under test. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystem; + + /** + * {@inheritdoc} + */ + public static $modules = ['system']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->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->setExpectedException(DirectoryNotReadyException::class, "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->setExpectedException(FileExistsException::class, "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->setExpectedException(FileException::class, "'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 6be98df4aa74e0395a266171978502b0cef43f8d..5d1e0ccd1f067ae63d2b6d6ba2b47c5a8a2e93a3 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 f826d4dd294b16791f976383d175c8e32ba914da..b3be963c371af6477f52ec7f340b0951fd4e5fec 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 e41f87e694c64cbf282d31a474fb70045dd39a01..1c2f9c2397894778a8dac10e7a031398273a07cc 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 817da28288e9249c6f29a37aeb310316da2a7bc0..c7878d084c5d067a0aaa194fde2cf47ae6a572d8 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 b13b92cba52e206bc00ca82a9b6c8a4ed4887991..1d64ab857dbeba295601ed576639bb337f72756f 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 dba742df221dbf1bfde4902d1e8bb14988397c88..0496973a7d2931d2653793166c5f4cd10c5c7920 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 4fa3b6779c88666194afe4e351ad06e84b8d32e8..760b2d081d5f6e1e0c07191d55245fed664f9068 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 204eacb4f516fa2cdb535add5c2653d4147744cf..73a2fd400293e4b3287fdbb6b5da7794eb5b9790 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/TestFileCreationTrait.php b/core/tests/Drupal/Tests/TestFileCreationTrait.php index a2f119b65ca4a4b51f7dee2e44edcf1457ec4bd7..ef783db8600183854a8ae0bd840a613f50c82926 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;