diff --git a/core/core.services.yml b/core/core.services.yml index 8af072f..787c145 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -564,6 +564,15 @@ 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 } kernel_destruct_subscriber: class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber tags: diff --git a/core/includes/file.inc b/core/includes/file.inc index e3e9326..87424fc 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -5,43 +5,11 @@ * API for handling file uploads and server file management. */ -use Drupal\Core\StreamWrapper\LocalStream; use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage; use Drupal\Component\Utility\String; +use Drupal\Core\StreamWrapper\LocalStream; 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(). @@ -54,45 +22,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. @@ -150,125 +79,49 @@ /** * 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 + * 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 + * 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() + * @see \Drupal\Core\StreamWrapper\StreamWrapperManager */ -function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) { - $wrappers_storage = &drupal_static(__FUNCTION__); - - if (!isset($wrappers_storage)) { - $wrappers = \Drupal::moduleHandler()->invokeAll('stream_wrappers'); - foreach ($wrappers as $scheme => $info) { - // Add defaults. - $wrappers[$scheme] += array('type' => STREAM_WRAPPERS_NORMAL); - } - drupal_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. */ 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); } /** * Returns the scheme of a URI (e.g. a stream). * - * @param $uri + * @param string $uri * A stream, referenced as "scheme://target". * - * @return + * @return string|bool * A string containing the name of the scheme, or FALSE if none. For example, * the URI "public://example.txt" would return "public". * @@ -286,10 +139,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. */ @@ -301,10 +154,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". @@ -321,7 +174,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() { @@ -337,10 +190,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) { @@ -363,25 +216,17 @@ 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). */ 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); } /** @@ -395,25 +240,17 @@ 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. */ 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/module.inc b/core/includes/module.inc index 429f5db..c9a13ff 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -30,7 +30,7 @@ */ function system_list($type) { $lists = &drupal_static(__FUNCTION__); - if ($cached = cache('bootstrap')->get('system_list')) { + if ($cached = \Drupal::cache('bootstrap')->get('system_list')) { $lists = $cached->data; } else { @@ -92,7 +92,7 @@ function system_list($type) { // Set the theme engine prefix. $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; } - cache('bootstrap')->set('system_list', $lists); + \Drupal::cache('bootstrap')->set('system_list', $lists); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache with all enabled modules and themes. @@ -110,8 +110,8 @@ function system_list_reset() { drupal_static_reset('system_list'); drupal_static_reset('system_rebuild_module_data'); drupal_static_reset('list_themes'); - cache('bootstrap')->delete('system_list'); - cache()->delete('system_info'); + \Drupal::cache('bootstrap')->delete('system_list'); + \Drupal::cache()->delete('system_info'); // Remove last known theme data state. // This causes system_list() to call system_rebuild_theme_data() on its next // invocation. When enabling a module that implements hook_system_info_alter() diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 098b165..c4b72f4 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -8,27 +8,28 @@ namespace Drupal\Core; use Drupal\Core\Cache\ListCacheBinsPass; -use Drupal\Core\DependencyInjection\ServiceProviderInterface; -use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterStringTranslatorsPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass; use Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\Theme\ThemeNegotiatorPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Scope; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; /** * ServiceProvider class for mandatory core services. @@ -54,6 +55,8 @@ public function register(ContainerBuilder $container) { $this->registerModuleHandler($container); $this->registerUuid($container); + $container->addCompilerPass(new RegisterStreamWrappersPass()); + $container->addCompilerPass(new RegisterRouteFiltersPass()); // 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 0160119..a3230c2 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -175,6 +175,7 @@ public function boot() { return; } $this->initializeContainer(); + $this->container->get('stream_wrapper_manager')->register(); $this->booted = TRUE; if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) { watchdog('DrupalKernel', 'Container cannot be written to disk'); @@ -188,6 +189,7 @@ public function shutdown() { if (FALSE === $this->booted) { return; } + $this->container->get('stream_wrapper_manager')->unregister(); $this->booted = FALSE; $this->container = NULL; } diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php index 65f26a0..cf2fa1f 100644 --- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php @@ -42,8 +42,6 @@ public function getImplementations($hook) { // Forms do not render without the basic elements in // drupal_common_theme() called from system_theme(). case 'theme': - // user_update_8011() uses public:// to create the image style directory. - case 'stream_wrappers': return array('system'); // This is called during rebuild to find testing themes. @@ -54,11 +52,6 @@ public function getImplementations($hook) { case 'user_role_load': return array(); - // t() in system_stream_wrappers() needs this. Other schema calls aren't - // supported. - case 'schema': - return array('locale'); - default: throw new \LogicException("Invoking hooks $hook is not supported during updates"); } diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 5c1ee6f..b32d1e3 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 90f17b3..e028ad0 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} + */ 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 9830a01..9992e1e 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. + */ + + /** + * Stream wrapper type flag -- not visible in the UI or accessible via web, + * but readable and writable. E.g. the temporary directory for uploads. + */ + const HIDDEN = 0x0012; + + /** + * Hidden, readable and writeable using local files. + */ + const LOCAL_HIDDEN = 0x0013; + + /** + * Visible, readable and writeable. + */ + const WRITE_VISIBLE = 0x0022; + + /** + * Visible and read-only. + */ + const READ_VISIBLE = 0x0014; + + /** + * Stream wrapper type flag -- 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 = 0x0022; + + /** + * Visible, readable and writeable using local files. + */ + const LOCAL_NORMAL = 0x0023; + + /** + * 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..4599085 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -0,0 +1,272 @@ +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]; + } + + /** + * @todo + */ + public function getNames($filter = StreamWrapperInterface::ALL) { + $names = array(); + foreach (array_keys($this->getWrappers($filter)) as $scheme) { + $name[$scheme] = $this->getViaScheme($scheme)->getName(); + } + + return $names; + } + + /** + * @todo + */ + public function getDescriptions($filter = StreamWrapperInterface::ALL) { + $names = array(); + foreach (array_keys($this->getWrappers($filter)) as $scheme) { + $name[$scheme] = $this->getViaScheme($scheme)->getDescription(); + } + + return $names; + } + + /** + * 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']); + + // Pre-populate the static cache with the filters most typically used. + $this->wrappers[StreamWrapperInterface::ALL][$scheme] = $info; + if (($info['type'] & StreamWrapperInterface::WRITE_VISIBLE) == StreamWrapperInterface::WRITE_VISIBLE) { + $this->wrappers[StreamWrapperInterface::WRITE_VISIBLE][$scheme] = $info; + } + } + } + + /** + * 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. + */ + protected 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); + } + } + +} 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 a29924b..80b4ec0 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. @@ -52,10 +53,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']; - } + $stream_wrappers = \Drupal::service('stream_wrapper_manager')->getDescriptions(StreamWrapperInterface::WRITE_VISIBLE); if (!empty($options)) { $form['scheme'] = array( '#type' => 'radios', diff --git a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php index 9bb9dd0..6f190a6 100644 --- a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php +++ b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php @@ -7,10 +7,11 @@ namespace Drupal\file\Plugin\Field\FieldType; +use Drupal\Core\Field\ConfigFieldItemInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\TypedData\DataDefinition; -use Drupal\Core\Field\ConfigFieldItemInterface; /** * Plugin implementation of the 'file' field type. @@ -128,10 +129,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 e5a10a0..9b25057 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/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php similarity index 59% rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php index b427d3f..ab2f7b8 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/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,8 +15,23 @@ * 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 'sites/default/files'; + return variable_get('stream_public_path', 'sites/default/files'); } /** diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyRemoteStreamWrapper.php similarity index 52% rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyRemoteStreamWrapper.php index 2741196..7e9f089 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyRemoteStreamWrapper.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/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/lib/Drupal/file_test/DummyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyStreamWrapper.php similarity index 65% rename from core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php rename to core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyStreamWrapper.php index 4836f09..295426d 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/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 'sites/default/files'; } diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php index 57869d4..402231e 100644 --- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php +++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php @@ -7,13 +7,14 @@ namespace Drupal\image\Entity; +use Drupal\Component\Utility\Crypt; +use Drupal\Component\Utility\Url; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageControllerInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\image\ImageEffectBag; use Drupal\image\ImageEffectInterface; use Drupal\image\ImageStyleInterface; -use Drupal\Component\Utility\Crypt; -use Drupal\Component\Utility\Url; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** @@ -245,7 +246,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/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php index be20c66..9685943 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php @@ -7,8 +7,9 @@ namespace Drupal\image\Plugin\Field\FieldType; -use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Drupal\Core\TypedData\DataDefinition; use Drupal\file\Plugin\Field\FieldType\FileItem; /** @@ -149,10 +150,7 @@ public function settingsForm(array $form, array &$form_state, $has_data) { // the field. $settings = $this->getFieldDefinition()->getField()->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/lib/Drupal/locale/TranslationsStream.php b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php similarity index 55% rename from core/modules/locale/lib/Drupal/locale/TranslationsStream.php rename to core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php index 5245bd4..44c8957 100644 --- a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php +++ b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php @@ -2,12 +2,15 @@ /** * @file - * Definition of Drupal\locale\TranslationStream. + * Contains \Drupal\locale\StreamWrapper\TranslationStream. */ -namespace Drupal\locale; +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. @@ -17,6 +20,27 @@ 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() { diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 7a7574a..53eceaf 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -222,21 +222,6 @@ function locale_theme() { } /** - * Implements hook_stream_wrappers(). - */ -function locale_stream_wrappers() { - $wrappers = array( - 'translations' => array( - 'name' => t('Translation files'), - 'class' => 'Drupal\locale\TranslationsStream', - 'description' => t('Translation files'), - 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, - ), - ); - return $wrappers; -} - -/** * Implements hook_language_insert(). */ function locale_language_insert($language) { diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index b5f4cf4..6765575 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/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 78b154e8..c077992 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -1146,10 +1146,6 @@ private function restoreEnvironment() { } } - // In case a fatal error occurred that was not in the test process read the - // log to pick up any fatal errors. - simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); - // Delete temporary files directory. file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10), array($this, 'filePreDeleteCallback')); @@ -1179,6 +1175,17 @@ private function restoreEnvironment() { // Restore original statics and globals. \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(); + + // In case a fatal error occurred that was not in the test process read the + // log to pick up any fatal errors. + simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); + if (isset($this->originalPrefix)) { drupal_valid_test_ua($this->originalPrefix); } diff --git a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php index 2d51684..139f1f7 100644 --- a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php +++ b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php @@ -7,8 +7,15 @@ namespace Drupal\system\Form; -use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\Component\Utility\String; +use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\Context\ContextInterface; use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Drupal\Core\StreamWrapper\StreamWrapperManager; +use Drupal\system\SystemConfigFormBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Configure file system settings for this site. @@ -16,6 +23,37 @@ class FileSystemForm extends ConfigFormBase { /** + * The stream wrapper manager. + * + * @var \Drupal\Core\StreamWrapper\StreamWrapperManager + */ + protected $streamWrapperManager; + + /** + * Constructs a FileSystemForm object. + * + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * The factory for configuration objects. + * @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager + * The stream wrapper manager. + */ + public function __construct(ConfigFactory $config_factory, StreamWrapperManager $stream_wrapper_manager) { + parent::__construct($config_factory); + + $this->streamWrapperManager = $stream_wrapper_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('stream_wrapper_manager') + ); + } + + /** * {@inheritdoc} */ public function getFormId() { @@ -54,9 +92,9 @@ 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] = check_plain($info['description']); - } + $options = array_map(function ($wrapper) { + return String::checkPlain($wrapper['description']); + }, $this->streamWrapperManager->getWrappers(StreamWrapperInterface::WRITE_VISIBLE)); if (!empty($options)) { $form['file_default_scheme'] = array( diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php index e5c5528..c892a35 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php @@ -7,9 +7,10 @@ namespace Drupal\system\Plugin\ImageToolkit; -use Drupal\Core\Image\ImageInterface; -use Drupal\Core\ImageToolkit\ImageToolkitBase; use Drupal\Component\Utility\Image as ImageUtility; +use Drupal\Core\ImageToolkit\ImageToolkitBase; +use Drupal\Core\Image\ImageInterface; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; /** * Defines the GD2 toolkit for image manipulation within Drupal. @@ -227,7 +228,7 @@ public function save(ImageInterface $image, $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/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php index ef84796..db1a7e1 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php @@ -20,7 +20,7 @@ class ReadOnlyStreamWrapperTest extends FileTestBase { public static $modules = array('file_test'); protected $scheme = 'dummy-readonly'; - protected $classname = 'Drupal\file_test\DummyReadOnlyStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper'; public static function getInfo() { return array( @@ -30,16 +30,6 @@ public static function getInfo() { ); } - function setUp() { - parent::setUp(); - drupal_static_reset('file_get_stream_wrappers'); - } - - function tearDown() { - parent::tearDown(); - stream_wrapper_unregister($this->scheme); - } - /** * Test write functionality of the read-only stream wrapper. */ diff --git a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php index 16bc592..9c729a4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php @@ -23,7 +23,7 @@ class StreamWrapperTest extends WebTestBase { public static $modules = array('file_test'); protected $scheme = 'dummy'; - protected $classname = 'Drupal\file_test\DummyStreamWrapper'; + protected $classname = 'Drupal\file_test\StreamWrapper\DummyStreamWrapper'; public static function getInfo() { return array( @@ -33,16 +33,6 @@ public static function getInfo() { ); } - function setUp() { - parent::setUp(); - drupal_static_reset('file_get_stream_wrappers'); - } - - function tearDown() { - parent::tearDown(); - stream_wrapper_unregister($this->scheme); - } - /** * Test the getClassName() function. */ diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 1de6b15..2865e56 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1697,80 +1697,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 c2cf620..5aaa865 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1894,35 +1894,13 @@ function system_library_info() { } /** - * Implements hook_stream_wrappers(). + * Implements hook_stream_wrappers_alter(). */ -function system_stream_wrappers() { - $wrappers = 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, - ), - 'temporary' => array( - 'name' => t('Temporary files'), - 'class' => 'Drupal\Core\StreamWrapper\TemporaryStream', - 'description' => t('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' => t('Private files'), - 'class' => 'Drupal\Core\StreamWrapper\PrivateStream', - 'description' => t('Private local files served by Drupal.'), - 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, - ); + if (!Drupal::config('system.file')->get('path.private')) { + unset($wrappers['private']); } - - return $wrappers; } /** diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 54e5b11..2708078 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -20,3 +20,11 @@ services: arguments: ['@batch.storage'] tags: - { name: theme_negotiator, priority: 1000 } + 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 }