diff --git a/core/core.services.yml b/core/core.services.yml index cab2b0a..29a734a 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -191,7 +191,7 @@ services: arguments: [default] file_system: class: Drupal\Core\File\FileSystem - arguments: ['@settings', '@logger.channel.file'] + arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file'] form_builder: class: Drupal\Core\Form\FormBuilder arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@theme.manager', '@?csrf_token'] diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 8ad0fc0..5aa79b8 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -340,16 +340,17 @@ function install_begin_request($class_loader, &$install_state) { $container ->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager') ->addArgument(new Reference('language_manager')); - $container - ->register('file_system', 'Drupal\Core\File\FileSystem') - ->addArgument(Settings::getInstance()) - ->addArgument((new LoggerChannelFactory())->get('file')); // Register the stream wrapper manager. $container ->register('stream_wrapper_manager', 'Drupal\Core\StreamWrapper\StreamWrapperManager') ->addArgument(new Reference('module_handler')) ->addMethodCall('setContainer', array(new Reference('service_container'))); + $container + ->register('file_system', 'Drupal\Core\File\FileSystem') + ->addArgument(new Reference('stream_wrapper_manager')) + ->addArgument(Settings::getInstance()) + ->addArgument((new LoggerChannelFactory())->get('file')); \Drupal::setContainer($container); diff --git a/core/lib/Drupal/Core/File/FileSystem.php b/core/lib/Drupal/Core/File/FileSystem.php index 75e9b57..aedfdf6 100644 --- a/core/lib/Drupal/Core/File/FileSystem.php +++ b/core/lib/Drupal/Core/File/FileSystem.php @@ -2,18 +2,19 @@ /** * @file - * Contains \Drupal\Core\File\File. + * Contains \Drupal\Core\File\FileSystem. */ namespace Drupal\Core\File; use Drupal\Core\Site\Settings; +use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; use Psr\Log\LoggerInterface; /** * Provides helpers to operate on files and stream wrappers. */ -class FileSystem { +class FileSystem implements FileSystemInterface { /** * Default mode for new directories. See self::chmod(). @@ -40,37 +41,30 @@ class FileSystem { protected $logger; /** + * The stream wrapper manager. + * + * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface + */ + protected $streamWrapperManager; + + /** * Constructs a new FileSystem. * + * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager + * The stream wrapper manager. * @param \Drupal\Core\Site\Settings $settings * The site settings. * @param \Psr\Log\LoggerInterface $logger * The file logger channel. */ - public function __construct(Settings $settings, LoggerInterface $logger) { + public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager, Settings $settings, LoggerInterface $logger) { + $this->streamWrapperManager = $stream_wrapper_manager; $this->settings = $settings; $this->logger = $logger; } /** - * Moves an uploaded file to a new location. - * - * PHP's move_uploaded_file() does not properly support streams if - * open_basedir is enabled, so this function fills that gap. - * - * Compatibility: normal paths and stream wrappers. - * - * @param string $filename - * The filename of the uploaded file. - * @param string $uri - * A string containing the destination URI of the file. - * - * @return bool - * TRUE on success, or FALSE on failure. - * - * @see move_uploaded_file() - * @see http://drupal.org/node/515192 - * @ingroup php_wrappers + * {@inheritdoc} */ public function moveUploadedFile($filename, $uri) { $result = @move_uploaded_file($filename, $uri); @@ -90,38 +84,15 @@ public function moveUploadedFile($filename, $uri) { } /** - * Sets the permissions on a file or directory. - * - * This function will use the file_chmod_directory and - * file_chmod_file settings for the default modes for directories - * and uploaded/generated files. By default these will give everyone read - * access so that users accessing the files with a user account without the - * webserver group (e.g. via FTP) can read these files, and give group write - * permissions so webserver group members (e.g. a vhost account) can alter - * files uploaded and owned by the webserver. - * - * PHP's chmod does not support stream wrappers so we use our wrapper - * implementation which interfaces with chmod() by default. Contrib wrappers - * may override this behavior in their implementations as needed. - * - * @param $uri - * A string containing a URI file, or directory path. - * @param $mode - * Integer value for the permissions. Consult PHP chmod() documentation for - * more information. - * - * @return bool - * TRUE for success, FALSE in the event of an error. - * - * @ingroup php_wrappers + * {@inheritdoc} */ public function chmod($uri, $mode = NULL) { if (!isset($mode)) { if (is_dir($uri)) { - $mode = $this->getSetting('file_chmod_directory', static::CHMOD_DIRECTORY); + $mode = $this->settings->get('file_chmod_directory', static::CHMOD_DIRECTORY); } else { - $mode = $this->getSetting('file_chmod_file', static::CHMOD_FILE); + $mode = $this->settings->get('file_chmod_file', static::CHMOD_FILE); } } @@ -134,21 +105,7 @@ public function chmod($uri, $mode = NULL) { } /** - * Deletes a file. - * - * PHP's unlink() is broken on Windows, as it can fail to remove a file when - * it has a read-only flag set. - * - * @param string $uri - * A URI or pathname. - * @param $context - * Refer to http://php.net/manual/ref.stream.php - * - * @return bool - * Boolean TRUE on success, or FALSE on failure. - * - * @see unlink() - * @ingroup php_wrappers + * {@inheritdoc} */ public function unlink($uri, $context = NULL) { $scheme = $this->uriScheme($uri); @@ -164,31 +121,13 @@ public function unlink($uri, $context = NULL) { } /** - * Resolves the absolute filepath of a local URI or filepath. - * - * The use of this method is discouraged, because it does not work for - * remote URIs. Except in rare cases, URIs should not be manually resolved. - * - * Only use this function if you know that the stream wrapper in the URI uses - * the local file system, and you need to pass an absolute path to a function - * that is incompatible with stream URIs. - * - * @param string $uri - * A stream wrapper URI or a filepath, possibly including one or more - * symbolic links. - * - * @return string|false - * The absolute local filepath (with no symbolic links) or FALSE on failure. - * - * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface::realpath() - * @see http://php.net/manual/function.realpath.php - * @ingroup php_wrappers + * {@inheritdoc} */ public function realpath($uri) { // If this URI is a stream, pass it off to the appropriate stream wrapper. // Otherwise, attempt PHP's realpath. This allows use of this method even // for unmanaged files outside of the stream wrapper interface. - if ($wrapper = $this->getStreamWrapperByUri($uri)) { + if ($wrapper = $this->streamWrapperManager->getViaUri($uri)) { return $wrapper->realpath(); } @@ -196,29 +135,13 @@ public function realpath($uri) { } /** - * Gets the name of the directory from a given path. - * - * PHP's dirname() does not properly pass streams, so this function fills that - * gap. It is backwards compatible with normal paths and will use PHP's - * dirname() as a fallback. - * - * Compatibility: normal paths and stream wrappers. - * - * @param string $uri - * A URI or path. - * - * @return string - * A string containing the directory name. - * - * @see dirname() - * @see http://drupal.org/node/515192 - * @ingroup php_wrappers + * {@inheritdoc} */ public function dirname($uri) { $scheme = $this->uriScheme($uri); if ($this->validScheme($scheme)) { - return $this->getStreamWrapperByScheme($scheme)->dirname($uri); + return $this->streamWrapperManager->getViaScheme($scheme)->dirname($uri); } else { return dirname($uri); @@ -226,15 +149,7 @@ public function dirname($uri) { } /** - * Gets the filename from a given path. - * - * PHP's basename() does not properly support streams or filenames beginning - * with a non-US-ASCII character. - * - * @see http://bugs.php.net/bug.php?id=37738 - * @see basename() - * - * @ingroup php_wrappers + * {@inheritdoc} */ public function basename($uri, $suffix = NULL) { $separators = '/'; @@ -255,37 +170,11 @@ public function basename($uri, $suffix = NULL) { } /** - * Creates a directory, optionally creating missing components in the path to - * the directory. - * - * When PHP's mkdir() creates a directory, the requested mode is affected by - * the process's umask. This function overrides the umask and sets the mode - * explicitly for all directory components created. - * - * @param $uri - * A URI or pathname. - * @param $mode - * Mode given to created directories. Defaults to the directory mode - * configured in the Drupal installation. It must have a leading zero. - * @param $recursive - * Create directories recursively, defaults to FALSE. Cannot work with a - * mode which denies writing or execution to the owner of the process. - * @param $context - * Refer to http://php.net/manual/ref.stream.php - * - * @return bool - * Boolean TRUE on success, or FALSE on failure. - * - * @see mkdir() - * @see http://drupal.org/node/515192 - * @ingroup php_wrappers - * - * @todo Update with open_basedir compatible recursion logic from - * \Drupal\Component\PhpStorage\FileStorage::ensureDirectory(). + * {@inheritdoc} */ public function mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) { if (!isset($mode)) { - $mode = $this->getSetting('file_chmod_directory', static::CHMOD_DIRECTORY); + $mode = $this->settings->get('file_chmod_directory', static::CHMOD_DIRECTORY); } // If the URI has a scheme, don't override the umask - schemes can handle @@ -356,21 +245,7 @@ protected function mkdirCall($uri, $mode, $recursive, $context) { } /** - * Removes a directory. - * - * PHP's rmdir() is broken on Windows, as it can fail to remove a directory - * when it has a read-only flag set. - * - * @param $uri - * A URI or pathname. - * @param $context - * Refer to http://php.net/manual/ref.stream.php - * - * @return bool - * Boolean TRUE on success, or FALSE on failure. - * - * @see rmdir() - * @ingroup php_wrappers + * {@inheritdoc} */ public function rmdir($uri, $context = NULL) { $scheme = $this->uriScheme($uri); @@ -386,32 +261,13 @@ public function rmdir($uri, $context = NULL) { } /** - * Creates a file with a unique filename in the specified directory. - * - * PHP's tempnam() does not return a URI like we want. This function will - * return a URI if given a URI, or it will return a filepath if given a - * filepath. - * - * Compatibility: normal paths and stream wrappers. - * - * @param $directory - * The directory where the temporary filename will be created. - * @param $prefix - * The prefix of the generated temporary filename. - * Note: Windows uses only the first three characters of prefix. - * - * @return string|bool - * The new temporary filename, or FALSE on failure. - * - * @see tempnam() - * @see http://drupal.org/node/515192 - * @ingroup php_wrappers + * {@inheritdoc} */ public function tempnam($directory, $prefix) { $scheme = $this->uriScheme($directory); if ($this->validScheme($scheme)) { - $wrapper = $this->getStreamWrapperByScheme($scheme); + $wrapper = $this->streamWrapperManager->getViaScheme($scheme); if ($filename = tempnam($wrapper->getDirectoryPath(), $prefix)) { return $scheme . '://' . static::basename($filename); @@ -427,16 +283,7 @@ public function tempnam($directory, $prefix) { } /** - * Returns the scheme of a URI (e.g. a stream). - * - * @param string $uri - * A stream, referenced as "scheme://target" or "data:target". - * - * @return string|bool - * A string containing the name of the scheme, or FALSE if none. For - * example, the URI "public://example.txt" would return "public". - * - * @see file_uri_target() + * {@inheritdoc} */ public function uriScheme($uri) { if (preg_match('/^([\w\-]+):\/\/|^(data):/', $uri, $matches)) { @@ -448,60 +295,13 @@ public function uriScheme($uri) { } /** - * Checks that the scheme of a stream URI is valid. - * - * Confirms that there is a registered stream handler for the provided scheme - * and that it is callable. This is useful if you want to confirm a valid - * scheme without creating a new instance of the registered handler. - * - * @param $scheme - * A URI scheme, a stream is referenced as "scheme://target". - * - * @return bool - * Returns TRUE if the string is the name of a validated stream, or FALSE if - * the scheme does not have a registered handler. + * {@inheritdoc} */ public function validScheme($scheme) { if (!$scheme) { return FALSE; } - return class_exists($this->getStreamWrapperClass($scheme)); - } - - /** - * Wraps file_stream_wrapper_get_class(). - * - * @codeCoverageIgnore - */ - protected function getStreamWrapperClass($scheme) { - return file_stream_wrapper_get_class($scheme); - } - - /** - * Wraps file_stream_wrapper_get_instance_by_scheme(). - * - * @codeCoverageIgnore - */ - protected function getStreamWrapperByScheme($scheme) { - return file_stream_wrapper_get_instance_by_scheme($scheme); - } - - /** - * Wraps file_stream_wrapper_get_instance_by_uri(). - * - * @codeCoverageIgnore - */ - protected function getStreamWrapperByUri($uri) { - return file_stream_wrapper_get_instance_by_uri($uri); - } - - /** - * Wraps the global Settings singleton. - * - * @codeCoverageIgnore - */ - protected function getSetting($name, $default = NULL) { - return $this->settings->get($name, $default); + return class_exists($this->streamWrapperManager->getClass($scheme)); } } diff --git a/core/lib/Drupal/Core/File/FileSystemInterface.php b/core/lib/Drupal/Core/File/FileSystemInterface.php new file mode 100644 index 0000000..5700903 --- /dev/null +++ b/core/lib/Drupal/Core/File/FileSystemInterface.php @@ -0,0 +1,245 @@ +wrappers[$filter])) { @@ -120,20 +69,7 @@ public function getWrappers($filter = StreamWrapperInterface::ALL) { } /** - * Returns registered stream wrapper names. - * - * @param int $filter - * (Optional) Filters out all types except those with an on bit for each on - * bit in $filter. For example, if $filter is - * StreamWrapperInterface::WRITE_VISIBLE, which is equal to - * (StreamWrapperInterface::READ | StreamWrapperInterface::WRITE | - * StreamWrapperInterface::VISIBLE), then only stream wrappers with all - * three of these bits set are returned. Defaults to - * StreamWrapperInterface::ALL, which returns all registered stream - * wrappers. - * - * @return array - * Stream wrapper names, keyed by scheme. + * {@inheritdoc} */ public function getNames($filter = StreamWrapperInterface::ALL) { $names = array(); @@ -145,20 +81,7 @@ public function getNames($filter = StreamWrapperInterface::ALL) { } /** - * Returns registered stream wrapper descriptions. - * - * @param int $filter - * (Optional) Filters out all types except those with an on bit for each on - * bit in $filter. For example, if $filter is - * StreamWrapperInterface::WRITE_VISIBLE, which is equal to - * (StreamWrapperInterface::READ | StreamWrapperInterface::WRITE | - * StreamWrapperInterface::VISIBLE), then only stream wrappers with all - * three of these bits set are returned. Defaults to - * StreamWrapperInterface::ALL, which returns all registered stream - * wrappers. - * - * @return array - * Stream wrapper descriptions, keyed by scheme. + * {@inheritdoc} */ public function getDescriptions($filter = StreamWrapperInterface::ALL) { $descriptions = array(); @@ -170,26 +93,14 @@ public function getDescriptions($filter = StreamWrapperInterface::ALL) { } /** - * Returns a stream wrapper via scheme. - * - * @param string $scheme - * The scheme of the stream wrapper. - * - * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool - * A stream wrapper object, or false if the scheme is not available. + * {@inheritdoc} */ public function getViaScheme($scheme) { return $this->getWrapper($scheme, $scheme . '://'); } /** - * Returns a stream wrapper via URI. - * - * @param string $uri - * The URI of the stream wrapper. - * - * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool - * A stream wrapper object, or false if the scheme is not available. + * {@inheritdoc} */ public function getViaUri($uri) { $scheme = file_uri_scheme($uri); @@ -197,13 +108,7 @@ public function getViaUri($uri) { } /** - * Returns the stream wrapper class. - * - * @param string $scheme - * The stream wrapper scheme. - * - * @return string|bool - * The stream wrapper class, or false if the scheme does not exist. + * {@inheritdoc} */ public function getClass($scheme) { if (isset($this->info[$scheme])) { @@ -283,14 +188,7 @@ public function unregister() { } /** - * Registers stream wrapper with PHP. - * - * @param string $scheme - * The scheme of the stream wrapper. - * @param string $class - * The class of the stream wrapper. - * @param int $type - * The type of the stream wrapper. + * {@inheritdoc} */ public function registerWrapper($scheme, $class, $type) { if (in_array($scheme, stream_get_wrappers(), TRUE)) { diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManagerInterface.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManagerInterface.php new file mode 100644 index 0000000..34e5f81 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManagerInterface.php @@ -0,0 +1,159 @@ +dateFormatter = $date_formatter; $this->streamWrapperManager = $stream_wrapper_manager; diff --git a/core/tests/Drupal/Tests/Core/File/FileSystemTest.php b/core/tests/Drupal/Tests/Core/File/FileSystemTest.php index da854f7..ba39d17 100644 --- a/core/tests/Drupal/Tests/Core/File/FileSystemTest.php +++ b/core/tests/Drupal/Tests/Core/File/FileSystemTest.php @@ -38,8 +38,9 @@ protected function setUp() { parent::setUp(); $settings = new Settings([]); + $stream_wrapper_manager = $this->getMock('Drupal\Core\StreamWrapper\StreamWrapperManagerInterface'); $this->logger = $this->getMock('Psr\Log\LoggerInterface'); - $this->fileSystem = new FileSystem($settings, $this->logger); + $this->fileSystem = new FileSystem($stream_wrapper_manager, $settings, $this->logger); } /**