diff --git a/core/core.services.yml b/core/core.services.yml index 6f8cce6..6e32f25 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -475,6 +475,9 @@ services: plugin.manager.condition: class: Drupal\Core\Condition\ConditionManager arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler'] + plugin.manager.stream_wrapper: + class: Drupal\Core\StreamWrapper\StreamWrapperManager + arguments: ['@container.namespaces', '@module_handler'] kernel_destruct_subscriber: class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber tags: diff --git a/core/includes/file.inc b/core/includes/file.inc index ec329e1..62140fd 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -137,37 +137,8 @@ /** * 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 | @@ -175,7 +146,7 @@ * bits set are returned. Defaults to STREAM_WRAPPERS_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 @@ -184,78 +155,33 @@ * * @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]; + return Drupal::service('plugin.manager.stream_wrapper')->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']; + $wrapper = Drupal::service('plugin.manager.stream_wrapper')->getDefinition($scheme); + return !empty($wrapper) ? $wrapper['class'] : FALSE; } /** * 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". * @@ -273,10 +199,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. */ @@ -288,10 +214,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". @@ -308,7 +234,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() { @@ -324,10 +250,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) { @@ -350,25 +276,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('plugin.manager.stream_wrapper')->getInstance(array('uri' => $uri)); } /** @@ -382,25 +300,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 + * @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('plugin.manager.stream_wrapper')->getInstance(array('scheme' => $scheme)); } /** diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 8789366..9f0a8d1 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -412,6 +412,12 @@ function install_begin_request(&$install_state) { $container ->register('module_handler', 'Drupal\Core\Extension\ModuleHandler'); + // Register the stream wrapper manager with an empty set of namespaces. + $container + ->register('plugin.manager.stream_wrapper', 'Drupal\Core\StreamWrapper\StreamWrapperManager') + ->addArgument(new ArrayObject()) + ->addArgument(new Reference('module_handler')); + // Register the Guzzle HTTP client for fetching translation files from a // remote translation server such as localization.drupal.org. $container->register('http_default_client', 'Guzzle\Http\Client') diff --git a/core/lib/Drupal/Core/Annotation/StreamWrapper.php b/core/lib/Drupal/Core/Annotation/StreamWrapper.php new file mode 100644 index 0000000..f72a6a6 --- /dev/null +++ b/core/lib/Drupal/Core/Annotation/StreamWrapper.php @@ -0,0 +1,68 @@ +isDot() && is_dir($component->getPathname() . '/Plugin')) { $namespaces['Drupal\\' . $parent_directory .'\\' . $component->getFilename()] = DRUPAL_ROOT . '/core/lib'; diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php index 0e5a57b..c296f48 100644 --- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php @@ -30,15 +30,15 @@ public function getImplementations($hook) { // Allow logging. case 'watchdog': return parent::getImplementations($hook); + // Forms and pages do not render without the basic elements defined in // system_element_info(). case 'element_info': // 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. case 'system_theme_info': // Those are needed by user_access() to check access on update.php. @@ -46,10 +46,12 @@ public function getImplementations($hook) { case 'entity_load': case 'user_role_load': return array(); - // t() in system_stream_wrappers() needs this. Other schema calls aren't - // supported. + + // t() (called indirectly by \Drupal\Core\StreamWrapper\PublicStream) + // 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/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php index 833580f..6aeced6 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php @@ -7,11 +7,22 @@ namespace Drupal\Core\StreamWrapper; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; +use Drupal\Core\StreamWrapper\LocalStream; + /** * Drupal private (private://) stream wrapper class. * * Provides support for storing privately accessible files with the Drupal file * interface. + * + * @StreamWrapper( + * id = "private", + * name = @Translation("Private files"), + * description = @Translation("Private local files served by Drupal."), + * type = STREAM_WRAPPERS_LOCAL_NORMAL + * ) */ class PrivateStream extends LocalStream { diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 207b77a..b2b87fd 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -7,11 +7,22 @@ namespace Drupal\Core\StreamWrapper; +use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; + /** * Defines a Drupal public (public://) stream wrapper class. * * Provides support for storing publicly accessible files with the Drupal file * interface. + * + * @StreamWrapper( + * id = "public", + * name = @Translation("Public files"), + * description = @Translation("Public local files served by the webserver."), + * type = STREAM_WRAPPERS_LOCAL_NORMAL + * ) */ class PublicStream extends LocalStream { diff --git a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php new file mode 100644 index 0000000..3d9fef8 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -0,0 +1,169 @@ +alterInfo($module_handler, 'stream_wrappers'); + } + + /** + * 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 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 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. + */ + public function getWrappers($filter = STREAM_WRAPPERS_ALL) { + if (!$this->wrappers) { + $wrappers = $this->getDefinitions(); + foreach ($wrappers as $scheme => $info) { + // Pre-populate the static cache with the filters most typically used. + $this->wrappers[STREAM_WRAPPERS_ALL][$scheme] = $info; + if (($info['type'] & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) { + $this->wrappers[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $info; + } + } + } + + if (!isset($this->wrappers[$filter])) { + $this->wrappers[$filter] = array(); + foreach ($this->wrappers[STREAM_WRAPPERS_ALL] as $scheme => $info) { + // Bit-wise filter. + if (($info['type'] & $filter) == $filter) { + $this->wrappers[$filter][$scheme] = $info; + } + } + } + + return $this->wrappers[$filter]; + } + + /** + * {@inheritdoc} + */ + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + + // Record whether we are overriding an existing scheme. + if (in_array($plugin_id, stream_get_wrappers(), TRUE)) { + $definition['override'] = TRUE; + stream_wrapper_unregister($plugin_id); + } + else { + $definition['override'] = FALSE; + } + + if (($definition['type'] & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) { + stream_wrapper_register($plugin_id, $definition['class']); + } + else { + stream_wrapper_register($plugin_id, $definition['class'], STREAM_IS_URL); + } + } + + /** + * {@inheritdoc} + */ + public function getInstance(array $options) { + if (isset($options['scheme'])) { + $scheme = $options['scheme']; + $uri = $scheme . '://'; + } + elseif (isset($options['uri'])) { + $uri = $options['uri']; + $scheme = file_uri_scheme($uri); + } + else { + return FALSE; + } + + // Attempt to instantiate a stream wrapper. + try { + $instance = $this->createInstance($scheme); + $instance->setUri($uri); + return $instance; + } + catch (PluginException $e) { + return FALSE; + } + } + +} diff --git a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php index 42dd4fd..0ace393 100644 --- a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php @@ -7,11 +7,22 @@ namespace Drupal\Core\StreamWrapper; +use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; + /** * Defines a Drupal temporary (temporary://) stream wrapper class. * * Provides support for storing temporarily accessible files with the Drupal * file interface. + * + * @StreamWrapper( + * id = "temporary", + * name = @Translation("Temporary files"), + * description = @Translation("Temporary local files for upload and previews."), + * type = STREAM_WRAPPERS_LOCAL_HIDDEN + * ) */ class TemporaryStream extends LocalStream { diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index b6b88dc..1b77063 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/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/StreamWrapper/DummyReadOnlyStreamWrapper.php similarity index 64% 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 b5adf01..5b5b09d 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,17 +2,25 @@ /** * @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; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; /** * Helper class for testing the stream wrapper registry. * * Dummy stream wrapper implementation (dummy-readonly://). + * + * @StreamWrapper( + * id = "dummy-readonly", + * name = @Translation("Dummy files (readonly)"), + * description = @Translation("Dummy wrapper for simpletest (readonly).") + * ) */ class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream { function getDirectoryPath() { 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 51% 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..e156d6a 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,12 +2,14 @@ /** * @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; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; /** * Helper class for testing the stream wrapper registry. @@ -15,6 +17,12 @@ * Dummy remote stream wrapper implementation (dummy-remote://). * * Basically just the public scheme but not returning a local file for realpath. + * + * @StreamWrapper( + * id = "dummy-remote", + * name = @Translation("Dummy files (remote)"), + * description = @Translation("Dummy wrapper for simpletest (remote).") + * ) */ class DummyRemoteStreamWrapper extends PublicStream { function realpath() { 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 64% 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..ffc3d9d 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,17 +2,25 @@ /** * @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; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; /** * Helper class for testing the stream wrapper registry. * * Dummy stream wrapper implementation (dummy://). + * + * @StreamWrapper( + * id = "dummy", + * name = @Translation("Dummy files"), + * description = @Translation("Dummy wrapper for simpletest.") + * ) */ class DummyStreamWrapper extends LocalStream { function getDirectoryPath() { diff --git a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php index 73976f9..182559a 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php +++ b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php @@ -247,7 +247,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('plugin.manager.stream_wrapper')->getWrappers(STREAM_WRAPPERS_WRITE_VISIBLE); foreach ($wrappers as $wrapper => $wrapper_data) { file_unmanaged_delete_recursive($wrapper . '://styles/' . $this->id()); } diff --git a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php similarity index 64% rename from core/modules/locale/lib/Drupal/locale/TranslationsStream.php rename to core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php index 34ef38e..bf80fb9 100644 --- a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php +++ b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php @@ -2,17 +2,26 @@ /** * @file - * Definition of Drupal\locale\TranslationStream. + * Definition of Drupal\locale\StreamWrapper\TranslationStream. */ -namespace Drupal\locale; +namespace Drupal\locale\StreamWrapper; use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\Annotation\StreamWrapper; +use Drupal\Core\Annotation\Translation; /** * Defines a Drupal translations (translations://) stream wrapper class. * * Provides support for storing translation files. + * + * @StreamWrapper( + * id = "translations", + * name = @Translation("Translation files"), + * description = @Translation("Translation files"), + * type = STREAM_WRAPPERS_LOCAL_HIDDEN + * ) */ class TranslationsStream extends LocalStream { diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index c83ff19..6f73741 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -254,21 +254,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/system/lib/Drupal/system/Form/FileSystemForm.php b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php index 3a4aa18..2771392 100644 --- a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php +++ b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php @@ -7,7 +7,12 @@ namespace Drupal\system\Form; +use Drupal\Component\Utility\String; +use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\Context\ContextInterface; +use Drupal\Core\StreamWrapper\StreamWrapperManager; use Drupal\system\SystemConfigFormBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Configure file system settings for this site. @@ -15,6 +20,40 @@ class FileSystemForm extends SystemConfigFormBase { /** + * 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\Config\Context\ContextInterface $context + * The configuration context to use. + * @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager + * The stream wrapper manager. + */ + public function __construct(ConfigFactory $config_factory, ContextInterface $context, StreamWrapperManager $stream_wrapper_manager) { + parent::__construct($config_factory, $context); + + $this->streamWrapperManager = $stream_wrapper_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('config.context.free'), + $container->get('plugin.manager.stream_wrapper') + ); + } + + /** * {@inheritdoc} */ public function getFormID() { @@ -54,9 +93,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(STREAM_WRAPPERS_WRITE_VISIBLE)); if (!empty($options)) { $form['file_default_scheme'] = array( 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 c17dcb7..e929206 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php @@ -22,7 +22,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( diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 8ef7556..a6e9f4b 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -2042,80 +2042,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 1935f5c..455acf9 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2005,35 +2005,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 (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 (!config('system.file')->get('path.private')) { + unset($wrappers['private']); } - - return $wrappers; } /**