diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index e64bfca..761e895 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2275,9 +2275,6 @@ function _drupal_bootstrap_page_cache() { // Allow specifying special cache handlers in settings.php, like // using memcached or files for storing cache information. require_once DRUPAL_ROOT . '/core/includes/cache.inc'; - foreach (variable_get('cache_backends', array()) as $include) { - require_once DRUPAL_ROOT . '/' . $include; - } // Check for a cache mode force from settings.php. if (variable_get('page_cache_without_database')) { $cache_enabled = TRUE; diff --git a/core/includes/cache.inc b/core/includes/cache.inc index 603a4d1..628bc21 100644 --- a/core/includes/cache.inc +++ b/core/includes/cache.inc @@ -27,16 +27,7 @@ function cache($bin = 'cache') { // Temporary backwards compatibiltiy layer, allow old style prefixed cache // bin names to be passed as arguments. $bin = str_replace('cache_', '', $bin); - - // We do not use drupal_static() here because we do not want to change the - // storage of a cache bin mid-request. - static $cache_objects; - if (!isset($cache_objects[$bin])) { - $cache_backends = cache_get_backends(); - $class = isset($cache_backends[$bin]) ? $cache_backends[$bin] : $cache_backends['cache']; - $cache_objects[$bin] = new $class($bin); - } - return $cache_objects[$bin]; + return drupal_container()->get('cache')->getPluginInstance(array('bin' => $bin)); } /** @@ -53,22 +44,12 @@ function cache($bin = 'cache') { * The list of tags to invalidate cache items for. */ function cache_invalidate(array $tags) { - foreach (cache_get_backends() as $bin => $class) { + foreach (drupal_container()->get('cache')->getPluginDefinitions() as $bin => $definition) { cache($bin)->invalidateTags($tags); } } /** - * Returns a list of cache backends for this site. - * - * @return - * An associative array with cache bins as keys, and backend classes as value. - */ -function cache_get_backends() { - return variable_get('cache_classes', array('cache' => 'Drupal\Core\Cache\DatabaseBackend')); -} - -/** * Expires data from the block and page caches. */ function cache_clear_all() { diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index eb9dff9..37113f3 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -295,7 +295,7 @@ function install_begin_request(&$install_state) { // because any data put in the cache during the installer is inherently // suspect, due to the fact that Drupal is not fully set up yet. require_once DRUPAL_ROOT . '/core/includes/cache.inc'; - $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\InstallBackend'); + $conf['cache_backends'] = array('cache' => array('class' => 'Drupal\Core\Cache\InstallBackend')); // The install process cannot use the database lock backend since the database // is not fully up, so we use a null backend implementation during the diff --git a/core/lib/Drupal/Component/Plugin/Derivative/DerivativeInterface.php b/core/lib/Drupal/Component/Plugin/Derivative/DerivativeInterface.php new file mode 100644 index 0000000..879ac73 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Derivative/DerivativeInterface.php @@ -0,0 +1,45 @@ +decorated = $decorated; + } + + /** + * Implements DiscoveryInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + + list($base_plugin_id, $derivative_id) = $this->decodePluginId($plugin_id); + + $plugin_definition = $this->decorated->getPluginDefinition($base_plugin_id); + $derivative_fetcher = $this->getDerivativeFetcher($base_plugin_id, $plugin_definition); + if ($derivative_fetcher) { + $plugin_definition = $derivative_fetcher->getDerivativeDefinition($derivative_id, $plugin_definition); + } + + return $plugin_definition; + } + + /** + * Implements DiscoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $plugin_definitions = $this->decorated->getPluginDefinitions(); + return $this->getDerivatives($plugin_definitions); + } + + /** + * Helper function for adding derivatives to a list of plugin definitions. + * + * This should be called by the class extending this in + * DiscoveryInterface::getPluginDefinitions(). + */ + protected function getDerivatives(array $base_plugin_definitions) { + $plugin_definitions = array(); + foreach ($base_plugin_definitions as $base_plugin_id => $plugin_definition) { + $derivative_fetcher = $this->getDerivativeFetcher($base_plugin_id, $plugin_definition); + if ($derivative_fetcher) { + $derivative_definitions = $derivative_fetcher->getDerivativeDefinitions($plugin_definition); + foreach ($derivative_definitions as $derivative_id => $derivative_definition) { + $plugin_id = $this->encodePluginId($base_plugin_id, $derivative_id); + $plugin_definitions[$plugin_id] = $derivative_definition; + } + } + else { + $plugin_definitions[$base_plugin_id] = $plugin_definition; + } + } + + return $plugin_definitions; + } + + /** + * Decode derivative id and plugin id from a string. + * + * @param string $plugin_id + * Plugin identifier that may point to a derivative plugin. + * + * @return + * An array with the base plugin id as the first index and the derivative id + * as the second. If there is no derivative id it will be null. + */ + protected function decodePluginId($plugin_id) { + // Try and split the passed plugin definition into a plugin and a + // derivative id. We don't need to check for !== FALSE because a leading + // colon would break the derivative system and doesn't makes sense. + if (strpos($plugin_id, ':')) { + return explode(':', $plugin_id, 2); + } + + return array($plugin_id, NULL); + } + + /** + * Encode plugin and derivative id's into a string. + * + * @param string $base_plugin_id + * The base plugin identifier. + * + * @param string $derivative_id + * The derivative identifier. + * + * @return string + * A uniquely encoded combination of the $base_plugin_id and $derivative_id. + */ + protected function encodePluginId($base_plugin_id, $derivative_id) { + if ($derivative_id) { + return "$base_plugin_id:$derivative_id"; + } + + // By returning the unmerged plugin_id, we are able to support derivative + // plugins that support fetching the base definitions. + return $base_plugin_id; + } + + /** + * Find a DerivativeInterface that can fetch derivatives for the plugin. + * + * @param string $base_plugin_id + * The base plugin id of the plugin. + * + * @param array $base_definition + * The base plugin definition to build derivatives. + * + * @return DerivativeInterface|null + * A DerivativeInterface or null if none exists for the plugin. + */ + protected function getDerivativeFetcher($base_plugin_id, array $base_definition) { + if (!isset($this->derivativeFetchers[$base_plugin_id])) { + $this->derivativeFetchers[$base_plugin_id] = FALSE; + if (isset($base_definition['derivative'])) { + $class = $base_definition['derivative']; + $this->derivativeFetchers[$base_plugin_id] = new $class($base_plugin_id); + } + } + return $this->derivativeFetchers[$base_plugin_id] ?: NULL; + } + + /** + * Pass through all unknown calls onto the decorated object. + */ + public function __call($method, $args) { + return call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php new file mode 100644 index 0000000..50e1f3a --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php @@ -0,0 +1,31 @@ +pluginDefinitions[$base_plugin_id]; + } + + /** + * Implements DerivativeAwareDiscovery::getBasePluginDefinitions(). + */ + public function getPluginDefinitions() { + return $this->pluginDefinitions; + } + + /** + * Set a plugin definition. + */ + public function setPluginDefinition($plugin, array $definition) { + $this->pluginDefinitions[$plugin] = $definition; + } + + /** + * Delete a plugin definition. + */ + public function deletePluginDefinition($plugin) { + unset($this->pluginDefinitions[$plugin]); + } +} diff --git a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php new file mode 100644 index 0000000..0b12867 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php @@ -0,0 +1,124 @@ +discovery = $discovery; + $this->classKey = $class_key; + } + + /** + * Implements FactoryInterface::createPluginInstance(). + */ + public function createPluginInstance($plugin_id, array $configuration) { + $plugin_class = $this->getPluginClass($plugin_id); + + // Lets figure out of there's a constructor for this class and pull + // arguments from the $options array if so to populate it. + $reflector = new \ReflectionClass($plugin_class); + if ($reflector->hasMethod('__construct')) { + $arguments = $this->getPluginInstanceArguments($reflector, $plugin_id, $configuration); + $instance = $reflector->newInstanceArgs($arguments); + } + else { + $instance = new $plugin_class(); + } + + return $instance; + } + + /** + * Inspect the plugin class and build a list of arguments for the constructor. + * + * This is provided as a helper method so factories extending the default + * factory can replace this and insert their own reflection logic. + * + * @param ReflectionClass $reflector + * The reflector object being used to inspect the plugin class. + * + * @param string $plugin_id + * The identifier of the plugin implementation. + * + * @param array $configuration + * An array of configuration that may be passed to the instance. + * + * @return + * An array of arguments to be passed to the constructor. + */ + protected function getPluginInstanceArguments(\ReflectionClass $reflector, $plugin_id, array $configuration) { + + $arguments = array(); + foreach ($reflector->getMethod('__construct')->getParameters() as $param) { + $param_name = $param->getName(); + $param_class = $param->getClass(); + + if ($param_name == 'plugin_id') { + $arguments[] = $plugin_id; + } + elseif ($param_name == 'configuration') { + $arguments[] = $configuration; + } + elseif ($param_class && $param_class->isInstance($this->discovery)) { + $arguments[] = $this->discovery; + } + elseif (isset($param_name, $configuration) || array_key_exists($param_name, $configuration)) { + $arguments[] = $configuration[$param_name]; + } + elseif ($param->isDefaultValueAvailable()) { + $arguments[] = $param->getDefaultValue(); + } + else { + // Missing constructor argument. + // TODO - Do we throw an exception or just skip and let sub classes + // extend this. + } + } + return $arguments; + } + + /** + * Responsible for finding the class relevant for a given plugin. + * + * @param array $config + * An array of configuration about the class. + * + * @return string + * The appropriate class name. + */ + protected function getPluginClass($plugin_id) { + if (empty($this->classKey)) { + throw new PluginException("The plugin type did not specify a valid key for determining the plugin instance class."); + } + + $plugin_definition = $this->discovery->getPluginDefinition($plugin_id); + if (empty($plugin_definition[$this->classKey])) { + throw new PluginException("The plugin did not specify an instance class."); + } + + $class = $plugin_definition[$this->classKey]; + + if (!class_exists($class)) { + throw new PluginException(t("Plugin instance class @class does not exist.", array('@class' => $class))); + } + + return $class; + } +} diff --git a/core/lib/Drupal/Component/Plugin/Factory/FactoryInterface.php b/core/lib/Drupal/Component/Plugin/Factory/FactoryInterface.php new file mode 100644 index 0000000..63a4339 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Factory/FactoryInterface.php @@ -0,0 +1,28 @@ +discovery = $discovery; + $this->factory = $factory; + $this->pluginIdKey = isset($options['id_key']) ? $options['id_key'] : 'plugin'; + $this->defaultPluginId = isset($options['default']) ? $options['default'] : 'default'; + } + + /** + * Implements MapperInterface::getPluginInstance(). + * + * This implements a simple mapper that does the following: + * - Maps a single $options key to the desired plugin id. + * - If that plugin doesn't exist, uses a default one. + * - Forwards all of $options to the plugin instance configuration. + */ + public function getPluginInstance(array $options) { + $instance_id = $this->getInstanceId($options); + if (!isset($this->instances[$instance_id])) { + $plugin_id = ($this->discovery->getPluginDefinition($options[$this->pluginIdKey]) !== NULL) ? $options[$this->pluginIdKey] : $this->defaultPluginId; + $configuration = $options; + $this->instances[$instance_id] = $this->factory->createPluginInstance($plugin_id, $configuration); + } + return $this->instances[$instance_id]; + } + + /** + * Returns an identifier for a given set of plugin options in order to reuse instances. + */ + protected function getInstanceId(array $options) { + return serialize($options); + } +} diff --git a/core/lib/Drupal/Component/Plugin/Mapper/MapperException.php b/core/lib/Drupal/Component/Plugin/Mapper/MapperException.php new file mode 100644 index 0000000..a0c3da7 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Mapper/MapperException.php @@ -0,0 +1,14 @@ +plugin_id = $plugin_id; + $this->discovery = $discovery; + $this->configuration = $configuration; + } + + /** + * Implements PluginInspectionInterface::getPluginId(). + */ + public function getPluginId() { + return $this->plugin_id; + } + + /** + * Implements PluginInspectionInterface::getPluginDefinition(). + */ + public function getpluginDefinition() { + return $this->discovery->getPluginDefinition($this->plugin_id); + } + + // Note: Plugin configuration is optional so its left to the plugin type to + // require a getter as part of its interface. +} diff --git a/core/lib/Drupal/Component/Plugin/PluginException.php b/core/lib/Drupal/Component/Plugin/PluginException.php new file mode 100644 index 0000000..8198622 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/PluginException.php @@ -0,0 +1,12 @@ +discovery->getPluginDefinition($plugin_id); + if (isset($definition)) { + $this->processPluginDefinition($definition, $plugin_id); + } + return $definition; + } + + /** + * Implements PluginTypeInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $definitions = $this->discovery->getPluginDefinitions(); + foreach ($definitions as $plugin_id => &$definition) { + $this->processPluginDefinition($definition, $plugin_id); + } + + return $definitions; + } + + /** + * Implements PluginTypeInterface::createPluginInstance(). + */ + public function createPluginInstance($plugin_id, array $configuration = array()) { + return $this->factory->createPluginInstance($plugin_id, $configuration); + } + + /** + * Implements PluginTypeInterface::getPluginInstance(). + */ + public function getPluginInstance(array $options) { + return $this->mapper->getPluginInstance($options); + } + + /** + * Helper that does extra processing on plugin definitions. + * + * By default we add defaults for the type to the definition. If a type has + * additional processing logic they can do that be replacing or extending the + * method. + */ + protected function processPluginDefinition(&$definition, $plugin_id) { + $definition += $this->defaults; + } +} diff --git a/core/lib/Drupal/Component/Plugin/PluginTypeInterface.php b/core/lib/Drupal/Component/Plugin/PluginTypeInterface.php new file mode 100644 index 0000000..63af79f --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/PluginTypeInterface.php @@ -0,0 +1,23 @@ +discovery = new PersistentVariableDiscovery('cache_backends', array('cache' => array('class' => 'Drupal\Core\Cache\DatabaseBackend'))); + $this->factory = new DefaultFactory($this); + $this->mapper = new DefaultMapper($this, $this, array('id_key' => 'bin', 'default' => 'cache')); + } +} diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php index 021e2f7..43f1714 100644 --- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php +++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php @@ -18,28 +18,28 @@ namespace Drupal\Core\Cache; * set a variable with the name of the cache bin as its key and the name of * your class as its value. For example, if your implementation of * Drupal\Core\Cache\CacheBackendInterface was called MyCustomCache, the - * following line would make Drupal use it for the 'cache_page' bin: + * following line would make Drupal use it for the 'page' bin: * @code - * $conf['cache_classes']['cache_page'] = 'MyCustomCache'; + * $conf['cache_backends']['page']['class'] = 'MyCustomCache'; * @endcode * * Additionally, you can register your cache implementation to be used by - * default for all cache bins by setting the $conf['cache_classes'] variable and - * changing the value of the 'cache' key to the name of your implementation of - * the Drupal\Core\Cache\CacheBackendInterface, e.g. + * default for all cache bins by setting the $conf['cache_backends'] variable + * and changing the value of the 'cache' key to the name of your implementation + * of the Drupal\Core\Cache\CacheBackendInterface, e.g. * @code - * $conf['cache_classes']['cache'] = 'MyCustomCache'; + * $conf['cache_backends']['cache']['class'] = 'MyCustomCache'; * @endcode * * To implement a completely custom cache bin, use the same variable format: * @code - * $conf['cache_classes']['custom_bin'] = 'MyCustomCache'; + * $conf['cache_backends']['custom_bin']['class'] = 'MyCustomCache'; * @endcode * To access your custom cache bin, specify the name of the bin when storing * or retrieving cached data: * @code - * cache_set($cid, $data, 'custom_bin', $expire); - * cache_get($cid, 'custom_bin'); + * cache('custom_bin')->set($cid, $data, $expire); + * cache('custom_bin')->get($cid); * @endcode * * @see cache() diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index b9b3431..a0677f9 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -16,6 +16,9 @@ class ContainerBuilder extends BaseContainerBuilder { /** * Registers the base Drupal services for the dependency injection container. + * + * @todo This is a temporary hardcoded initial registration pending a more + * robust mechanism for Drupal core and modules to declare their services. */ public function __construct() { parent::__construct(); @@ -24,5 +27,7 @@ class ContainerBuilder extends BaseContainerBuilder { // functions. This default is overridden by drupal_language_initialize() // during language negotiation. $this->register(LANGUAGE_TYPE_INTERFACE, 'Drupal\\Core\\Language\\Language'); + + $this->register('cache', 'Drupal\Core\Cache\Cache'); } } diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php new file mode 100644 index 0000000..347a489 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php @@ -0,0 +1,85 @@ +decorated = $decorated; + $this->cacheKey = $cache_key; + } + + /** + * Implements DicoveryInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + $definitions = $this->getCachedDefinitions(); + if (isset($definitions)) { + $definition = isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL; + } + else { + $definition = $this->decorated->getPluginDefinition($plugin_id); + } + return $definition; + } + + /** + * Implements DicoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $definitions = $this->getCachedDefinitions(); + if (!isset($definitions)) { + $definitions = $this->decorated->getPluginDefinitions(); + $this->setCachedDefinitions($definitions); + } + return $definitions; + } + + /** + * + */ + protected function getCachedDefinitions() { + if (!isset($this->definitions) && isset($this->cacheKey) && $cache = cache()->get($this->cacheKey)) { + $this->definitions = $cache->data; + } + return $this->definitions; + } + + /** + * + */ + protected function setCachedDefinitions($definitions) { + if (isset($this->cacheKey)) { + cache()->set($this->cacheKey, $definitions); + } + $this->definitions = $definitions; + } + + /** + * Pass through all unknown calls onto the decorated object. + */ + public function __call($method, $args) { + return call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php new file mode 100644 index 0000000..0721f2c --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php @@ -0,0 +1,45 @@ +hook = $hook; + } + + /** + * Implements DicoveryInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + $plugins = $this->getPluginDefinitions(); + return $plugins[$plugin_id]; + } + + /** + * Implements DicoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + foreach (module_implements($this->hook) as $module) { + $function = $module . '_' . $this->hook; + foreach ($function() as $plugin_id => $definition) { + $definition['module'] = $module; + $definitions[$plugin_id] = $definition; + } + } + drupal_alter($this->hook, $definitions); + return $definitions; + } + +} diff --git a/core/lib/Drupal/Core/Plugin/Discovery/PersistentVariableDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/PersistentVariableDiscovery.php new file mode 100644 index 0000000..a52342c --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Discovery/PersistentVariableDiscovery.php @@ -0,0 +1,39 @@ +variableName = $variable_name; + $this->default = $default; + } + + /** + * Implements DicoveryInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + $plugins = $this->getPluginDefinitions(); + return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : NULL; + } + + /** + * Implements DicoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + return variable_get($this->variableName, $this->default); + } + +} diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc index f31d98f..f5f12f8 100644 --- a/core/modules/image/image.admin.inc +++ b/core/modules/image/image.admin.inc @@ -94,8 +94,10 @@ function image_style_form($form, &$form_state, $style) { } // Build the new image effect addition form and add it to the effect list. + $image_effects = image_effect_definitions(); + ksort($image_effects); $new_effect_options = array(); - foreach (image_effect_definitions() as $effect => $definition) { + foreach ($image_effects as $effect => $definition) { $new_effect_options[$effect] = check_plain($definition['label']); } $form['effects']['new'] = array( diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 973f36a..1f42cf5 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Drupal\Core\File\File; +use Drupal\image\Effect; /** * Image style constant for user presets in the database. @@ -483,6 +484,16 @@ function image_field_update_instance($instance, $prior_instance) { } /** + * Implements hook_hook_info(). + */ +function image_hook_info() { + $hooks['image_effect_info'] = array( + 'group' => 'effects', + ); + return $hooks; +} + +/** * Clear cached versions of a specific file in all styles. * * @param $path @@ -914,7 +925,7 @@ function image_style_path($style_name, $uri) { } /** - * Pull in image effects exposed by modules implementing hook_image_effect_info(). + * Returns the definitions of all image effects. * * @return * An array of image effects to be used when transforming images. @@ -922,41 +933,15 @@ function image_style_path($style_name, $uri) { * @see image_effect_definition_load() */ function image_effect_definitions() { - global $language_interface; - - // hook_image_effect_info() includes translated strings, so each language is - // cached separately. - $langcode = $language_interface->langcode; - - $effects = &drupal_static(__FUNCTION__); - - if (!isset($effects)) { - if ($cache = cache()->get("image_effects:$langcode") && !empty($cache->data)) { - $effects = $cache->data; - } - else { - $effects = array(); - include_once DRUPAL_ROOT . '/core/modules/image/image.effects.inc'; - foreach (module_implements('image_effect_info') as $module) { - foreach (module_invoke($module, 'image_effect_info') as $name => $effect) { - // Ensure the current toolkit supports the effect. - $effect['module'] = $module; - $effect['name'] = $name; - $effect['data'] = isset($effect['data']) ? $effect['data'] : array(); - $effects[$name] = $effect; - } - } - uasort($effects, '_image_effect_definitions_sort'); - drupal_alter('image_effect_info', $effects); - cache()->set("image_effects:$langcode", $effects); - } - } - - return $effects; + // @todo Rather than hard-coding the plugin type class, register this with + // Drupal's dependency injection container once it supports compilable + // registration of module classes. + $plugin_type = new Effect(); + return $plugin_type->getPluginDefinitions(); } /** - * Load the definition for an image effect. + * Returns the definition of an image effect. * * The effect definition is a set of core properties for an image effect, not * containing any user-settings. The definition defines various functions to @@ -978,8 +963,11 @@ function image_effect_definitions() { * one-line summary of the effect. Does not include the "theme_" prefix. */ function image_effect_definition_load($effect) { - $definitions = image_effect_definitions(); - return isset($definitions[$effect]) ? $definitions[$effect] : FALSE; + // @todo Rather than hard-coding the plugin type class, register this with + // Drupal's dependency injection container once it supports compilable + // registration of module classes. + $plugin_type = new Effect(); + return $plugin_type->getPluginDefinition($effect); } /** @@ -1181,12 +1169,3 @@ function image_filter_keyword($value, $current_pixels, $new_pixels) { } return $value; } - -/** - * Internal function for sorting image effect definitions through uasort(). - * - * @see image_effect_definitions() - */ -function _image_effect_definitions_sort($a, $b) { - return strcasecmp($a['name'], $b['name']); -} diff --git a/core/modules/image/lib/Drupal/image/Effect.php b/core/modules/image/lib/Drupal/image/Effect.php new file mode 100644 index 0000000..8b08f67 --- /dev/null +++ b/core/modules/image/lib/Drupal/image/Effect.php @@ -0,0 +1,31 @@ +get(LANGUAGE_TYPE_INTERFACE)->langcode; + + $this->discovery = new CacheDecorator(new HookDiscovery('image_effect_info'), $cache_key); + $this->defaults = array( + 'data' => array(), + ); + } + + /** + * + */ + protected function processPluginDefinition(&$definition, $plugin_id) { + parent::processPluginDefinition($definition, $plugin_id); + $definition['name'] = $plugin_id; + } +} diff --git a/core/modules/simpletest/simpletest.info b/core/modules/simpletest/simpletest.info index af11f5b..aa00b72 100644 --- a/core/modules/simpletest/simpletest.info +++ b/core/modules/simpletest/simpletest.info @@ -5,3 +5,4 @@ version = VERSION core = 8.x files[] = simpletest.test configure = admin/config/development/testing/settings + diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTest.php new file mode 100644 index 0000000..ca94ded --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTest.php @@ -0,0 +1,98 @@ + 'Plugin API', + 'description' => 'Tests the the plugin system API.', + 'group' => 'Plugin API', + ); + } + + public function setUp() { + parent::setUp(); + + // Real modules implementing plugin types may expose a module-specific API + // for retrieving its plugin type objects, but for unit testing, we can get + // the object directly. + $this->TestPluginType = new TestPluginType(); + } + + function testPluginDefintionFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('coolaid'); + $this->assertEqual($plugin_definition['string'], 'oh yeah', 'Plugin definition retrievable by the test plugin type.'); + } + + function testPluginInstanceFetching() { + $plugin = $this->TestPluginType->createPluginInstance('coolaid', array('string' => 'this is a test')); + $this->assertEqual(get_class($plugin), 'Drupal\plugin_test\Plugin\TestPluginInstanceClass', 'Correct plugin instance implementation returned by the test plugin type.'); + $this->assertEqual($plugin->string(), 'this is a test', 'Plugin instance returns the proper string'); + } + + function testPluginDerivativeDefintionFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer:test1'); + $this->assertEqual($plugin_definition['string'], 'Test String 1', 'Plugin derivative definition retrievable by the test plugin type.'); + } + + function testPluginEmptyDerivativeDefintionFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer'); + $this->assertEqual($plugin_definition['string'], 'derivative test', 'Plugin derivative base returns the proper string'); + } + + function testPluginInstanceDerivativeFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer:test1'); + $plugin = $this->TestPluginType->createPluginInstance('beer:test1', $plugin_definition); + $this->assertEqual($plugin->string(), 'Test String 1', 'Plugin derivative instance 1 returns the proper string'); + + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer:test2'); + $plugin = $this->TestPluginType->createPluginInstance('beer:test2', $plugin_definition); + $this->assertEqual($plugin->string(), 'Test String 2', 'Plugin derivative instance 2 returns the proper string'); + } + + function testPluginEmptyDerivativeInstanceFetching() { + try { + // This test is working against a plugin definition which does not + // use_base => TRUE, and so it should throw an exception. + $plugin = $this->TestPluginType->createPluginInstance('wine'); + $this->fail('Drupal\Component\Plugin\PluginException expected'); + } + catch (Exception $e) { + if ($e instanceof PluginException) { + $this->pass('Drupal\Component\Plugin\PluginException expected and caught.'); + } + else { + $this->fail('Drupal\Component\Plugin\PluginException expected'); + } + } + try { + // This test is working against a plugin definition which does + // use_base => TRUE, and so it should not throw an exception. + $plugin = $this->TestPluginType->createPluginInstance('beer', array('string' => 'test')); + $this->pass('Drupal\Component\Plugin\PluginException not thrown'); + } + catch (Exception $e) { + $this->fail('An unexpected exception was thrown.'); + } + } + + function testPluginGetDefinitions() { + $definitions = $this->TestPluginType->getPluginDefinitions(); + $expected = array ( + 'coolaid', + 'beer:test1', + 'beer:test2', + 'beer', + 'wine:test1', + 'wine:test2', + ); + $this->assertIdentical($expected, array_keys($definitions), 'All plugin ids that were expected were found.'); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php new file mode 100644 index 0000000..a5fe93e --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php @@ -0,0 +1,25 @@ +getDerivativeDefinitions($base_plugin_definition); + if (isset($derivatives[$derivative_id])) { + return $derivatives[$derivative_id]; + } + } + + /** + * Implements DerivativeInterface::getDerivativeDefinitions(). + */ + public function getDerivativeDefinitions(array $base_plugin_definition) { + + $derivatives = array( + 'test1' => array( + 'string' => 'Test String 1', + ) + $base_plugin_definition, + 'test2' => array( + 'string' => 'Test String 2', + ) + $base_plugin_definition, + ); + if (!empty($base_plugin_definition['use_base'])) { + $derivatives[NULL] = $base_plugin_definition; + } + return $derivatives; + } +} diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginInstanceClass.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginInstanceClass.php new file mode 100644 index 0000000..ed33ad4 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginInstanceClass.php @@ -0,0 +1,21 @@ +string = $string; + } + + public function string() { + return $this->string; + } +} diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Type/TestPluginType.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Type/TestPluginType.php new file mode 100644 index 0000000..5f83fe2 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Type/TestPluginType.php @@ -0,0 +1,37 @@ +discovery = new DerivativeDiscoveryDecorator(new StaticDiscovery()); + $this->discovery->setPluginDefinition('coolaid', array( + 'string' => 'oh yeah', + 'class' => 'Drupal\plugin_test\Plugin\TestPluginInstanceClass', + )); + $this->discovery->setPluginDefinition('beer', array( + 'string' => 'derivative test', + 'class' => 'Drupal\plugin_test\Plugin\TestPluginInstanceClass', + 'derivative' => 'Drupal\plugin_test\Plugin\Derivative\DerivativeTest', + 'use_base' => TRUE, + )); + $this->discovery->setPluginDefinition('wine', array( + 'string' => 'derivative test', + 'class' => 'Drupal\plugin_test\Plugin\TestPluginInstanceClass', + 'derivative' => 'Drupal\plugin_test\Plugin\Derivative\DerivativeTest', + )); + $this->factory = new DefaultFactory($this); + $this->mapper = new DefaultMapper($this, $this); + } +} diff --git a/core/modules/system/tests/modules/plugin_test/plugin_test.info b/core/modules/system/tests/modules/plugin_test/plugin_test.info new file mode 100644 index 0000000..406e02f --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/plugin_test.info @@ -0,0 +1,6 @@ +name = "Plugin Test Support" +description = "Test that plugins can provide plugins and provide namespace discovery for plugin test implementations." +package = Testing +version = VERSION +core = 8.x +hidden = TRUE diff --git a/core/modules/system/tests/modules/plugin_test/plugin_test.module b/core/modules/system/tests/modules/plugin_test/plugin_test.module new file mode 100644 index 0000000..dc6986d --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/plugin_test.module @@ -0,0 +1,6 @@ +