diff --git a/core/core.services.yml b/core/core.services.yml index 562d64f..5e2ed2b 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -724,6 +724,23 @@ services: plugin.manager.condition: class: Drupal\Core\Condition\ConditionManager parent: default_plugin_manager + stream_wrapper_manager: + class: Drupal\Core\StreamWrapper\StreamWrapperManager + arguments: ['@module_handler'] + calls: + - [setContainer, ['@service_container']] + stream_wrapper.public: + class: Drupal\Core\StreamWrapper\PublicStream + tags: + - { name: stream_wrapper, scheme: public } + stream_wrapper.private: + class: Drupal\Core\StreamWrapper\PrivateStream + tags: + - { name: stream_wrapper, scheme: private } + stream_wrapper.temporary: + class: Drupal\Core\StreamWrapper\TemporaryStream + tags: + - { name: stream_wrapper, scheme: temporary } kernel_destruct_subscriber: class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber tags: diff --git a/core/includes/file.inc b/core/includes/file.inc index 6ffb269..d7e0541 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -11,39 +11,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PublicStream; - -/** - * Stream wrapper bit flags that are the basis for composite types. - * - * Note that 0x0002 is skipped, because it was the value of a constant that has - * since been removed. - */ - -/** - * Stream wrapper bit flag -- a filter that matches all wrappers. - */ -const STREAM_WRAPPERS_ALL = 0x0000; - -/** - * Stream wrapper bit flag -- refers to a local file system location. - */ -const STREAM_WRAPPERS_LOCAL = 0x0001; - -/** - * Stream wrapper bit flag -- wrapper is readable (almost always true). - */ -const STREAM_WRAPPERS_READ = 0x0004; - -/** - * Stream wrapper bit flag -- wrapper is writeable. - */ -const STREAM_WRAPPERS_WRITE = 0x0008; - -/** - * Stream wrapper bit flag -- exposed in the UI and potentially web accessible. - */ -const STREAM_WRAPPERS_VISIBLE = 0x0010; - +use Drupal\Core\StreamWrapper\StreamWrapperInterface; /** * Default mode for new directories. See drupal_chmod(). @@ -56,45 +24,6 @@ const FILE_CHMOD_FILE = 0664; /** - * Composite stream wrapper bit flags that are usually used as the types. - */ - -/** - * Stream wrapper type flag -- not visible in the UI or accessible via web, - * but readable and writable. E.g. the temporary directory for uploads. - */ -define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE); - -/** - * Stream wrapper type flag -- hidden, readable and writeable using local files. - */ -define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN); - -/** - * Stream wrapper type flag -- visible, readable and writeable. - */ -define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | STREAM_WRAPPERS_VISIBLE); - -/** - * Stream wrapper type flag -- visible and read-only. - */ -define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE); - -/** - * Stream wrapper type flag -- the default when 'type' is omitted from - * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL, - * because PHP grants a greater trust level to local files (for example, they - * can be used in an "include" statement, regardless of the "allow_url_include" - * setting), so stream wrappers need to explicitly opt-in to this. - */ -define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE); - -/** - * Stream wrapper type flag -- visible, readable and writeable using local files. - */ -define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL); - -/** * @defgroup file File interface * @{ * Common file handling functions. @@ -155,122 +84,46 @@ /** * Provides Drupal stream wrapper registry. * - * A stream wrapper is an abstraction of a file system that allows Drupal to - * use the same set of methods to access both local files and remote resources. - * - * Provide a facility for managing and querying user-defined stream wrappers - * in PHP. PHP's internal stream_get_wrappers() doesn't return the class - * registered to handle a stream, which we need to be able to find the handler - * for class instantiation. - * - * If a module registers a scheme that is already registered with PHP, the - * existing scheme will be unregistered and replaced with the specified class. - * - * A stream is referenced as "scheme://target". - * - * The optional $filter parameter can be used to retrieve only the stream - * wrappers that are appropriate for particular usage. For example, this returns - * only stream wrappers that use local file storage: - * @code - * $local_stream_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL); - * @endcode - * - * The $filter parameter can only filter to types containing a particular flag. - * In some cases, you may want to filter to types that do not contain a - * particular flag. For example, you may want to retrieve all stream wrappers - * that are not writable, or all stream wrappers that are not local. PHP's - * array_diff_key() function can be used to help with this. For example, this - * returns only stream wrappers that do not use local file storage: - * @code - * $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL)); - * @endcode - * - * @param $filter + * @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 STREAM_WRAPPERS_WRITE_VISIBLE, - * which is equal to (STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | - * STREAM_WRAPPERS_VISIBLE), then only stream wrappers with all three of these - * bits set are returned. Defaults to STREAM_WRAPPERS_ALL, which returns all - * registered stream wrappers. + * 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 + * @return array * An array keyed by scheme, with values containing an array of information * about the stream wrapper, as returned by hook_stream_wrappers(). If $filter - * is omitted or set to STREAM_WRAPPERS_ALL, the entire Drupal stream wrapper - * registry is returned. Otherwise only the stream wrappers whose 'type' - * bitmask has an on bit for each bit specified in $filter are returned. + * is omitted or set to StreamWrapperInterface::ALL, the entire Drupal stream + * wrapper registry is returned. Otherwise only the stream wrappers whose + * 'type' bitmask has an on bit for each bit specified in $filter are + * returned. * - * @see hook_stream_wrappers() * @see hook_stream_wrappers_alter() + * + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal::service('stream_wrapper_manager')->getWrappers(). */ -function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) { - $wrappers_storage = &drupal_static(__FUNCTION__, array()); - - if (empty($wrappers_storage)) { - // Initialize $wrappers_storage, so that we are not calling this method - // repeatedly if no stream wrappers exist. - $wrappers_storage[STREAM_WRAPPERS_ALL] = array(); - $wrappers = array(); - if (\Drupal::hasService('module_handler')) { - $wrappers = \Drupal::moduleHandler()->invokeAll('stream_wrappers'); - foreach ($wrappers as $scheme => $info) { - // Add defaults. - $wrappers[$scheme] += array('type' => STREAM_WRAPPERS_NORMAL); - } - \Drupal::moduleHandler()->alter('stream_wrappers', $wrappers); - } - $existing = stream_get_wrappers(); - foreach ($wrappers as $scheme => $info) { - // We only register classes that implement our interface. - if (in_array('Drupal\Core\StreamWrapper\StreamWrapperInterface', class_implements($info['class']), TRUE)) { - // Record whether we are overriding an existing scheme. - if (in_array($scheme, $existing, TRUE)) { - $wrappers[$scheme]['override'] = TRUE; - stream_wrapper_unregister($scheme); - } - else { - $wrappers[$scheme]['override'] = FALSE; - } - if (($info['type'] & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) { - stream_wrapper_register($scheme, $info['class']); - } - else { - stream_wrapper_register($scheme, $info['class'], STREAM_IS_URL); - } - } - // Pre-populate the static cache with the filters most typically used. - $wrappers_storage[STREAM_WRAPPERS_ALL][$scheme] = $wrappers[$scheme]; - if (($info['type'] & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) { - $wrappers_storage[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $wrappers[$scheme]; - } - } - } - - if (!isset($wrappers_storage[$filter])) { - $wrappers_storage[$filter] = array(); - foreach ($wrappers_storage[STREAM_WRAPPERS_ALL] as $scheme => $info) { - // Bit-wise filter. - if (($info['type'] & $filter) == $filter) { - $wrappers_storage[$filter][$scheme] = $info; - } - } - } - - return $wrappers_storage[$filter]; +function file_get_stream_wrappers($filter = StreamWrapperInterface::ALL) { + return \Drupal::service('stream_wrapper_manager')->getWrappers($filter); } /** * Returns the stream wrapper class name for a given scheme. * - * @param $scheme + * @param string $scheme * Stream scheme. * - * @return + * @return string|bool * Return string if a scheme has a registered handler, or FALSE. + * + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal::service('stream_wrapper_manager')->getClass(). */ function file_stream_wrapper_get_class($scheme) { - $wrappers = file_get_stream_wrappers(); - return empty($wrappers[$scheme]) ? FALSE : $wrappers[$scheme]['class']; + return \Drupal::service('stream_wrapper_manager')->getClass($scheme); } /** @@ -297,10 +150,10 @@ function file_uri_scheme($uri) { * 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 + * @param string $scheme * A URI scheme, a stream is referenced as "scheme://target". * - * @return + * @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. */ @@ -312,10 +165,10 @@ function file_stream_wrapper_valid_scheme($scheme) { /** * Returns the part of a URI after the schema. * - * @param $uri + * @param string $uri * A stream, referenced as "scheme://target". * - * @return + * @return string|bool * A string containing the target (path), or FALSE if none. * For example, the URI "public://sample/test.txt" would return * "sample/test.txt". @@ -332,7 +185,7 @@ function file_uri_target($uri) { /** * Gets the default file stream implementation. * - * @return + * @return string * 'public', 'private' or any other file scheme defined as the default. */ function file_default_scheme() { @@ -348,10 +201,10 @@ function file_default_scheme() { * - Remove trailing slashes from target * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://". * - * @param $uri + * @param string $uri * String reference containing the URI to normalize. * - * @return + * @return string * The normalized URI. */ function file_stream_wrapper_uri_normalize($uri) { @@ -374,25 +227,20 @@ function file_stream_wrapper_uri_normalize($uri) { * The scheme determines the stream wrapper class that should be * used by consulting the stream wrapper registry. * - * @param $uri + * @param string $uri * A stream, referenced as "scheme://target". * - * @return + * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool * Returns a new stream wrapper object appropriate for the given URI or FALSE * if no registered handler could be found. For example, a URI of * "private://example.txt" would return a new private stream wrapper object * (Drupal\Core\StreamWrapper\PrivateStream). + * + * * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal::service('stream_wrapper_manager')->getViaUri(). */ function file_stream_wrapper_get_instance_by_uri($uri) { - if ($scheme = file_uri_scheme($uri)) { - $class = file_stream_wrapper_get_class($scheme); - if (class_exists($class)) { - $instance = new $class(); - $instance->setUri($uri); - return $instance; - } - } - return FALSE; + return \Drupal::service('stream_wrapper_manager')->getViaUri($uri); } /** @@ -406,25 +254,20 @@ function file_stream_wrapper_get_instance_by_uri($uri) { * Note: the instance URI will be initialized to "scheme://" so that you can * make the customary method calls as if you had retrieved an instance by URI. * - * @param $scheme + * @param string $scheme * If the stream was "public://target", "public" would be the scheme. * - * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface + * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool * Returns a new stream wrapper object appropriate for the given $scheme. * For example, for the public scheme a stream wrapper object * (Drupal\Core\StreamWrapper\PublicStream). * FALSE is returned if no registered handler could be found. + * + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal::service('stream_wrapper_manager')->getViaScheme(). */ function file_stream_wrapper_get_instance_by_scheme($scheme) { - $class = file_stream_wrapper_get_class($scheme); - if (class_exists($class)) { - $instance = new $class(); - $instance->setUri($scheme . '://'); - return $instance; - } - else { - return FALSE; - } + return \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme); } /** diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 214ce28..0c3adfb 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -323,6 +323,12 @@ function install_begin_request(&$install_state) { ->register('path.matcher', 'Drupal\Core\Path\PathMatcher') ->addArgument(new Reference('config.factory')); + // 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'))); + \Drupal::setContainer($container); // Determine whether base system services are ready to operate. diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 42c7dea..2e67de3 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -10,6 +10,7 @@ use Drupal\Core\Cache\CacheContextsPass; use Drupal\Core\Cache\ListCacheBinsPass; use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; @@ -54,6 +55,7 @@ public function register(ContainerBuilder $container) { // Collect tagged handler services as method calls on consumer services. $container->addCompilerPass(new TaggedHandlersPass()); + $container->addCompilerPass(new RegisterStreamWrappersPass()); // Add a compiler pass for registering event subscribers. $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php new file mode 100644 index 0000000..01193d3 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php @@ -0,0 +1,38 @@ +hasDefinition('stream_wrapper_manager')) { + return; + } + + $stream_wrapper_manager = $container->getDefinition('stream_wrapper_manager'); + + foreach ($container->findTaggedServiceIds('stream_wrapper') as $id => $attributes) { + $class = $container->getDefinition($id)->getClass(); + $scheme = $attributes[0]['scheme']; + + $stream_wrapper_manager->addMethodCall('addStreamWrapper', array($id, $class, $scheme)); + } + } + +} diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index e54cf89..8f90918 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -381,6 +381,7 @@ public function boot() { $seed = unpack("L", Crypt::randomBytes(4)); mt_srand($seed[1]); + $this->container->get('stream_wrapper_manager')->register(); $this->booted = TRUE; return $this; @@ -393,6 +394,7 @@ public function shutdown() { if (FALSE === $this->booted) { return; } + $this->container->get('stream_wrapper_manager')->unregister(); $this->booted = FALSE; $this->container = NULL; $this->moduleList = NULL; diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 937953f..0111747 100644 --- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php @@ -43,6 +43,13 @@ protected $uri; /** + * {@inheritdoc} + */ + public static function getType() { + return StreamWrapperInterface::NORMAL; + } + + /** * Gets the path that the wrapper is responsible for. * * @todo Review this method name in D8 per http://drupal.org/node/701358. diff --git a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php index 39b3f28..6bcd56b 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php @@ -16,6 +16,27 @@ class PrivateStream extends LocalStream { /** + * {@inheritdoc} + */ + public static function getType() { + return StreamWrapperInterface::LOCAL_NORMAL; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Private files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Private local files served by Drupal.'); + } + + /** * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() */ public function getDirectoryPath() { diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 637c9ef..e2b78ec 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -21,6 +21,27 @@ class PublicStream extends LocalStream { /** * {@inheritdoc} */ + public static function getType() { + return StreamWrapperInterface::LOCAL_NORMAL; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Public files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Public local files served by the webserver.'); + } + + /** + * {@inheritdoc} + */ public function getDirectoryPath() { return static::basePath(); } diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php index 3f9f5d4..ac95452 100644 --- a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php @@ -30,6 +30,100 @@ interface StreamWrapperInterface extends PhpStreamWrapperInterface { /** + * Stream wrapper bit flags that are the basis for composite types. + * + * Note that 0x0002 is skipped, because it was the value of a constant that + * has since been removed. + */ + + /** + * A filter that matches all wrappers. + */ + const ALL = 0x0000; + + /** + * Refers to a local file system location. + */ + const LOCAL = 0x0001; + + /** + * Wrapper is readable (almost always true). + */ + const READ = 0x0004; + + /** + * Wrapper is writeable. + */ + const WRITE = 0x0008; + + /** + * Exposed in the UI and potentially web accessible. + */ + const VISIBLE = 0x0010; + + /** + * Composite stream wrapper bit flags that are usually used as the types. + */ + + /** + * Not visible in the UI or accessible via web, but readable and writable. + * E.g. the temporary directory for uploads. + */ + const HIDDEN = 0x000C; + + /** + * Hidden, readable and writeable using local files. + */ + const LOCAL_HIDDEN = 0x000D; + + /** + * Visible, readable and writeable. + */ + const WRITE_VISIBLE = 0x001C; + + /** + * Visible and read-only. + */ + const READ_VISIBLE = 0x0014; + + /** + * This is the default 'type' falg. This does not include + * StreamWrapperInterface::LOCAL, because PHP grants a greater trust level to + * local files (for example, they can be used in an "include" statement, + * regardless of the "allow_url_include" setting), so stream wrappers need to + * explicitly opt-in to this. + */ + const NORMAL = 0x001C; + + /** + * Visible, readable and writeable using local files. + */ + const LOCAL_NORMAL = 0x001D; + + /** + * Returns the type of stream wrapper. + * + * @return int + */ + public static function getType(); + + /** + * Returns the name of the stream wrapper for use in the UI. + * + * @return string + * The stream wrapper name. + */ + public function getName(); + + /** + * Returns the description of the stream wrapper for use in the UI. + * + * @return string + * The stream wrapper description. + */ + public function getDescription(); + + /** * Sets the absolute stream resource URI. * * This allows you to set the URI. Generally is only called by the factory diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php new file mode 100644 index 0000000..ddd4799 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -0,0 +1,317 @@ +moduleHandler = $module_handler; + } + + /** + * Provides Drupal stream wrapper registry. + * + * A stream wrapper is an abstraction of a file system that allows Drupal to + * use the same set of methods to access both local files and remote + * resources. + * + * Provide a facility for managing and querying user-defined stream wrappers + * in PHP. PHP's internal stream_get_wrappers() doesn't return the class + * registered to handle a stream, which we need to be able to find the handler + * for class instantiation. + * + * If a module registers a scheme that is already registered with PHP, the + * existing scheme will be unregistered and replaced with the specified class. + * + * A stream is referenced as "scheme://target". + * + * The optional $filter parameter can be used to retrieve only the stream + * wrappers that are appropriate for particular usage. For example, this + * returns only stream wrappers that use local file storage: + * + * @code + * $local_stream_wrappers = file_get_stream_wrappers(StreamWrapperInterface::LOCAL); + * @endcode + * + * The $filter parameter can only filter to types containing a particular + * flag. In some cases, you may want to filter to types that do not contain a + * particular flag. For example, you may want to retrieve all stream wrappers + * that are not writable, or all stream wrappers that are not local. PHP's + * array_diff_key() function can be used to help with this. For example, this + * returns only stream wrappers that do not use local file storage: + * @code + * $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(StreamWrapperInterface::ALL), file_get_stream_wrappers(StreamWrapperInterface::LOCAL)); + * @endcode + * + * @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 + * An array keyed by scheme, with values containing an array of information + * about the stream wrapper, as returned by hook_stream_wrappers(). If + * $filter is omitted or set to StreamWrapperInterface::ALL, the entire + * Drupal stream wrapper registry is returned. Otherwise only the stream + * wrappers whose 'type' bitmask has an on bit for each bit specified in + * $filter are returned. + */ + public function getWrappers($filter = StreamWrapperInterface::ALL) { + if (!isset($this->wrappers[$filter])) { + $this->wrappers[$filter] = array(); + foreach ($this->wrappers[StreamWrapperInterface::ALL] as $scheme => $info) { + // Bit-wise filter. + if (($info['type'] & $filter) == $filter) { + $this->wrappers[$filter][$scheme] = $info; + } + } + } + + return $this->wrappers[$filter]; + } + + /** + * 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. + */ + public function getNames($filter = StreamWrapperInterface::ALL) { + $names = array(); + foreach (array_keys($this->getWrappers($filter)) as $scheme) { + $names[$scheme] = $this->getViaScheme($scheme)->getName(); + } + + return $names; + } + + /** + * 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. + */ + public function getDescriptions($filter = StreamWrapperInterface::ALL) { + $descriptions = array(); + foreach (array_keys($this->getWrappers($filter)) as $scheme) { + $descriptions[$scheme] = $this->getViaScheme($scheme)->getDescription(); + } + + return $descriptions; + } + + /** + * 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. + */ + 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. + */ + public function getViaUri($uri) { + $scheme = file_uri_scheme($uri); + return $this->getWrapper($scheme, $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. + */ + public function getClass($scheme) { + if (isset($this->info[$scheme])) { + return $this->info[$scheme]['class']; + } + + return FALSE; + } + + /** + * Returns a stream wrapper instance. + * + * @param string $scheme + * The scheme of the desired stream wrapper. + * @param string $uri + * The URI of the stream. + * + * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool + * A stream wrapper object, or false if the scheme is not available. + */ + protected function getWrapper($scheme, $uri) { + if (isset($this->services[$scheme])) { + $instance = $this->container->get($this->services[$scheme]); + $instance->setUri($uri); + return $instance; + } + + return FALSE; + } + + /** + * Adds a stream wrapper. + * + * Internal use only. + * + * @param string $service_id + * The service id. + * @param string $class + * The stream wrapper class. + */ + public function addStreamWrapper($service_id, $class, $scheme) { + $info = array(); + $info['class'] = $class; + $info['type'] = $class::getType(); + $this->services[$scheme] = $service_id; + $this->info[$scheme] = $info; + } + + /** + * Registers the tagged stream wrappers. + * + * Internal use only. + */ + public function register() { + $this->moduleHandler->alter('stream_wrappers', $this->info); + $this->services = array_intersect_key($this->services, $this->info); + + foreach ($this->info as $scheme => $info) { + $this->registerWrapper($scheme, $info['class'], $info['type']); + } + } + + /** + * Unregisters the tagged stream wrappers. + * + * Internal use only. + */ + public function unregister() { + foreach (array_keys($this->wrappers[StreamWrapperInterface::ALL]) as $scheme) { + stream_wrapper_unregister($scheme); + } + } + + /** + * 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. + */ + public function registerWrapper($scheme, $class, $type) { + if (in_array($scheme, stream_get_wrappers(), TRUE)) { + stream_wrapper_unregister($scheme); + } + + if (($type & StreamWrapperInterface::LOCAL) == StreamWrapperInterface::LOCAL) { + stream_wrapper_register($scheme, $class); + } + else { + stream_wrapper_register($scheme, $class, STREAM_IS_URL); + } + + // Pre-populate the static cache with the filters most typically used. + $info = array('type' => $type, 'class' => $class); + $this->wrappers[StreamWrapperInterface::ALL][$scheme] = $info; + + if (($type & StreamWrapperInterface::WRITE_VISIBLE) == StreamWrapperInterface::WRITE_VISIBLE) { + $this->wrappers[StreamWrapperInterface::WRITE_VISIBLE][$scheme] = $info; + } + } + +} diff --git a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php index 42dd4fd..0ea466c 100644 --- a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php @@ -16,6 +16,27 @@ class TemporaryStream extends LocalStream { /** + * {@inheritdoc} + */ + public static function getType() { + return StreamWrapperInterface::LOCAL_HIDDEN; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Temporary files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Temporary local files for upload and previews.'); + } + + /** * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() */ public function getDirectoryPath() { diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc index 69acabe..4d7ec4d 100644 --- a/core/modules/editor/editor.admin.inc +++ b/core/modules/editor/editor.admin.inc @@ -5,8 +5,9 @@ * Administration functions for editor.module. */ -use Drupal\editor\Entity\Editor; use Drupal\Component\Utility\NestedArray; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Drupal\editor\Entity\Editor; /** * Subform constructor to configure the text editor's image upload settings. @@ -51,10 +52,7 @@ function editor_image_upload_settings_form(Editor $editor) { // Any visible, writable wrapper can potentially be used for uploads, // including a remote file system that integrates with a CDN. - $stream_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE); - foreach ($stream_wrappers as $scheme => $info) { - $options[$scheme] = $info['description']; - } + $options = \Drupal::service('stream_wrapper_manager')->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE); if (!empty($options)) { $form['scheme'] = array( '#type' => 'radios', @@ -68,8 +66,8 @@ function editor_image_upload_settings_form(Editor $editor) { // Set data- attributes with human-readable names for all possible stream // wrappers, so that drupal.ckeditor.drupalimage.admin's summary rendering // can use that. - foreach ($stream_wrappers as $scheme => $info) { - $form['scheme'][$scheme]['#attributes']['data-label'] = t('Storage: @name', array('@name' => $info['name'])); + foreach (\Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE) as $scheme => $name) { + $form['scheme'][$scheme]['#attributes']['data-label'] = t('Storage: @name', array('@name' => $name)); } $form['directory'] = array( diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php index a56448a..ac6cb8a 100644 --- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php +++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Bytes; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; /** @@ -129,10 +130,7 @@ public function settingsForm(array &$form, array &$form_state, $has_data) { ), ); - $scheme_options = array(); - foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) { - $scheme_options[$scheme] = $stream_wrapper['name']; - } + $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE); $element['uri_scheme'] = array( '#type' => 'radios', '#title' => t('Upload destination'), diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index b1bdeb3..6ee2ecc 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -14,29 +14,6 @@ const FILE_URL_TEST_CDN_2 = 'http://cdn2.example.com'; /** - * Implements hook_stream_wrappers(). - */ -function file_test_stream_wrappers() { - return array( - 'dummy' => array( - 'name' => t('Dummy files'), - 'class' => 'Drupal\file_test\DummyStreamWrapper', - 'description' => t('Dummy wrapper for simpletest.'), - ), - 'dummy-remote' => array( - 'name' => t('Dummy files (remote)'), - 'class' => 'Drupal\file_test\DummyRemoteStreamWrapper', - 'description' => t('Dummy wrapper for simpletest (remote).'), - ), - 'dummy-readonly' => array( - 'name' => t('Dummy files (readonly)'), - 'class' => 'Drupal\file_test\DummyReadOnlyStreamWrapper', - 'description' => t('Dummy wrapper for simpletest (readonly).'), - ), - ); -} - -/** * Reset/initialize the history of calls to the file_* hooks. * * @see file_test_get_calls() diff --git a/core/modules/file/tests/file_test/file_test.services.yml b/core/modules/file/tests/file_test/file_test.services.yml new file mode 100644 index 0000000..1e05c26 --- /dev/null +++ b/core/modules/file/tests/file_test/file_test.services.yml @@ -0,0 +1,13 @@ +services: + stream_wrapper.dummy_readonly: + class: Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper + tags: + - { name: stream_wrapper, scheme: dummy-readonly } + stream_wrapper.dummy_remote: + class: Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper + tags: + - { name: stream_wrapper, scheme: dummy-remote } + stream_wrapper.dummy: + class: Drupal\file_test\StreamWrapper\DummyStreamWrapper + tags: + - { name: stream_wrapper, scheme: dummy } diff --git a/core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php new file mode 100644 index 0000000..99cf3e8 --- /dev/null +++ b/core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php @@ -0,0 +1,54 @@ +getWrappers(StreamWrapperInterface::WRITE_VISIBLE); foreach ($wrappers as $wrapper => $wrapper_data) { if (file_exists($directory = $wrapper . '://styles/' . $this->id())) { file_unmanaged_delete_recursive($directory); diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php index c2e8737..f9e3475 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -8,6 +8,7 @@ namespace Drupal\image\Plugin\Field\FieldType; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; use Drupal\file\Plugin\Field\FieldType\FileItem; @@ -160,10 +161,7 @@ public function settingsForm(array &$form, array &$form_state, $has_data) { // the field. $settings = $this->getFieldDefinition()->getFieldStorageDefinition()->getSettings(); - $scheme_options = array(); - foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) { - $scheme_options[$scheme] = $stream_wrapper['name']; - } + $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE); $element['uri_scheme'] = array( '#type' => 'radios', '#title' => t('Upload destination'), diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 0728d07..80039f2 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -207,21 +207,6 @@ function locale_theme() { } /** - * Implements hook_stream_wrappers(). - */ -function locale_stream_wrappers() { - $wrappers = array( - 'translations' => array( - 'name' => new TranslationWrapper('Translation files'), - 'class' => 'Drupal\locale\TranslationsStream', - 'description' => new TranslationWrapper('Translation files'), - 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, - ), - ); - return $wrappers; -} - -/** * Implements hook_ENTITY_TYPE_insert() for 'language_entity'. */ function locale_language_entity_insert(LanguageEntity $language) { diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index 7a0ced3..1b840b9 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -11,3 +11,7 @@ services: tags: - { name: string_translator } - { name: needs_destruction } + stream_wrapper.translations: + class: Drupal\locale\StreamWrapper\TranslationsStream + tags: + - { name: stream_wrapper, scheme: translations } diff --git a/core/modules/locale/src/StreamWrapper/TranslationsStream.php b/core/modules/locale/src/StreamWrapper/TranslationsStream.php new file mode 100644 index 0000000..44c8957 --- /dev/null +++ b/core/modules/locale/src/StreamWrapper/TranslationsStream.php @@ -0,0 +1,57 @@ +get('translation.path'); + } + + /** + * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl(). + * @throws \LogicException PO files URL should not be public. + */ + function getExternalUrl() { + throw new \LogicException('PO files URL should not be public.'); + } +} diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 616d064..079fea0 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -15,6 +15,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Site\Settings; use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; @@ -77,13 +78,11 @@ protected $keyValueFactory; /** - * A list of stream wrappers that have been registered for this test. - * - * @see \Drupal\simpletest\KernelTestBase::registerStreamWrapper() + * Array of registered stream wrappers. * * @var array */ - private $streamWrappers = array(); + protected $streamWrappers = array(); /** * {@inheritdoc} @@ -299,6 +298,12 @@ public function containerBuild(ContainerBuilder $container) { $container->getDefinition('password')->setArguments(array(1)); } + // 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'))); + $request = Request::create('/'); $container->get('request_stack')->push($request); } @@ -486,53 +491,10 @@ protected function disableModules(array $modules) { * The fully qualified class name to register. * @param int $type * The Drupal Stream Wrapper API type. Defaults to - * STREAM_WRAPPERS_LOCAL_NORMAL. + * StreamWrapperInterface::NORMAL. */ - protected function registerStreamWrapper($scheme, $class, $type = STREAM_WRAPPERS_LOCAL_NORMAL) { - if (isset($this->streamWrappers[$scheme])) { - $this->unregisterStreamWrapper($scheme, $this->streamWrappers[$scheme]); - } - $this->streamWrappers[$scheme] = $type; - if (($type & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) { - stream_wrapper_register($scheme, $class); - } - else { - stream_wrapper_register($scheme, $class, STREAM_IS_URL); - } - // @todo Revamp Drupal's stream wrapper API for D8. - // @see https://drupal.org/node/2028109 - $wrappers = &drupal_static('file_get_stream_wrappers', array()); - $wrappers[STREAM_WRAPPERS_ALL][$scheme] = array( - 'type' => $type, - 'class' => $class, - ); - if (($type & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) { - $wrappers[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $wrappers[STREAM_WRAPPERS_ALL][$scheme]; - } - } - - /** - * Unregisters a stream wrapper previously registered by this test. - * - * KernelTestBase::tearDown() automatically cleans up all registered - * stream wrappers, so this usually does not have to be called manually. - * - * @param string $scheme - * The scheme to unregister. - * @param int $type - * The Drupal Stream Wrapper API type of the scheme to unregister. - */ - protected function unregisterStreamWrapper($scheme, $type) { - stream_wrapper_unregister($scheme); - unset($this->streamWrappers[$scheme]); - // @todo Revamp Drupal's stream wrapper API for D8. - // @see https://drupal.org/node/2028109 - $wrappers = &drupal_static('file_get_stream_wrappers', array()); - foreach ($wrappers as $filter => $schemes) { - if (is_int($filter) && (($filter & $type) == $filter)) { - unset($wrappers[$filter][$scheme]); - } - } + protected function registerStreamWrapper($scheme, $class, $type = StreamWrapperInterface::NORMAL) { + $this->container->get('stream_wrapper_manager')->registerWrapper($scheme, $class, $type); } /** diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index e6fe068..200ede0 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -1064,10 +1064,7 @@ private function prepareEnvironment() { // - WebTestBase re-initializes Drupal stream wrappers after installation. // The original stream wrappers are restored after the test run. // @see TestBase::restoreEnvironment() - $wrappers = file_get_stream_wrappers(); - foreach ($wrappers as $scheme => $info) { - stream_wrapper_unregister($scheme); - } + $this->originalContainer->get('stream_wrapper_manager')->unregister(); // Reset statics. drupal_static_reset(); @@ -1203,6 +1200,12 @@ private function restoreEnvironment() { \Drupal::setContainer($this->originalContainer); $GLOBALS['config_directories'] = $this->originalConfigDirectories; + // Re-initialize original stream wrappers of the parent site. + // This must happen after static variables have been reset and the original + // container and $config_directories are restored, as simpletest_log_read() + // uses the public stream wrapper to locate the error.log. + $this->originalContainer->get('stream_wrapper_manager')->register(); + if (isset($this->originalPrefix)) { drupal_valid_test_ua($this->originalPrefix); } diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index ac2f0a0..7e0001c 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -770,7 +770,7 @@ public function getSessionName() { * * Installs Drupal with the installation profile specified in * \Drupal\simpletest\WebTestBase::$profile into the prefixed database. - + * * Afterwards, installs any additional modules specified in the static * \Drupal\simpletest\WebTestBase::$modules property of each class in the * class hierarchy. @@ -946,6 +946,11 @@ protected function setUp() { $this->resetAll(); $this->kernel->prepareLegacyRequest($request); + // Explicitly call register() again on the container registered in \Drupal. + // @todo This should already be called through + // DrupalKernel::prepareLegacyRequest() -> DrupalKernel::boot() but that + // appears to be calling a different container. + \Drupal::service('stream_wrapper_manager')->register(); // Temporary fix so that when running from run-tests.sh we don't get an // empty current path which would indicate we're on the home page. $path = current_path(); diff --git a/core/modules/system/src/Form/FileSystemForm.php b/core/modules/system/src/Form/FileSystemForm.php index d7b1ad5..8a4caf9 100644 --- a/core/modules/system/src/Form/FileSystemForm.php +++ b/core/modules/system/src/Form/FileSystemForm.php @@ -12,6 +12,8 @@ use Drupal\Core\Datetime\Date as DateFormatter; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Drupal\Core\StreamWrapper\StreamWrapperManager; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -27,16 +29,26 @@ class FileSystemForm extends ConfigFormBase { protected $dateFormatter; /** + * The stream wrapper manager. + * + * @var \Drupal\Core\StreamWrapper\StreamWrapperManager + */ + protected $streamWrapperManager; + + /** * Constructs a FileSystemForm object. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. * @param \Drupal\Core\Datetime\Date $date_formatter * The date formatter service. + * @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager + * The stream wrapper manager. */ - public function __construct(ConfigFactoryInterface $config_factory, DateFormatter $date_formatter) { + public function __construct(ConfigFactoryInterface $config_factory, DateFormatter $date_formatter, StreamWrapperManager $stream_wrapper_manager) { parent::__construct($config_factory); $this->dateFormatter = $date_formatter; + $this->streamWrapperManager = $stream_wrapper_manager; } /** @@ -45,7 +57,8 @@ public function __construct(ConfigFactoryInterface $config_factory, DateFormatte public static function create(ContainerInterface $container) { return new static ( $container->get('config.factory'), - $container->get('date') + $container->get('date'), + $container->get('stream_wrapper_manager') ); } @@ -88,9 +101,7 @@ public function buildForm(array $form, array &$form_state) { ); // Any visible, writeable wrapper can potentially be used for the files // directory, including a remote file system that integrates with a CDN. - foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) { - $options[$scheme] = String::checkPlain($info['description']); - } + $options = $this->streamWrapperManager->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE); if (!empty($options)) { $form['file_default_scheme'] = array( diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php index c1a29a6..5450cf1 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\ImageToolkit\ImageToolkitBase; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; /** * Defines the GD2 toolkit for image manipulation within Drupal. @@ -113,7 +114,7 @@ public function save($destination) { // Work around lack of stream wrapper support in imagejpeg() and imagepng(). if ($scheme && file_stream_wrapper_valid_scheme($scheme)) { // If destination is not local, save image to temporary local file. - $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL); + $local_wrappers = file_get_stream_wrappers(StreamWrapperInterface::LOCAL); if (!isset($local_wrappers[$scheme])) { $permanent_destination = $destination; $destination = drupal_tempnam('temporary://', 'gd_'); diff --git a/core/modules/system/src/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/src/Tests/File/ReadOnlyStreamWrapperTest.php index 96747b0..6cd324f 100644 --- a/core/modules/system/src/Tests/File/ReadOnlyStreamWrapperTest.php +++ b/core/modules/system/src/Tests/File/ReadOnlyStreamWrapperTest.php @@ -26,7 +26,7 @@ class ReadOnlyStreamWrapperTest extends FileTestBase { * * @var string */ - protected $classname = 'Drupal\file_test\DummyReadOnlyStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper'; /** * Test write functionality of the read-only stream wrapper. diff --git a/core/modules/system/src/Tests/File/RemoteFileDirectoryTest.php b/core/modules/system/src/Tests/File/RemoteFileDirectoryTest.php index 8ebe597..0e1453c 100644 --- a/core/modules/system/src/Tests/File/RemoteFileDirectoryTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileDirectoryTest.php @@ -33,7 +33,7 @@ class RemoteFileDirectoryTest extends DirectoryTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileScanDirectoryTest.php b/core/modules/system/src/Tests/File/RemoteFileScanDirectoryTest.php index f09e299..031a862 100644 --- a/core/modules/system/src/Tests/File/RemoteFileScanDirectoryTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileScanDirectoryTest.php @@ -33,7 +33,7 @@ class RemoteFileScanDirectoryTest extends ScanDirectoryTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileUnmanagedCopyTest.php b/core/modules/system/src/Tests/File/RemoteFileUnmanagedCopyTest.php index 18828a1..59de492 100644 --- a/core/modules/system/src/Tests/File/RemoteFileUnmanagedCopyTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileUnmanagedCopyTest.php @@ -33,7 +33,7 @@ class RemoteFileUnmanagedCopyTest extends UnmanagedCopyTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteRecursiveTest.php b/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteRecursiveTest.php index cb81a60..7519d95 100644 --- a/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteRecursiveTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteRecursiveTest.php @@ -33,7 +33,7 @@ class RemoteFileUnmanagedDeleteRecursiveTest extends UnmanagedDeleteRecursiveTes * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteTest.php b/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteTest.php index 4247cd4..c5f6c02 100644 --- a/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileUnmanagedDeleteTest.php @@ -33,7 +33,7 @@ class RemoteFileUnmanagedDeleteTest extends UnmanagedDeleteTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileUnmanagedMoveTest.php b/core/modules/system/src/Tests/File/RemoteFileUnmanagedMoveTest.php index 9714778..df0975a 100644 --- a/core/modules/system/src/Tests/File/RemoteFileUnmanagedMoveTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileUnmanagedMoveTest.php @@ -33,7 +33,7 @@ class RemoteFileUnmanagedMoveTest extends UnmanagedMoveTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/RemoteFileUnmanagedSaveDataTest.php b/core/modules/system/src/Tests/File/RemoteFileUnmanagedSaveDataTest.php index 47686ac..1d99774 100644 --- a/core/modules/system/src/Tests/File/RemoteFileUnmanagedSaveDataTest.php +++ b/core/modules/system/src/Tests/File/RemoteFileUnmanagedSaveDataTest.php @@ -33,7 +33,7 @@ class RemoteFileUnmanagedSaveDataTest extends UnmanagedSaveDataTest { * * @var string */ - protected $classname = 'Drupal\file_test\DummyRemoteStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper'; function setUp() { parent::setUp(); diff --git a/core/modules/system/src/Tests/File/StreamWrapperTest.php b/core/modules/system/src/Tests/File/StreamWrapperTest.php index 755e988..37ff430 100644 --- a/core/modules/system/src/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/src/Tests/File/StreamWrapperTest.php @@ -35,7 +35,7 @@ class StreamWrapperTest extends FileTestBase { * * @var string */ - protected $classname = 'Drupal\file_test\DummyStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyStreamWrapper'; /** * Test the getClassName() function. diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 111ddfe..8a0703d 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1416,80 +1416,10 @@ function hook_modules_uninstalled($modules) { } /** - * Registers PHP stream wrapper implementations associated with a module. - * - * Provide a facility for managing and querying user-defined stream wrappers - * in PHP. PHP's internal stream_get_wrappers() doesn't return the class - * registered to handle a stream, which we need to be able to find the handler - * for class instantiation. - * - * If a module registers a scheme that is already registered with PHP, it will - * be unregistered and replaced with the specified class. - * - * @return - * A nested array, keyed first by scheme name ("public" for "public://"), - * then keyed by the following values: - * - 'name' A short string to name the wrapper. - * - 'class' A string specifying the PHP class that implements the - * Drupal\Core\StreamWrapper\StreamWrapperInterface interface. - * - 'description' A string with a short description of what the wrapper does. - * - 'type' (Optional) A bitmask of flags indicating what type of streams this - * wrapper will access - local or remote, readable and/or writeable, etc. - * Many shortcut constants are defined in file.inc. Defaults to - * STREAM_WRAPPERS_NORMAL which includes all of these bit flags: - * - STREAM_WRAPPERS_READ - * - STREAM_WRAPPERS_WRITE - * - STREAM_WRAPPERS_VISIBLE - * - * @see file_get_stream_wrappers() - * @see hook_stream_wrappers_alter() - * @see system_stream_wrappers() - */ -function hook_stream_wrappers() { - return array( - 'public' => array( - 'name' => t('Public files'), - 'class' => 'Drupal\Core\StreamWrapper\PublicStream', - 'description' => t('Public local files served by the webserver.'), - 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, - ), - 'private' => array( - 'name' => t('Private files'), - 'class' => 'Drupal\Core\StreamWrapper\PrivateStream', - 'description' => t('Private local files served by Drupal.'), - 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, - ), - 'temp' => array( - 'name' => t('Temporary files'), - 'class' => 'Drupal\Core\StreamWrapper\TemporaryStream', - 'description' => t('Temporary local files for upload and previews.'), - 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, - ), - 'cdn' => array( - 'name' => t('Content delivery network files'), - // @todo: Fix the name of this class when we decide on module PSR-0 usage. - 'class' => 'MyModuleCDNStream', - 'description' => t('Files served by a content delivery network.'), - // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL - ), - 'youtube' => array( - 'name' => t('YouTube video'), - // @todo: Fix the name of this class when we decide on module PSR-0 usage. - 'class' => 'MyModuleYouTubeStream', - 'description' => t('Video streamed from YouTube.'), - // A module implementing YouTube integration may decide to support using - // the YouTube API for uploading video, but here, we assume that this - // particular module only supports playing YouTube video. - 'type' => STREAM_WRAPPERS_READ_VISIBLE, - ), - ); -} - -/** * Alters the list of PHP stream wrapper implementations. * * @see file_get_stream_wrappers() - * @see hook_stream_wrappers() + * @see \Drupal\Core\StreamWrapper\StreamWrapperManager */ function hook_stream_wrappers_alter(&$wrappers) { // Change the name of private files to reflect the performance. diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 117dc49..005086e 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -719,35 +719,13 @@ function system_theme_suggestions_field(array $variables) { } /** - * Implements hook_stream_wrappers(). + * Implements hook_stream_wrappers_alter(). */ -function system_stream_wrappers() { - $wrappers = array( - 'public' => array( - 'name' => new TranslationWrapper('Public files'), - 'class' => 'Drupal\Core\StreamWrapper\PublicStream', - 'description' => new TranslationWrapper('Public local files served by the webserver.'), - 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, - ), - 'temporary' => array( - 'name' => new TranslationWrapper('Temporary files'), - 'class' => 'Drupal\Core\StreamWrapper\TemporaryStream', - 'description' => new TranslationWrapper('Temporary local files for upload and previews.'), - 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, - ), - ); - +function system_stream_wrappers_alter(&$wrappers) { // Only register the private file stream wrapper if a file path has been set. - if (\Drupal::config('system.file')->get('path.private')) { - $wrappers['private'] = array( - 'name' => new TranslationWrapper('Private files'), - 'class' => 'Drupal\Core\StreamWrapper\PrivateStream', - 'description' => new TranslationWrapper('Private local files served by Drupal.'), - 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, - ); + if (!\Drupal::config('system.file')->get('path.private')) { + unset($wrappers['private']); } - - return $wrappers; } /**