Commit 5a5cdc70 authored by Dries's avatar Dries

- Patch #1497366 by neclimdul, effulgentsia, EclipseGc, merlinofchaos:...

- Patch #1497366 by neclimdul, effulgentsia, EclipseGc, merlinofchaos: introduce Plugin System to core.
parent 3cae919f
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Derivative\DerivativeInterface.
*/
namespace Drupal\Component\Plugin\Derivative;
/**
* Plugin interface for derivative plugin handling.
*/
interface DerivativeInterface {
/**
* Returns the definition of a derivative plugin.
*
* @param string $derivative_id
* The derivative id. The id must uniquely identify the derivative within a
* given base plugin, but derivative ids can be reused across base plugins.
* @param array $base_plugin_definition
* The definition array of the base plugin from which the derivative plugin
* is derived.
*
* @return array
* The full definition array of the derivative plugin, typically a merge of
* $base_plugin_definition with extra derivative-specific information. NULL
* if the derivative doesn't exist.
*/
public function getDerivativeDefinition($derivative_id, array $base_plugin_definition);
/**
* Returns the definition of all derivatives of a base plugin.
*
* @param array $base_plugin_definition
* The definition array of the base plugin.
* @return array
* An array of full derivative definitions keyed on derivative id.
*
* @see getDerivativeDefinition()
*/
public function getDerivativeDefinitions(array $base_plugin_definition);
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator.
*/
namespace Drupal\Component\Plugin\Discovery;
/**
* Base class providing the tools for a plugin discovery to be derivative aware.
*
* Provides a decorator that allows the use of plugin derivatives for normal
* implementations DiscoveryInterface.
*/
class DerivativeDiscoveryDecorator implements DiscoveryInterface {
protected $derivativeFetchers = array();
protected $decorated;
/**
* Creates a Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator
* object.
*
* @param DiscoveryInterface $discovery
* The parent object implementing DiscoveryInterface that is being
* decorated.
*/
public function __construct(DiscoveryInterface $decorated) {
$this->decorated = $decorated;
}
/**
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
*/
public function getDefinition($plugin_id) {
list($base_plugin_id, $derivative_id) = $this->decodePluginId($plugin_id);
$plugin_definition = $this->decorated->getDefinition($base_plugin_id);
if (isset($plugin_definition)) {
$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::getDefinitions().
*/
public function getDefinitions() {
$plugin_definitions = $this->decorated->getDefinitions();
return $this->getDerivatives($plugin_definitions);
}
/**
* Adds derivatives to a list of plugin definitions.
*
* This should be called by the class extending this in
* DiscoveryInterface::getDefinitions().
*/
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);
}
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Discovery\DiscoveryInterface.
*/
namespace Drupal\Component\Plugin\Discovery;
/**
* An interface defining the minimum requirements of building a plugin
* discovery component.
*/
interface DiscoveryInterface {
/**
* Gets a specific plugin definition.
*
* @param string $plugin_id
* A plugin id.
*
* @return array
* A plugin definition.
*/
public function getDefinition($plugin_id);
/**
* Gets the definition of all plugins for this type.
*
* @return array
* An array of plugin definitions.
*/
public function getDefinitions();
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Discovery\StaticDiscovery.
*/
namespace Drupal\Component\Plugin\Discovery;
/**
* A discovery mechanism that allows plugin definitions to be manually
* registered rather than actively discovered.
*/
class StaticDiscovery implements DiscoveryInterface {
/**
* The array of plugin definitions, keyed by plugin id.
*
* @var array
*/
protected $definitions = array();
/**
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
*/
public function getDefinition($base_plugin_id) {
return isset($this->definitions[$base_plugin_id]) ? $this->definitions[$base_plugin_id] : NULL;
}
/**
* Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
*/
public function getDefinitions() {
return $this->definitions;
}
/**
* Sets a plugin definition.
*/
public function setDefinition($plugin, array $definition) {
$this->definitions[$plugin] = $definition;
}
/**
* Deletes a plugin definition.
*/
public function deleteDefinition($plugin) {
unset($this->definitions[$plugin]);
}
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Exception\ExceptionInterface.
*/
namespace Drupal\Component\Plugin\Exception;
/**
* Exception interface for all exceptions thrown by the Plugin component.
*/
interface ExceptionInterface { }
<?php
/**
* @file
* Definition of Drupal\Core\Plugin\Exception\InvalidDecoratedMethod.
*/
namespace Drupal\Component\Plugin\Exception;
use Drupal\Component\Plugin\Exception\ExceptionInterface;
use \BadMethodCallException;
/**
* Exception thrown when a decorator's _call() method is triggered, but the
* decorated object does not contain the requested method.
*
*/
class InvalidDecoratedMethod extends BadMethodCallException implements ExceptionInterface { }
<?php
/**
* @file
* Base exception interface for grouping mapper exceptions.
*/
namespace Drupal\Component\Plugin\Exception;
/**
* Extended interface for exceptions thrown specifically by the Mapper subsystem
* within the Plugin component.
*/
interface MapperExceptionInterface extends ExceptionInterface { }
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Exception\PluginException.
*/
namespace Drupal\Component\Plugin\Exception;
use Exception;
/**
* Generic Plugin exception class to be thrown when no more specific class
* is applicable.
*/
class PluginException extends Exception implements ExceptionInterface { }
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Factory\DefaultFactory.
*/
namespace Drupal\Component\Plugin\Factory;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\Derivative\DerivativeInterface;
/**
* Default plugin factory.
*
* Instantiates plugin instances by passing the full configuration array as a
* single constructor argument. Plugin types wanting to support plugin classes
* with more flexible constructor signatures can do so by using an alternate
* factory such as Drupal\Component\Plugin\Factory\ReflectionFactory.
*/
class DefaultFactory implements FactoryInterface {
/**
* The object that retrieves the definitions of the plugins that this factory instantiates.
*
* The plugin definition includes the plugin class and possibly other
* information necessary for proper instantiation.
*
* @var Drupal\Component\Plugin\Discovery\DiscoveryInterface
*/
protected $discovery;
/**
* Constructs a Drupal\Component\Plugin\Factory\DefaultFactory object.
*/
public function __construct(DiscoveryInterface $discovery) {
$this->discovery = $discovery;
}
/**
* Implements Drupal\Component\Plugin\Factory\FactoryInterface::createInstance().
*/
public function createInstance($plugin_id, array $configuration) {
$plugin_class = $this->getPluginClass($plugin_id);
return new $plugin_class($configuration, $plugin_id, $this->discovery);
}
/**
* Finds the class relevant for a given plugin.
*
* @param array $plugin_id
* The id of a plugin.
*
* @return string
* The appropriate class name.
*/
protected function getPluginClass($plugin_id) {
$plugin_definition = $this->discovery->getDefinition($plugin_id);
if (empty($plugin_definition['class'])) {
throw new PluginException('The plugin did not specify an instance class.');
}
$class = $plugin_definition['class'];
if (!class_exists($class)) {
throw new PluginException(sprintf('Plugin instance class "%s" does not exist.', $class));
}
return $class;
}
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Factory\FactoryInterface.
*/
namespace Drupal\Component\Plugin\Factory;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
/**
* Factory interface implemented by all plugin factories.
*/
interface FactoryInterface {
/**
* Returns a preconfigured instance of a plugin.
*
* @param string $plugin_id
* The id of the plugin being instantiated.
* @param array $configuration
* An array of configuration relevant to the plugin instance.
*
* @return object
* A fully configured plugin instance.
*/
public function createInstance($plugin_id, array $configuration);
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Factory\ReflectionFactory.
*/
namespace Drupal\Component\Plugin\Factory;
use ReflectionClass;
/**
* A plugin factory that maps instance configuration to constructor arguments.
*
* Provides logic for any basic plugin type that needs to provide individual
* plugins based upon some basic logic.
*/
class ReflectionFactory extends DefaultFactory {
/**
* Implements Drupal\Component\Plugin\Factory\FactoryInterface::createInstance().
*/
public function createInstance($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->getInstanceArguments($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 getInstanceArguments(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 {
$arguments[] = NULL;
}
}
return $arguments;
}
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\Mapper\MapperInterface.
*/
namespace Drupal\Component\Plugin\Mapper;
/**
* Plugin mapper interface.
*
* Plugin mappers are responsible for mapping a plugin request to its
* implementation. For example, it might map a cache bin to a memcache bin.
*
* Mapper objects incorporate the best practices of retrieving configurations,
* type information, and factory instantiation.
*/
interface MapperInterface {
/**
* Returns a preconfigured instance of a plugin.
*
* @param array $options
* An array of options that can be used to determine a suitable plugin to
* instantiate and how to configure it.
*
* @return object
* A fully configured plugin instance. The interface of the plugin instance
* will depends on the plugin type.
*/
public function getInstance(array $options);
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\PluginBase
*/
namespace Drupal\Component\Plugin;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
/**
* Base class for plugins wishing to support metadata inspection.
*/
abstract class PluginBase implements PluginInspectionInterface {
/**
* The discovery object.
*
* @var Drupal\Component\Plugin\Discovery\DiscoveryInterface
*/
protected $discovery;
/**
* The plugin_id.
*
* @var string
*/
protected $plugin_id;
/**
* Configuration information passed into the plugin.
*
* @var array
*/
protected $configuration;
/**
* Constructs a Drupal\Component\Plugin\PluginBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param DiscoveryInterface $discovery
* The Discovery class that holds access to the plugin implementation
* definition.
*/
public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
$this->configuration = $configuration;
$this->plugin_id = $plugin_id;
$this->discovery = $discovery;
}
/**
* Implements Drupal\Component\Plugin\PluginInterface::getPluginId().
*/
public function getPluginId() {
return $this->plugin_id;
}
/**
* Implements Drupal\Component\Plugin\PluginInterface::getDefinition().
*/
public function getDefinition() {
return $this->discovery->getDefinition($this->plugin_id);
}
// Note: Plugin configuration is optional so its left to the plugin type to
// require a getter as part of its interface.
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\PluginInspectionInterface.
*/
namespace Drupal\Component\Plugin;
/**
* Plugin interface for providing some metadata inspection.
*
* This interface provides some simple tools for code recieving a plugin to
* interact with the plugin system.
*/
interface PluginInspectionInterface {
/**
* Returns the plugin_id of the plugin instance.
*
* @return string
* The plugin_id of the plugin instance.
*/
public function getPluginId();
/**
* Returns the definition of the plugin implementation.
*
* @return array
* The plugin definition, as returned by the discovery object used by the
* plugin manager.
*/
public function getDefinition();
}
<?php
/**
* @file
* Definition of Drupal\Component\Plugin\PluginManagerBase
*/
namespace Drupal\Component\Plugin;
/**
* Base class for plugin managers.
*/
abstract class PluginManagerBase implements PluginManagerInterface {
/**
* The object that discovers plugins managed by this manager.
*
* @var Drupal\Component\Plugin\Discovery\DiscoveryInterface
*/
protected $discovery;
/**
* The object that instantiates plugins managed by this manager.
*
* @var Drupal\Component\Plugin\Factory\FactoryInterface
*/
protected