diff --git a/core/core.services.yml b/core/core.services.yml index db5240658f91dd22c4b2bd6b17bba326adce1cf9..05d80695e9fff262a1fb7ec214f8ae5fc55d8487 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -861,6 +861,23 @@ services: plugin.manager.element_info: class: Drupal\Core\Render\ElementInfoManager 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 6030f14339764aff7f9ce6b13396329e9bbe643d..f543c77d5658cf1f399a544464d81192392e5425 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(). @@ -55,45 +23,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 * @{ @@ -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. - * - * @return + * 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 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); } /** @@ -301,10 +154,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. */ @@ -338,7 +191,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() { @@ -354,10 +207,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) { @@ -380,25 +233,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); } /** @@ -412,25 +260,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 adb7d81abb846daa98bf8f79a321560a73df3346..1550ac0405a6f8fbaf8b563257499ac81825a805 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -325,6 +325,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/includes/install.inc b/core/includes/install.inc index 9515b4f13a17c4fa6f6831b9018c6d7d72660c4b..bcca67ce3e5c43f53055530a66ae2a4b5e79a5a9 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -652,16 +652,6 @@ function drupal_install_system($install_state) { $kernel->getContainer()->get('module_handler')->install(array('system'), FALSE); \Drupal::service('router.builder')->rebuild(); - // DrupalKernel::prepareLegacyRequest() above calls into - // DrupalKernel::bootCode(), which primes file_get_stream_wrappers()'s static - // list of custom stream wrappers that are based on the currently enabled - // list of modules (none). - // @todo Custom stream wrappers of a new module have to be registered as soon - // as the module is installed/enabled. Fix either ModuleHandler::install() - // and/or DrupalKernel::updateModules(). - // @see https://drupal.org/node/2028109 - drupal_static_reset('file_get_stream_wrappers'); - // Ensure default language is saved. if (isset($install_state['parameters']['langcode'])) { \Drupal::config('system.site') diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 8393903fb650645061f670467052a91d125b7e2b..db54f9133ebede9a1140c12b61cd0d8185d05612 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -11,6 +11,7 @@ use Drupal\Core\Cache\ListCacheBinsPass; use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass; use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; @@ -55,6 +56,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 0000000000000000000000000000000000000000..01193d3d802bb34b81cebef721332a3a8ff6bf1c --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterStreamWrappersPass.php @@ -0,0 +1,38 @@ +<?php + +/** + * @file + * Contains Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass. + */ + +namespace Drupal\Core\DependencyInjection\Compiler; + +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds services tagged 'stream_wrapper' to the stream_wrapper_manager service. + */ +class RegisterStreamWrappersPass implements CompilerPassInterface { + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) { + if (!$container->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 6a9b78d8b88f61e77d6a8d3a94bfa8bb3713532c..c014e4ad2541c22351e5045431d9d005d3d4f741 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -389,6 +389,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; @@ -401,6 +402,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; @@ -430,9 +432,6 @@ public function preHandle(Request $request) { // Put the request on the stack. $this->container->get('request_stack')->push($request); - // Make sure all stream wrappers are registered. - file_get_stream_wrappers(); - // Set the allowed protocols once we have the config available. $allowed_protocols = $this->container->get('config.factory')->get('system.filter')->get('protocols'); if (!isset($allowed_protocols)) { diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index fe6c0809ffb2af55a5d6d08948ece83b539e1ab1..035e75facf4853f0eeed18c8abd7f1c69e344b04 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -865,8 +865,7 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // via Drush, as the 'translations' stream wrapper is provided by // Interface Translation module and is later used to import // translations. - drupal_static_reset('file_get_stream_wrappers'); - file_get_stream_wrappers(); + \Drupal::service('stream_wrapper_manager')->register(); // Update the theme registry to include it. drupal_theme_rebuild(); diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 937953f2f49bc8e2eb8ac08d8eab94acfc0bd2e1..0111747462def17a58d057704e5f23ccc9aa335b 100644 --- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php @@ -42,6 +42,13 @@ abstract class LocalStream implements StreamWrapperInterface { */ protected $uri; + /** + * {@inheritdoc} + */ + public static function getType() { + return StreamWrapperInterface::NORMAL; + } + /** * Gets the path that the wrapper is responsible for. * diff --git a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php index ce076567e25f33b9423e13bcbbf78f92e36af1c2..84aa2c174aaf2eea2503711579492bce2226be28 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php @@ -19,6 +19,27 @@ class PrivateStream extends LocalStream { use UrlGeneratorTrait; + /** + * {@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() */ diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 637c9ef0c8bcfbf466df73cbdf067f0ea4f88e62..e2b78ec37ce8233eb026fe1c0b445e7cb952fb27 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -18,6 +18,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} */ diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php index 3f9f5d412dee4a551fd35c7a4dc1f1b325ad4dbe..ac95452b9feb7fb68b6e52183ddf5d33c5b33e55 100644 --- a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php @@ -29,6 +29,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. * diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php new file mode 100644 index 0000000000000000000000000000000000000000..43532a0299ded8216ab62f92aceee7eecd582ef1 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -0,0 +1,337 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\StreamWrapper\StreamWrapperManager. + */ + +namespace Drupal\Core\StreamWrapper; + +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\DependencyInjection\ContainerAware; + +/** + * Provides a StreamWrapper manager. + * + * @see file_get_stream_wrappers() + * @see hook_stream_wrappers_alter() + * @see system_stream_wrappers() + * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface + */ +class StreamWrapperManager extends ContainerAware { + + /** + * Contains stream wrapper info. + * + * An associative array where keys are scheme names and values are themselves + * associative arrays with the keys class, type and (optionally) service_id, + * and string values. + * + * @var array + */ + protected $info = array(); + + /** + * Contains collected stream wrappers. + * + * Keyed by filter, each value is itself an associative array keyed by scheme. + * Each of those values is an array representing a stream wrapper, with the + * following keys and values: + * - class: stream wrapper class name + * - type: a bitmask corresponding to the type constants in + * StreamWrapperInterface + * - service_id: name of service + * + * The array on key StreamWrapperInterface::ALL contains representations of + * all schemes and corresponding wrappers. + * + * @var array + */ + protected $wrappers = array(); + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Constructs a StreamWrapperManager object. + * + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + */ + public function __construct(ModuleHandlerInterface $module_handler) { + $this->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])) { + return $this->wrappers[$filter]; + } + else if (isset($this->wrappers[StreamWrapperInterface::ALL])) { + $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]; + } + else { + return array(); + } + } + + /** + * 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->info[$scheme]['service_id'])) { + $instance = $this->container->get($this->info[$scheme]['service_id']); + $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. + * @param string $scheme + * The scheme for which the wrapper should be registered. + */ + public function addStreamWrapper($service_id, $class, $scheme) { + $this->info[$scheme] = array( + 'class' => $class, + 'type' => $class::getType(), + 'service_id' => $service_id, + ); + } + + /** + * Registers the tagged stream wrappers. + * + * Internal use only. + */ + public function register() { + $this->moduleHandler->alter('stream_wrappers', $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() { + // Normally, there are definitely wrappers set for the ALL filter. However, + // in some cases involving many container rebuilds (e.g. WebTestBase), + // $this->wrappers may be empty although wrappers are still registered + // globally. Thus an isset() check is needed before iterating. + if (isset($this->wrappers[StreamWrapperInterface::ALL])) { + 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 f04213d6141ad07ac543a19f929434df5306a66d..2f7806000c6d455bd007e1900f5fe466461c1afc 100644 --- a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php @@ -15,6 +15,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() */ diff --git a/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/core/lib/Drupal/Core/Test/TestRunnerKernel.php index 50bc112bcc439b3037d51cd69f3eb78a950d44a5..8829113357a2cfe19ad3e7348c0afb689b99db3a 100644 --- a/core/lib/Drupal/Core/Test/TestRunnerKernel.php +++ b/core/lib/Drupal/Core/Test/TestRunnerKernel.php @@ -75,9 +75,7 @@ public function boot() { simpletest_classloader_register(); - // Register System module stream wrappers and create the build/artifacts - // directory if necessary. - file_get_stream_wrappers(); + // Create the build/artifacts directory if necessary. if (!is_dir('public://simpletest')) { mkdir('public://simpletest', 0777, TRUE); } diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc index 2c16f05deed90dfeae7323978e61fab104fced17..85006ebb0c73c7a19014da1967771ea23a2fa03a 100644 --- a/core/modules/editor/editor.admin.inc +++ b/core/modules/editor/editor.admin.inc @@ -5,6 +5,7 @@ * Administration functions for editor.module. */ +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Component\Utility\SafeMarkup; use Drupal\editor\Entity\Editor; @@ -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 133998ec021e3fd8ee229eef52a2920c6a4bcf2e..13c861666bb71a3d1799f0b469b60677ff82b36f 100644 --- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php +++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php @@ -13,6 +13,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; /** @@ -133,10 +134,7 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state ), ); - $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 b1bdeb3cf33a04181ab9b59d2473681311f047d0..6ee2ecc32170fccdce9c72c0de05012d02377567 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -13,29 +13,6 @@ const FILE_URL_TEST_CDN_1 = 'http://cdn1.example.com'; 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. * 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 0000000000000000000000000000000000000000..1e05c26bff613ccd1d249442a43402ea8ce108e2 --- /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/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php similarity index 65% rename from core/modules/file/tests/file_test/src/DummyReadOnlyStreamWrapper.php rename to core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php index d230bc5afa2ab33ed74449936e4d8772fdaeb8bf..99cf3e864f66efd1ba809903a65e575440f51675 100644 --- a/core/modules/file/tests/file_test/src/DummyReadOnlyStreamWrapper.php +++ b/core/modules/file/tests/file_test/src/StreamWrapper/DummyReadOnlyStreamWrapper.php @@ -2,10 +2,10 @@ /** * @file - * Definition of Drupal\file_test\DummyReadOnlyStreamWrapper. + * Contains \Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper. */ -namespace Drupal\file_test; +namespace Drupal\file_test\StreamWrapper; use Drupal\Core\StreamWrapper\LocalReadOnlyStream; @@ -15,6 +15,21 @@ * Dummy stream wrapper implementation (dummy-readonly://). */ class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream { + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Dummy files (readonly)'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Dummy wrapper for simpletest (readonly).'); + } + function getDirectoryPath() { return conf_path() . '/files'; } diff --git a/core/modules/file/tests/file_test/src/DummyRemoteStreamWrapper.php b/core/modules/file/tests/file_test/src/StreamWrapper/DummyRemoteStreamWrapper.php similarity index 52% rename from core/modules/file/tests/file_test/src/DummyRemoteStreamWrapper.php rename to core/modules/file/tests/file_test/src/StreamWrapper/DummyRemoteStreamWrapper.php index 274119691af74c36554289c70dfd11bc00a9e355..7e9f089765ea87933d5978e3dc5d303750665cf6 100644 --- a/core/modules/file/tests/file_test/src/DummyRemoteStreamWrapper.php +++ b/core/modules/file/tests/file_test/src/StreamWrapper/DummyRemoteStreamWrapper.php @@ -2,10 +2,10 @@ /** * @file - * Definition of Drupal\file_test\DummyRemoteStreamWrapper. + * Contains \Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper. */ -namespace Drupal\file_test; +namespace Drupal\file_test\StreamWrapper; use Drupal\Core\StreamWrapper\PublicStream; @@ -17,6 +17,21 @@ * Basically just the public scheme but not returning a local file for realpath. */ class DummyRemoteStreamWrapper extends PublicStream { + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Dummy files (remote)'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Dummy wrapper for simpletest (remote).'); + } + function realpath() { return FALSE; } diff --git a/core/modules/file/tests/file_test/src/DummyStreamWrapper.php b/core/modules/file/tests/file_test/src/StreamWrapper/DummyStreamWrapper.php similarity index 65% rename from core/modules/file/tests/file_test/src/DummyStreamWrapper.php rename to core/modules/file/tests/file_test/src/StreamWrapper/DummyStreamWrapper.php index cbea40f039a53b317e6cd137608ffdfa87c3899e..2ed31d0caba251472759684d70d4b2b9a6e0ae44 100644 --- a/core/modules/file/tests/file_test/src/DummyStreamWrapper.php +++ b/core/modules/file/tests/file_test/src/StreamWrapper/DummyStreamWrapper.php @@ -2,10 +2,10 @@ /** * @file - * Definition of Drupal\file_test\DummyStreamWrapper. + * Contains \Drupal\file_test\StreamWrapper\DummyStreamWrapper. */ -namespace Drupal\file_test; +namespace Drupal\file_test\StreamWrapper; use Drupal\Core\StreamWrapper\LocalStream; @@ -15,6 +15,21 @@ * Dummy stream wrapper implementation (dummy://). */ class DummyStreamWrapper extends LocalStream { + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Dummy files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Dummy wrapper for simpletest.'); + } + function getDirectoryPath() { return conf_path() . '/files'; } diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php index 0aea62d6fea521f7f317dfe5c468dd487dfa6892..037119b95c79284e67d4d392affcb6c6280c0021 100644 --- a/core/modules/image/src/Entity/ImageStyle.php +++ b/core/modules/image/src/Entity/ImageStyle.php @@ -19,6 +19,7 @@ use Drupal\image\ImageStyleInterface; use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** @@ -247,7 +248,7 @@ public function flush($path = NULL) { } // Delete the style directory in each registered wrapper. - $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE); + $wrappers = \Drupal::service('stream_wrapper_manager')->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 f92e5494c66f926b1473b0448fe181ba2003cef9..405630895b4757ac5c574f44cad4dbc5c2110934 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -11,6 +11,7 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; use Drupal\file\Entity\File; use Drupal\file\Plugin\Field\FieldType\FileItem; @@ -168,10 +169,7 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state // 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 01ef7814837a371dc1757743f937c65a2fc30915..957dc8e2ed7f411fd94193fa162a73b2ae93d356 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -195,21 +195,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 'configurable_language'. */ diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index b3cb964bf22e09857d2ad8f72e9264323079e3dd..e25786d135ae7ce9d7a2e9028275816cbf5ac06c 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -16,3 +16,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 0000000000000000000000000000000000000000..44c89577220d60a4da3d2884441137f8d2bd1763 --- /dev/null +++ b/core/modules/locale/src/StreamWrapper/TranslationsStream.php @@ -0,0 +1,57 @@ +<?php + +/** + * @file + * Contains \Drupal\locale\StreamWrapper\TranslationStream. + */ + +namespace Drupal\locale\StreamWrapper; + +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; +use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; + +/** + * Defines a Drupal translations (translations://) stream wrapper class. + * + * Provides support for storing translation files. + */ +class TranslationsStream extends LocalStream { + + /** + * {@inheritdoc} + */ + public static function getType() { + return StreamWrapperInterface::LOCAL_HIDDEN; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return t('Translation files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return t('Translation files'); + } + + /** + * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() + */ + function getDirectoryPath() { + return \Drupal::config('locale.settings')->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 5534d7e4478768d5769cfbaaec31248f966104df..e281988b08424e76f9311a92606a7808ee5f956e 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -16,6 +16,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Site\Settings; use Symfony\Component\DependencyInjection\Parameter; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; @@ -78,13 +79,11 @@ abstract class KernelTestBase extends TestBase { 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} @@ -305,6 +304,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); } @@ -495,53 +500,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 b8bed094f1ee9ffc3748ae54b29c37d4dd8a394b..9475715b4f6f8c6d4f2cdf3d00ea725e953072a8 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -1063,10 +1063,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(); @@ -1188,6 +1185,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); } @@ -1196,9 +1199,6 @@ private function restoreEnvironment() { } conf_path(TRUE, TRUE); - // Restore stream wrappers of the test runner. - file_get_stream_wrappers(); - // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); $callbacks = $this->originalShutdownCallbacks; diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index bf05b7c6c5e1e2a5d7339268fbc7698c24eac99d..39aeabcfe800e13ccf892387a40604f5b433f677 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -738,7 +738,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. @@ -909,13 +909,17 @@ protected function setUp() { // Reset/rebuild all data structures after enabling the modules, primarily // to synchronize all data structures and caches between the test runner and // the child site. - // Affects e.g. file_get_stream_wrappers(). // @see \Drupal\Core\DrupalKernel::bootCode() // @todo Test-specific setUp() methods may set up further fixtures; find a // way to execute this after setUp() is done, or to eliminate it entirely. $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. + $this->container->get('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 010b9c40a73c6f5d37cdee6bc0683ac709049ea3..7c8db2114b17b749c12e95c7fa422129c51aeca3 100644 --- a/core/modules/system/src/Form/FileSystemForm.php +++ b/core/modules/system/src/Form/FileSystemForm.php @@ -13,6 +13,8 @@ use Drupal\Core\Form\FormStateInterface; 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,6 +29,13 @@ class FileSystemForm extends ConfigFormBase { */ protected $dateFormatter; + /** + * The stream wrapper manager. + * + * @var \Drupal\Core\StreamWrapper\StreamWrapperManager + */ + protected $streamWrapperManager; + /** * Constructs a FileSystemForm object. * @@ -34,10 +43,13 @@ class FileSystemForm extends ConfigFormBase { * The factory for configuration objects. * @param \Drupal\Core\Datetime\DateFormatter $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; } /** @@ -46,7 +58,8 @@ public function __construct(ConfigFactoryInterface $config_factory, DateFormatte public static function create(ContainerInterface $container) { return new static ( $container->get('config.factory'), - $container->get('date.formatter') + $container->get('date.formatter'), + $container->get('stream_wrapper_manager') ); } @@ -89,9 +102,7 @@ public function buildForm(array $form, FormStateInterface $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 473972fb15b9a71d98927af2f90544f6461f0210..7d0a28c593ea17e0e517eac39b2c342caaca15ad 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php @@ -11,6 +11,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\ImageToolkit\ImageToolkitBase; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; /** * Defines the GD2 toolkit for image manipulation within Drupal. @@ -165,7 +166,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 c9cb68b80efc718a971d9462fb83eaeb7243c9c9..008e4ed5aee5f158ccb5341e1cc29a1b20a838d9 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 5858fb97d1e8d4c926ab941677fb8157b499fd3c..7f9d9129a13bf7a199ee73b92e6fcd886384bf50 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'; protected 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 2b21d32984b7a790f91c2a2547f5e3f2b5747270..cc57f90c37b5581b3603d54314739fedc4199748 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'; protected 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 eadeea5ae75a4cc34e0dfa2acbb197494b437fc0..402196e6056fea30c5bca2480e46998eebad8fd0 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'; protected 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 ef7019f8051e48943c83a7d1b1f5d4f4a3e7e7b8..c6bc98243517103e6f054edd39a4849798a0de39 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'; protected 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 38e6e8a7e3f62e56681d6417bc2f561496c95e50..1cfcd9257c1188194b9f61cf5c985df835f0d7e9 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'; protected 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 a198f7502886a74ce870782cab5c904335de4fd8..ae495b91428e3355709151906548d1fd8e9f974c 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'; protected 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 52d05ef6a91ce6c507e18f7d9dd8fe93383e24cf..73ebf4e984374f4c236a9ac96fb030ef3fb63cbf 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'; protected 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 0c9721423c598a5f48f2f489f0c99378538f1ab6..e678f907ceb8c6a389383a720b559a7cce48e064 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 cd2e6cf6ee574f28d4c85f30aee41581dff49c45..d9c44f47934ca0f93b80de0958028646c2e5e163 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1327,81 +1327,11 @@ function hook_modules_uninstalled($modules) { mymodule_cache_rebuild(); } -/** - * 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 26a6e902cad9f5745a2f30d609f94240737cfe94..9376013c362b09b4cd241cd3834aacd0ec3a7912 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -303,35 +303,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; } /**