diff --git a/core/core.services.yml b/core/core.services.yml index 465d145..02d9540 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -499,6 +499,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 6065dd4..494db7f 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -139,37 +139,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 | @@ -177,7 +148,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 @@ -186,78 +157,35 @@ * * @see hook_stream_wrappers() * @see hook_stream_wrappers_alter() + * @see \Drupal\Core\StreamWrapper\StreamWrapperManager + * + * @deprecated Use \Drupal::service('plugin.manager.stream_wrapper')->getWrappers() */ 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". * @@ -275,10 +203,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. */ @@ -290,10 +218,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". @@ -310,7 +238,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() { @@ -326,10 +254,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) { @@ -352,25 +280,19 @@ function file_stream_wrapper_uri_normalize($uri) { * The scheme determines the stream wrapper class that should be * used by consulting the stream wrapper registry. * - * @param $uri + * @param string $uri * A stream, referenced as "scheme://target". * - * @return + * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool * Returns a new stream wrapper object appropriate for the given URI or FALSE * if no registered handler could be found. For example, a URI of * "private://example.txt" would return a new private stream wrapper object * (Drupal\Core\StreamWrapper\PrivateStream). + * + * @deprecated Use \Drupal::service('plugin.manager.stream_wrapper')->getInstance() */ 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)); } /** @@ -384,25 +306,19 @@ function file_stream_wrapper_get_instance_by_uri($uri) { * Note: the instance URI will be initialized to "scheme://" so that you can * make the customary method calls as if you had retrieved an instance by URI. * - * @param $scheme + * @param string $scheme * If the stream was "public://target", "public" would be the scheme. * - * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface + * @return \Drupal\Core\StreamWrapper\StreamWrapperInterface|bool * Returns a new stream wrapper object appropriate for the given $scheme. * For example, for the public scheme a stream wrapper object * (Drupal\Core\StreamWrapper\PublicStream). * FALSE is returned if no registered handler could be found. + * + * @deprecated Use \Drupal::service('plugin.manager.stream_wrapper')->getInstance() */ 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 9b0ef4c..9280976 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -413,6 +413,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 7d7ac9c..aa38d37 100644 --- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php @@ -35,15 +35,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. @@ -51,10 +51,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 39b3f28..dbe09fc 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\StreamWrapper\PrivateStream. + * Contains \Drupal\Core\StreamWrapper\PrivateStream. */ namespace Drupal\Core\StreamWrapper; @@ -12,24 +12,29 @@ * * 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 { /** - * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() + * {@inheritdoc} */ public function getDirectoryPath() { return \Drupal::config('system.file')->get('path.private'); } /** - * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl(). - * - * @return string - * Returns the HTML URI of a private file. + * {@inheritdoc} */ function getExternalUrl() { $path = str_replace('\\', '/', $this->getTarget()); return url('system/files/' . $path, array('absolute' => TRUE)); } + } diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 90f17b3..c9fb480 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\StreamWrapper\PublicStream. + * Contains \Drupal\Core\StreamWrapper\PublicStream. */ namespace Drupal\Core\StreamWrapper; @@ -12,6 +12,13 @@ * * 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..3f4ec19 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -0,0 +1,171 @@ +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..4aacb5e 100644 --- a/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/TemporaryStream.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\StreamWrapper\TemporaryStream. + * Contains \Drupal\Core\StreamWrapper\TemporaryStream. */ namespace Drupal\Core\StreamWrapper; @@ -12,21 +12,29 @@ * * 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 { /** - * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() + * {@inheritdoc} */ public function getDirectoryPath() { return file_directory_temp(); } /** - * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl(). + * {@inheritdoc} */ public function getExternalUrl() { $path = str_replace('\\', '/', $this->getTarget()); return url('system/temporary/' . $path, array('absolute' => TRUE)); } + } 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/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 b5adf01..52293ce 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; @@ -13,27 +13,34 @@ * 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 { + + /** + * {@inheritdoc} + */ function getDirectoryPath() { return variable_get('stream_public_path', 'sites/default/files'); } /** - * Override getInternalUri(). - * - * Return a dummy path for testing. + * {@inheritdoc} */ function getInternalUri() { return '/dummy/example.txt'; } /** - * Override getExternalUrl(). - * - * Return the HTML URI of a public file. + * {@inheritdoc} */ function getExternalUrl() { return '/dummy/example.txt'; } + } 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 54% 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..6dc2d0e 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; @@ -15,9 +15,20 @@ * 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 { + + /** + * {@inheritdoc} + */ 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 61% 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..ffd6311 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; @@ -13,6 +13,12 @@ * 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() { @@ -20,20 +26,17 @@ function getDirectoryPath() { } /** - * Override getInternalUri(). - * - * Return a dummy path for testing. + * {@inheritdoc} */ function getInternalUri() { return '/dummy/example.txt'; } /** - * Override getExternalUrl(). - * - * Return the HTML URI of a public file. + * {@inheritdoc} */ function getExternalUrl() { return '/dummy/example.txt'; } + } diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php index 42abf7c..a67c265 100644 --- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php +++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php @@ -254,7 +254,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) { if (file_exists($directory = $wrapper . '://styles/' . $this->id())) { file_unmanaged_delete_recursive($directory); diff --git a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php similarity index 62% rename from core/modules/locale/lib/Drupal/locale/TranslationsStream.php rename to core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php index 5245bd4..5492275 100644 --- a/core/modules/locale/lib/Drupal/locale/TranslationsStream.php +++ b/core/modules/locale/lib/Drupal/locale/StreamWrapper/TranslationsStream.php @@ -2,10 +2,10 @@ /** * @file - * Definition of Drupal\locale\TranslationStream. + * Contains \Drupal\locale\StreamWrapper\TranslationStream. */ -namespace Drupal\locale; +namespace Drupal\locale\StreamWrapper; use Drupal\Core\StreamWrapper\LocalStream; @@ -13,21 +13,30 @@ * 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 { /** - * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() + * {@inheritdoc} */ function getDirectoryPath() { return \Drupal::config('locale.settings')->get('translation.path'); } /** - * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl(). + * {@inheritdoc} + * * @throws \LogicException PO files URL should not be public. */ function getExternalUrl() { throw new \LogicException('PO files URL should not be public.'); } + } diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index ae37daf..65bbab6 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -242,21 +242,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 100c921..29a070b 100644 --- a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php +++ b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php @@ -7,8 +7,14 @@ namespace Drupal\system\Form; +use Drupal\Component\Utility\String; +use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\Context\ContextInterface; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\StreamWrapper\StreamWrapperManager; +use Drupal\system\SystemConfigFormBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Configure file system settings for this site. @@ -16,6 +22,40 @@ 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\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 +94,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 16bc592..548f342 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( diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index abda14e..40520d7 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1827,80 +1827,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 03f9042..f27522d 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1970,35 +1970,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; } /**