diff --git a/core/includes/cache.inc b/core/includes/cache.inc index 73bb05f..82c8637 100644 --- a/core/includes/cache.inc +++ b/core/includes/cache.inc @@ -32,9 +32,7 @@ function cache($bin = 'cache') { // 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); + $cache_objects[$bin] = drupal_container()->get('cache')->getPluginInstance(array('bin' => $bin)); } return $cache_objects[$bin]; } @@ -53,7 +51,7 @@ 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); } } diff --git a/core/includes/module.inc b/core/includes/module.inc index 928abc9..3c1e786 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -764,7 +764,7 @@ function module_hook_info() { // case common.inc, subsystems, and modules are not loaded yet, so it does not // make sense to support hook groups resp. lazy-loaded include files prior to // full bootstrap. - if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) { + if (drupal_bootstrap(NULL, FALSE) < DRUPAL_BOOTSTRAP_CODE) { return array(); } $hook_info = &drupal_static(__FUNCTION__); 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..bb07069 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Derivative/DerivativeInterface.php @@ -0,0 +1,44 @@ +decorated = $decorated; + } + + /** + * Implements Drupal\Component\Plugin\Discovery\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 Drupal\Component\Plugin\Discovery\DiscoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $plugin_definitions = $this->decorated->getPluginDefinitions(); + return $this->getDerivatives($plugin_definitions); + } + + /** + * Adds 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; + } + + /** + * Decodes derivative id and plugin id from a string. + * + * @param string $plugin_id + * Plugin identifier that may point to a derivative plugin. + * + * @return array + * 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); + } + + /** + * Encodes 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; + } + + /** + * Finds a Drupal\Component\Plugin\Discovery\DerivativeInterface. + * + * This Drupal\Component\Plugin\Discovery\DerivativeInterface 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 Drupal\Component\Plugin\Discovery\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; + } + + /** + * Passes 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..de4afdb --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php @@ -0,0 +1,35 @@ +pluginDefinitions[$base_plugin_id]) ? $this->pluginDefinitions[$base_plugin_id] : NULL; + } + + /** + * Implements DerivativeAwareDiscovery::getBasePluginDefinitions(). + */ + public function getPluginDefinitions() { + return $this->pluginDefinitions; + } + + /** + * Sets a plugin definition. + */ + public function setPluginDefinition($plugin, array $definition) { + $this->pluginDefinitions[$plugin] = $definition; + } + + /** + * Deletes 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..9ae63e3 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php @@ -0,0 +1,80 @@ +discovery = $discovery; + $this->classKey = $class_key; + } + + /** + * Implements FactoryInterface::createPluginInstance(). + */ + public function createPluginInstance($plugin_id, array $configuration) { + $plugin_class = $this->getPluginClass($plugin_id); + return new $plugin_class($configuration); + } + + /** + * Finds 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..3a15583 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Factory/FactoryInterface.php @@ -0,0 +1,29 @@ +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; + } + + /** + * Inspects the plugin class and build a list of arguments for the constructor. + * + * This is provided as a helper method so factories extending this class 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 array + * 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($configuration[$param_name]) || 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; + } +} diff --git a/core/lib/Drupal/Component/Plugin/Mapper/DefaultMapper.php b/core/lib/Drupal/Component/Plugin/Mapper/DefaultMapper.php new file mode 100644 index 0000000..ada30ec --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Mapper/DefaultMapper.php @@ -0,0 +1,92 @@ +discovery = $discovery; + $this->factory = $factory; + $this->pluginIdKey = isset($options['id_key']) ? $options['id_key'] : 'plugin'; + $this->defaultPluginId = isset($options['default']) ? $options['default'] : 'default'; + } + + /** + * Implements Drupal\Component\Plugin\Mapper\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. + * + * @param array $options + * @todo + * + * @return + * @todo + */ + 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 Drupal\Component\Plugin\PluginInspectionInterface::getPluginId(). + */ + public function getPluginId() { + return $this->plugin_id; + } + + /** + * Implements Drupal\Component\Plugin\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..877e47e --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/PluginException.php @@ -0,0 +1,12 @@ +processPluginDefinition() if + * additional processing of plugins is necessary or helpful for development + * purposes. + * + * @var array + */ + protected $defaults = array(); + + /** + * Implements Drupal\Component\Plugin\PluginTypeInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + $definition = $this->discovery->getPluginDefinition($plugin_id); + if (isset($definition)) { + $this->processPluginDefinition($definition, $plugin_id); + } + return $definition; + } + + /** + * Implements Drupal\Component\Plugin\PluginTypeInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $definitions = $this->discovery->getPluginDefinitions(); + foreach ($definitions as $plugin_id => &$definition) { + $this->processPluginDefinition($definition, $plugin_id); + } + + return $definitions; + } + + /** + * Implements Drupal\Component\Plugin\PluginTypeInterface::createPluginInstance(). + */ + public function createPluginInstance($plugin_id, array $configuration = array()) { + return $this->factory->createPluginInstance($plugin_id, $configuration); + } + + /** + * Implements Drupal\Component\Plugin\PluginTypeInterface::getPluginInstance(). + */ + public function getPluginInstance(array $options) { + return $this->mapper->getPluginInstance($options); + } + + /** + * Performs 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 by 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..7487a66 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/PluginTypeInterface.php @@ -0,0 +1,24 @@ +discovery = new StaticDiscovery(); + foreach (variable_get('cache_classes', array('cache' => 'Drupal\Core\Cache\DatabaseBackend')) as $bin => $class) { + $this->discovery->setPluginDefinition($bin, array('class' => $class)); + } + + // Use a custom factory instead of ReflectionFactory to optimize + // performance. + $this->factory = new CacheFactory($this); + + $this->mapper = new DefaultMapper($this->discovery, $this->factory, array('id_key' => 'bin', 'default' => 'cache')); + } +} diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php new file mode 100644 index 0000000..dc3dc72 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheFactory.php @@ -0,0 +1,24 @@ +getPluginClass($plugin_id); + return new $class($configuration['bin']); + } + +} diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index aa73ace..dd797f0 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(); @@ -27,5 +30,8 @@ class ContainerBuilder extends BaseContainerBuilder { // Register the default language content. $this->register(LANGUAGE_TYPE_CONTENT, 'Drupal\\Core\\Language\\Language'); + + // Register the cache plugin type. + $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..209ccf0 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php @@ -0,0 +1,124 @@ +decorated = $decorated; + $this->cacheKey = $cache_key; + $this->cacheBin = $cache_bin; + } + + /** + * Implements Drupal\Component\Plugin\Discovery\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 Drupal\Component\Plugin\Discovery\DicoveryInterface::getPluginDefinitions(). + */ + public function getPluginDefinitions() { + $definitions = $this->getCachedDefinitions(); + if (!isset($definitions)) { + $definitions = $this->decorated->getPluginDefinitions(); + $this->setCachedDefinitions($definitions); + } + return $definitions; + } + + /** + * Returns the cached plugin definitions of the decorated discovery class. + * + * @return mixed + * On success this will return an array of plugin definitions. On failure + * this should return NULL, indicating to other methods that this has not + * yet been defined. Success with no values should return as an empty array + * and would actually be returned by the getPluginDefinitions() method. + */ + protected function getCachedDefinitions() { + if (!isset($this->definitions) && isset($this->cacheKey) && $cache = cache($this->cacheBin)->get($this->cacheKey)) { + $this->definitions = $cache->data; + } + return $this->definitions; + } + + /** + * Sets a cache of plugin definitions for the decorated discovery class. + * + * @param array $definitions + * List of definitions to store in cache. + */ + protected function setCachedDefinitions($definitions) { + if (isset($this->cacheKey)) { + cache($this->cacheBin)->set($this->cacheKey, $definitions); + } + $this->definitions = $definitions; + } + + /** + * Passes 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..6aba72c --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php @@ -0,0 +1,57 @@ +hook = $hook; + } + + /** + * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getPluginDefinition(). + */ + public function getPluginDefinition($plugin_id) { + $plugins = $this->getPluginDefinitions(); + return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : array(); + } + + /** + * Implements Drupal\Component\Plugin\Discovery\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/modules/image/image.admin.inc b/core/modules/image/image.admin.inc index d0ef114..4ba0c9f 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 f3bf83d..906c0d0 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Drupal\Core\File\File; +use Drupal\image\Effect; /** * Image style constant for user presets in the database. @@ -484,6 +485,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 @@ -915,7 +926,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. @@ -923,41 +934,15 @@ function image_style_path($style_name, $uri) { * @see image_effect_definition_load() */ function image_effect_definitions() { - $language_interface = drupal_container()->get(LANGUAGE_TYPE_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 @@ -979,8 +964,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); } /** @@ -1182,12 +1170,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..66cbd42 --- /dev/null +++ b/core/modules/image/lib/Drupal/image/Effect.php @@ -0,0 +1,41 @@ +get(LANGUAGE_TYPE_INTERFACE)->langcode; + $this->discovery = new CacheDecorator(new HookDiscovery('image_effect_info'), $cache_key); + + // Defaults added to each effect definition. + $this->defaults = array( + 'data' => array(), + ); + } + + /** + * Overrides Drupal\Component\Plugin\PluginType::processPluginDefinition(). + */ + 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 46f00c3..6e18c42 100644 --- a/core/modules/simpletest/simpletest.info +++ b/core/modules/simpletest/simpletest.info @@ -4,3 +4,4 @@ package = Core version = VERSION core = 8.x 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..a15cc89 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTest.php @@ -0,0 +1,110 @@ + '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('fruitpunch'); + $this->assertEqual($plugin_definition['string'], 'Almost as good as grape drink.', 'Plugin definition retrievable by the test plugin type.'); + } + + function testPluginInstanceFetching() { + $plugin = $this->TestPluginType->createPluginInstance('fruitpunch', 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:stout'); + // This returns the string specified by BeerDerivativeTest for the stout + // derived plugin. + $this->assertEqual($plugin_definition['string'], 'Might even carry your bags for you.', 'Plugin derivative definition retrievable by the test plugin type.'); + } + + function testPluginEmptyDerivativeDefintionFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer'); + // This returns the 'default' string which was explicitly set for the plugin + // in the TestPluginType constructor. + $this->assertEqual($plugin_definition['string'], 'derivative test', 'Plugin derivative base returns the proper string'); + } + + function testPluginInstanceDerivativeFetching() { + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer:stout'); + $plugin = $this->TestPluginType->createPluginInstance('beer:stout', $plugin_definition); + $this->assertEqual($plugin->string(), 'Might even carry your bags for you.', 'Plugin derivative instance 1 returns the proper string'); + + $plugin_definition = $this->TestPluginType->getPluginDefinition('beer:ipa'); + $plugin = $this->TestPluginType->createPluginInstance('beer:ipa', $plugin_definition); + $this->assertEqual($plugin->string(), 'Not just from the subcontinent anymore!', '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 ( + 'fruitpunch', + 'beer:stout', + 'beer:ipa', + 'beer', + 'wine:malbec', + 'wine:syrah', + ); + $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..3071471 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/PluginTestBase.php @@ -0,0 +1,33 @@ +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( + 'stout' => array( + 'string' => 'Might even carry your bags for you.', + ) + $base_plugin_definition, + 'ipa' => array( + 'string' => 'Not just from the subcontinent anymore!', + ) + $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/Derivative/WineDerivativeTest.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Derivative/WineDerivativeTest.php new file mode 100644 index 0000000..95f55d6 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Derivative/WineDerivativeTest.php @@ -0,0 +1,46 @@ +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( + 'malbec' => array( + 'string' => 'Argentina won\'t steer you wrong.', + ) + $base_plugin_definition, + 'syrah' => array( + 'string' => 'Nom nom, chocolatey tannins.', + ) + $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..3909b92 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginInstanceClass.php @@ -0,0 +1,25 @@ +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..298b7f1 --- /dev/null +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/Type/TestPluginType.php @@ -0,0 +1,40 @@ +discovery = new DerivativeDiscoveryDecorator(new StaticDiscovery()); + $this->discovery->setPluginDefinition('fruitpunch', array( + 'string' => 'Almost as good as grape drink.', + '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\BeerDerivativeTest', + 'use_base' => TRUE, + )); + $this->discovery->setPluginDefinition('wine', array( + 'string' => 'derivative test', + 'class' => 'Drupal\plugin_test\Plugin\TestPluginInstanceClass', + 'derivative' => 'Drupal\plugin_test\Plugin\Derivative\WineDerivativeTest', + )); + $this->factory = new ReflectionFactory($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 @@ +