diff --git a/core/includes/file.inc b/core/includes/file.inc index c63920c..5d7af0d 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -5,6 +5,7 @@ * API for handling file uploads and server file management. */ +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\StreamWrapper\LocalStream; use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -140,37 +141,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 | @@ -178,7 +150,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 @@ -187,73 +159,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::service('plugin.manager.stream_wrapper')->getDefinitions(); - $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". * @@ -271,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. */ @@ -286,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". @@ -306,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() { @@ -322,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) { @@ -348,25 +280,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)); } /** @@ -380,25 +304,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/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php index 6d6526f..5b5b966 100644 --- a/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php +++ b/core/lib/Drupal/Core/StreamWrapper/StreamWrapperManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\StreamWrapper; +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManager; @@ -24,6 +25,11 @@ class StreamWrapperManager extends DefaultPluginManager { /** + * @var array + */ + protected $wrappers = array(); + + /** * Constructs a new StreamWrapperManager object. * * @param \Traversable $namespaces @@ -43,4 +49,128 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac $this->setCacheBackend($cache_backend, $language_manager, '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/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php index 7dd36c0..1e3f53a 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 @@ -316,7 +316,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/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(