Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ClassResolver.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
/**
* Implements the class resolver interface supporting class names and services.
*/
class ClassResolver implements ClassResolverInterface, ContainerAwareInterface {
use DependencySerializationTrait;
use ContainerAwareTrait;
/**
* {@inheritdoc}
*/
public function getInstanceFromDefinition($definition) {
if ($this->container->has($definition)) {
$instance = $this->container->get($definition);
}
else {
if (!class_exists($definition)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $definition));
}
if (is_subclass_of($definition, 'Drupal\Core\DependencyInjection\ContainerInjectionInterface')) {
$instance = $definition::create($this->container);
}
else {
$instance = new $definition();
}
}
if ($instance instanceof ContainerAwareInterface) {
$instance->setContainer($this->container);
}
return $instance;
}
}

View file

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ClassResolverInterface.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Provides an interface to get a instance of a class with dependency injection.
*/
interface ClassResolverInterface {
/**
* Returns a class instance with a given class definition.
*
* In contrast to controllers you don't specify a method.
*
* @param string $definition
* A class name or service name.
*
* @throws \InvalidArgumentException
* If $class is not a valid service identifier and the class does not exist.
*
* @return object
* The instance of the class.
*/
public function getInstanceFromDefinition($definition);
}

View file

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Defines a compiler pass to allow automatic override per backend.
*
* A module developer has to tag his backend service with "backend_overridable":
* @code
* custom_service:
* class: ...
* tags:
* - { name: backend_overridable }
* @endcode
*
* As a site admin you set the 'default_backend' in your services.yml file:
* @code
* parameters:
* default_backend: sqlite
* @endcode
*
* As a developer for alternative storage engines you register a service with
* $yourbackend.$original_service:
*
* @code
* sqlite.custom_service:
* class: ...
* @endcode
*/
class BackendCompilerPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
$default_backend = $container->hasParameter('default_backend') ? $container->getParameter('default_backend') : NULL;
// No default backend was configured, so continue as normal.
if (!isset($default_backend)) {
return;
}
foreach ($container->findTaggedServiceIds('backend_overridable') as $id => $attributes) {
// If the service is already an alias it is not the original backend, so
// we don't want to fallback to other storages any longer.
if ($container->hasAlias($id)) {
continue;
}
if ($container->hasDefinition("$default_backend.$id") || $container->hasAlias("$default_backend.$id")) {
$container->setAlias($id, new Alias("$default_backend.$id"));
}
}
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\DependencySerializationTraitPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Sets the _serviceId property on all services.
*
* @see \Drupal\Core\DependencyInjection\DependencySerializationTrait
*/
class DependencySerializationTraitPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
foreach ($container->getDefinitions() as $service_id => $definition) {
// Only add the property to services that are public (as private services
// can not be reloaded through Container::get()) and are objects.
if (!$definition->hasTag('parameter_service') && $definition->isPublic()) {
$definition->setProperty('_serviceId', $service_id);
}
}
}
}

View file

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Drupal\Core\DrupalKernelInterface;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Passes the container to the alter() method of all service providers.
*/
class ModifyServiceDefinitionsPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->has('kernel')) {
return;
}
$kernel = $container->get('kernel');
if (!($kernel instanceof DrupalKernelInterface)) {
return;
}
$providers = $kernel->getServiceProviders('app');
foreach ($providers as $provider) {
if ($provider instanceof ServiceModifierInterface) {
$provider->alter($container);
}
}
$providers = $kernel->getServiceProviders('site');
foreach ($providers as $provider) {
if ($provider instanceof ServiceModifierInterface) {
$provider->alter($container);
}
}
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged 'access_check' to the access_manager service.
*/
class RegisterAccessChecksPass implements CompilerPassInterface {
/**
* Implements CompilerPassInterface::process().
*
* Adds services tagged 'access_check' to the access_manager service.
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('access_manager')) {
return;
}
$access_manager = $container->getDefinition('access_manager.check_provider');
foreach ($container->findTaggedServiceIds('access_check') as $id => $attributes) {
$applies = array();
$method = 'access';
$needs_incoming_request = FALSE;
foreach ($attributes as $attribute) {
if (isset($attribute['applies_to'])) {
$applies[] = $attribute['applies_to'];
}
if (isset($attribute['method'])) {
$method = $attribute['method'];
}
if (!empty($attribute['needs_incoming_request'])) {
$needs_incoming_request = TRUE;
}
}
$access_manager->addMethodCall('addCheckService', array($id, $method, $applies, $needs_incoming_request));
}
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class RegisterKernelListenersPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('event_dispatcher')) {
return;
}
$definition = $container->getDefinition('event_dispatcher');
$event_subscriber_info = [];
foreach ($container->findTaggedServiceIds('event_subscriber') as $id => $attributes) {
// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $container->getDefinition($id)->getClass();
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
if (!$refClass->implementsInterface($interface)) {
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}
// Get all subscribed events.
foreach ($class::getSubscribedEvents() as $event_name => $params) {
if (is_string($params)) {
$priority = 0;
$event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params]];
}
elseif (is_string($params[0])) {
$priority = isset($params[1]) ? $params[1] : 0;
$event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params[0]]];
}
else {
foreach ($params as $listener) {
$priority = isset($listener[1]) ? $listener[1] : 0;
$event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $listener[0]]];
}
}
}
}
foreach (array_keys($event_subscriber_info) as $event_name) {
krsort($event_subscriber_info[$event_name]);
}
$definition->addArgument($event_subscriber_info);
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route enhancers onto the lazy route enhancers.
*/
class RegisterLazyRouteEnhancers implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_enhancer.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_enhancer') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_enhancer.lazy_collector')
->addArgument($service_ids);
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route filters onto the lazy route filter.
*/
class RegisterLazyRouteFilters implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_filter.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_filter') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_filter.lazy_collector')
->addArgument($service_ids);
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged "needs_destruction" to the "kernel_destruct_subscriber"
* service.
*
* @see \Drupal\Core\DestructableInterface
*/
class RegisterServicesForDestructionPass implements CompilerPassInterface {
/**
* Implements \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process().
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('kernel_destruct_subscriber')) {
return;
}
$definition = $container->getDefinition('kernel_destruct_subscriber');
$services = $container->findTaggedServiceIds('needs_destruction');
foreach ($services as $id => $attributes) {
$definition->addMethodCall('registerService', array($id));
}
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Adds services tagged 'stream_wrapper' to the stream_wrapper_manager service.
*/
class RegisterStreamWrappersPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('stream_wrapper_manager')) {
return;
}
$stream_wrapper_manager = $container->getDefinition('stream_wrapper_manager');
foreach ($container->findTaggedServiceIds('stream_wrapper') as $id => $attributes) {
$class = $container->getDefinition($id)->getClass();
$scheme = $attributes[0]['scheme'];
$stream_wrapper_manager->addMethodCall('addStreamWrapper', array($id, $class, $scheme));
}
}
}

View file

@ -0,0 +1,114 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\StackedKernelPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Provides a compiler pass for stacked HTTP kernels.
*
* Builds the HTTP kernel by collecting all services tagged 'http_middleware'
* and assembling them into a StackedKernel. The middleware with the lowest
* priority ends up as the outermost while the highest priority middleware
* wraps the actual HTTP kernel defined by the http_kernel.basic service.
*
* The 'http_middleware' service tag additionally accepts a 'responder'
* parameter. It should be set to TRUE if many or most requests will be handled
* directly by the middleware. Any underlying middleware and the HTTP kernel are
* then flagged as 'lazy'. As a result those low priority services and their
* dependencies are only initialized if the 'responder' middleware fails to
* generate a response and the request is delegated to the underlying kernel.
*
* In general middlewares should not have heavy dependencies. This is especially
* important for high-priority services which need to run before the internal
* page cache.
*
* An example of a high priority middleware.
* @code
* http_middleware.reverse_proxy:
* class: Drupal\Core\StackMiddleware\ReverseProxyMiddleware
* arguments: ['@settings']
* tags:
* - { name: http_middleware, priority: 300 }
* @endcode
*
* An example of a responder middleware:
* @code
* http_middleware.page_cache:
* class: Drupal\page_cache\StackMiddleware\PageCache
* arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy']
* tags:
* - { name: http_middleware, priority: 200, responder: true }
* @endcode
*
* @see \Stack\Builder
*/
class StackedKernelPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('http_kernel')) {
return;
}
$stacked_kernel = $container->getDefinition('http_kernel');
// Return now if this is not a stacked kernel.
if ($stacked_kernel->getClass() !== 'Stack\StackedHttpKernel') {
return;
}
$middlewares = [];
$priorities = [];
$responders = [];
foreach ($container->findTaggedServiceIds('http_middleware') as $id => $attributes) {
$priorities[$id] = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$middlewares[$id] = $container->getDefinition($id);
$responders[$id] = !empty($attributes[0]['responder']);
}
array_multisort($priorities, SORT_ASC, $middlewares, $responders);
$decorated_id = 'http_kernel.basic';
$middlewares_param = [new Reference($decorated_id)];
$first_responder = array_search(TRUE, array_reverse($responders, TRUE), TRUE);
if ($first_responder) {
$container->getDefinition($decorated_id)->setLazy(TRUE);
}
foreach ($middlewares as $id => $decorator) {
// Prepend a reference to the middlewares container parameter.
array_unshift($middlewares_param, new Reference($id));
// Prepend the inner kernel as first constructor argument.
$arguments = $decorator->getArguments();
array_unshift($arguments, new Reference($decorated_id));
$decorator->setArguments($arguments);
if ($first_responder === $id) {
$first_responder = FALSE;
}
elseif ($first_responder) {
$decorator->setLazy(TRUE);
}
$decorated_id = $id;
}
$arguments = [$middlewares_param[0], $middlewares_param];
$stacked_kernel->setArguments($arguments);
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Provides a compiler pass for stacked session save handlers.
*/
class StackedSessionHandlerPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if ($container->hasDefinition('session_handler')) {
return;
}
$session_handler_proxies = [];
$priorities = [];
foreach ($container->findTaggedServiceIds('session_handler_proxy') as $id => $attributes) {
$priorities[$id] = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$session_handler_proxies[$id] = $container->getDefinition($id);
}
array_multisort($priorities, SORT_ASC, $session_handler_proxies);
$decorated_id = 'session_handler.storage';
foreach ($session_handler_proxies as $id => $decorator) {
// Prepend the inner session handler as first constructor argument.
$arguments = $decorator->getArguments();
array_unshift($arguments, new Reference($decorated_id));
$decorator->setArguments($arguments);
$decorated_id = $id;
}
$container->setAlias('session_handler', $decorated_id);
}
}

View file

@ -0,0 +1,171 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Collects services to add/inject them into a consumer service.
*
* This mechanism allows a service to get multiple processor services injected,
* in order to establish an extensible architecture.
*
* It differs from the factory pattern in that processors are not lazily
* instantiated on demand; the consuming service receives instances of all
* registered processors when it is instantiated. Unlike a factory service, the
* consuming service is not ContainerAware.
*
* It differs from plugins in that all processors are explicitly registered by
* service providers (driven by declarative configuration in code); the mere
* availability of a processor (cf. plugin discovery) does not imply that a
* processor ought to be registered and used.
*
* It differs from regular service definition arguments (constructor injection)
* in that a consuming service MAY allow further processors to be added
* dynamically at runtime. This is why the called method (optionally) receives
* the priority of a processor as second argument.
*
* @see \Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass::process()
*/
class TaggedHandlersPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*
* Finds services tagged with 'service_collector', then finds all
* corresponding tagged services and adds a method call for each to the
* consuming/collecting service definition.
*
* Supported 'service_collector' tag attributes:
* - tag: The tag name used by handler services to collect. Defaults to the
* service ID of the consumer.
* - call: The method name to call on the consumer service. Defaults to
* 'addHandler'. The called method receives two arguments:
* - The handler instance as first argument.
* - Optionally the handler's priority as second argument, if the method
* accepts a second parameter and its name is "priority". In any case, all
* handlers registered at compile time are sorted already.
* - required: Boolean indicating if at least one handler service is required.
* Defaults to FALSE.
*
* Example (YAML):
* @code
* tags:
* - { name: service_collector, tag: breadcrumb_builder, call: addBuilder }
* @endcode
*
* Supported handler tag attributes:
* - priority: An integer denoting the priority of the handler. Defaults to 0.
*
* Example (YAML):
* @code
* tags:
* - { name: breadcrumb_builder, priority: 100 }
* @endcode
*
* @throws \Symfony\Component\DependencyInjection\Exception\LogicException
* If the method of a consumer service to be called does not type-hint an
* interface.
* @throws \Symfony\Component\DependencyInjection\Exception\LogicException
* If a tagged handler does not implement the required interface.
* @throws \Symfony\Component\DependencyInjection\Exception\LogicException
* If at least one tagged service is required but none are found.
*/
public function process(ContainerBuilder $container) {
foreach ($container->findTaggedServiceIds('service_collector') as $consumer_id => $passes) {
foreach ($passes as $pass) {
$tag = isset($pass['tag']) ? $pass['tag'] : $consumer_id;
$method_name = isset($pass['call']) ? $pass['call'] : 'addHandler';
$required = isset($pass['required']) ? $pass['required'] : FALSE;
// Determine parameters.
$consumer = $container->getDefinition($consumer_id);
$method = new \ReflectionMethod($consumer->getClass(), $method_name);
$params = $method->getParameters();
$interface_pos = 0;
$id_pos = NULL;
$priority_pos = NULL;
$extra_params = [];
foreach ($params as $pos => $param) {
if ($param->getClass()) {
$interface = $param->getClass();
}
else if ($param->getName() === 'id') {
$id_pos = $pos;
}
else if ($param->getName() === 'priority') {
$priority_pos = $pos;
}
else {
$extra_params[$param->getName()] = $pos;
}
}
// Determine the ID.
if (!isset($interface)) {
throw new LogicException(vsprintf("Service consumer '%s' class method %s::%s() has to type-hint an interface.", array(
$consumer_id,
$consumer->getClass(),
$method_name,
)));
}
$interface = $interface->getName();
// Find all tagged handlers.
$handlers = array();
$extra_arguments = array();
foreach ($container->findTaggedServiceIds($tag) as $id => $attributes) {
// Validate the interface.
$handler = $container->getDefinition($id);
if (!is_subclass_of($handler->getClass(), $interface)) {
throw new LogicException("Service '$id' for consumer '$consumer_id' does not implement $interface.");
}
$handlers[$id] = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
// Keep track of other tagged handlers arguments.
foreach ($extra_params as $name => $pos) {
$extra_arguments[$id][$pos] = isset($attributes[0][$name]) ? $attributes[0][$name] : $params[$pos]->getDefaultValue();
}
}
if (empty($handlers)) {
if ($required) {
throw new LogicException(sprintf("At least one service tagged with '%s' is required.", $tag));
}
continue;
}
// Sort all handlers by priority.
arsort($handlers, SORT_NUMERIC);
// Add a method call for each handler to the consumer service
// definition.
foreach ($handlers as $id => $priority) {
$arguments = array();
$arguments[$interface_pos] = new Reference($id);
if (isset($priority_pos)) {
$arguments[$priority_pos] = $priority;
}
if (isset($id_pos)) {
$arguments[$id_pos] = $id;
}
// Add in extra arguments.
if (isset($extra_arguments[$id])) {
// Place extra arguments in their right positions.
$arguments += $extra_arguments[$id];
}
// Sort the arguments by position.
ksort($arguments);
$consumer->addMethodCall($method_name, $arguments);
}
}
}
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Container.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
/**
* Extends the symfony container to set the service ID on the created object.
*/
class Container extends SymfonyContainer {
/**
* {@inheritdoc}
*/
public function set($id, $service, $scope = SymfonyContainer::SCOPE_CONTAINER) {
parent::set($id, $service, $scope);
// Ensure that the _serviceId property is set on synthetic services as well.
if (isset($this->services[$id]) && is_object($this->services[$id]) && !isset($this->services[$id]->_serviceId)) {
$this->services[$id]->_serviceId = $id;
}
}
/**
* {@inheritdoc}
*/
public function __sleep() {
trigger_error('The container was serialized.', E_USER_ERROR);
return array_keys(get_object_vars($this));
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ContainerBuilder.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
/**
* Drupal's dependency injection container builder.
*
* @todo Submit upstream patches to Symfony to not require these overrides.
*
* @ingroup container
*/
class ContainerBuilder extends SymfonyContainerBuilder {
/**
* {@inheritdoc}
*/
public function __construct(ParameterBagInterface $parameterBag = NULL) {
$this->setResourceTracking(FALSE);
parent::__construct($parameterBag);
}
/**
* Overrides Symfony\Component\DependencyInjection\ContainerBuilder::set().
*
* Drupal's container builder can be used at runtime after compilation, so we
* override Symfony's ContainerBuilder's restriction on setting services in a
* frozen builder.
*
* @todo Restrict this to synthetic services only. Ideally, the upstream
* ContainerBuilder class should be fixed to allow setting synthetic
* services in a frozen builder.
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER) {
if (strtolower($id) !== $id) {
throw new \InvalidArgumentException("Service ID names must be lowercase: $id");
}
SymfonyContainer::set($id, $service, $scope);
// Ensure that the _serviceId property is set on synthetic services as well.
if (isset($this->services[$id]) && is_object($this->services[$id]) && !isset($this->services[$id]->_serviceId)) {
$this->services[$id]->_serviceId = $id;
}
}
/**
* {@inheritdoc}
*/
public function register($id, $class = null) {
if (strtolower($id) !== $id) {
throw new \InvalidArgumentException("Service ID names must be lowercase: $id");
}
return parent::register($id, $class);
}
/**
* {@inheritdoc}
*/
public function setParameter($name, $value) {
if (strtolower($name) !== $name) {
throw new \InvalidArgumentException("Parameter names must be lowercase: $name");
}
parent::setParameter($name, $value);
}
/**
* Synchronizes a service change.
*
* This method is a copy of the ContainerBuilder of symfony.
*
* This method updates all services that depend on the given
* service by calling all methods referencing it.
*
* @param string $id A service id
*/
private function synchronize($id) {
foreach ($this->getDefinitions() as $definitionId => $definition) {
// only check initialized services
if (!$this->initialized($definitionId)) {
continue;
}
foreach ($definition->getMethodCalls() as $call) {
foreach ($call[1] as $argument) {
if ($argument instanceof Reference && $id == (string) $argument) {
$this->callMethod($this->get($definitionId), $call);
}
}
}
}
}
/**
* A 1to1 copy of parent::callMethod.
*/
protected function callMethod($service, $call) {
$services = self::getServiceConditionals($call[1]);
foreach ($services as $s) {
if (!$this->has($s)) {
return;
}
}
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1])));
}
/**
* {@inheritdoc}
*/
public function __sleep() {
trigger_error('The container was serialized.', E_USER_ERROR);
return array_keys(get_object_vars($this));
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ContainerInjectionInterface.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a common interface for dependency container injection.
*
* This interface gives classes who need services a factory method for
* instantiation rather than defining a new service.
*/
interface ContainerInjectionInterface {
/**
* Instantiates a new instance of this class.
*
* This is a factory method that returns a new instance of this class. The
* factory should pass any needed dependencies into the constructor of this
* class, but not the container itself. Every call to this method must return
* a new instance of this class; that is, it may not implement a singleton.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The service container this instance should use.
*/
public static function create(ContainerInterface $container);
}

View file

@ -0,0 +1,18 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ContainerNotInitializedException.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Exception thrown when a method is called that requires a container, but the
* container is not initialized yet.
*
* @see \Drupal
*/
class ContainerNotInitializedException extends \RuntimeException {
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\DependencySerializationTrait.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides dependency injection friendly methods for serialization.
*/
trait DependencySerializationTrait {
/**
* An array of service IDs keyed by property name used for serialization.
*
* @var array
*/
protected $_serviceIds = array();
/**
* {@inheritdoc}
*/
public function __sleep() {
$this->_serviceIds = array();
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value) && isset($value->_serviceId)) {
// If a class member was instantiated by the dependency injection
// container, only store its ID so it can be used to get a fresh object
// on unserialization.
$this->_serviceIds[$key] = $value->_serviceId;
unset($vars[$key]);
}
// Special case the container, which might not have a service ID.
elseif ($value instanceof ContainerInterface) {
$this->_serviceIds[$key] = 'service_container';
unset($vars[$key]);
}
}
return array_keys($vars);
}
/**
* {@inheritdoc}
*/
public function __wakeup() {
$container = \Drupal::getContainer();
foreach ($this->_serviceIds as $key => $service_id) {
$this->$key = $container->get($service_id);
}
$this->_serviceIds = array();
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ServiceModifierInterface.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Interface that service providers can implement to modify services.
*
* @ingroup container
*/
interface ServiceModifierInterface {
/**
* Modifies existing service definitions.
*
* @param ContainerBuilder $container
* The ContainerBuilder whose service definitions can be altered.
*/
public function alter(ContainerBuilder $container);
}

View file

@ -0,0 +1,29 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ServiceProviderBase.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Base service provider implementation.
*
* @ingroup container
*/
abstract class ServiceProviderBase implements ServiceProviderInterface, ServiceModifierInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
}
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\ServiceProviderInterface.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Interface that all service providers must implement.
*
* @ingroup container
*/
interface ServiceProviderInterface {
/**
* Registers services to the container.
*
* @param ContainerBuilder $container
* The ContainerBuilder to register services to.
*/
public function register(ContainerBuilder $container);
}

View file

@ -0,0 +1,375 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\YamlFileLoader.
*/
namespace Drupal\Core\DependencyInjection;
use Drupal\Component\FileCache\FileCacheFactory;
use Drupal\Component\Serialization\Yaml;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* YamlFileLoader loads YAML files service definitions.
*
* Drupal does not use Symfony's Config component, and Symfony's dependency on
* it cannot be removed easily. Therefore, this is a partial but mostly literal
* copy of upstream, which does not depend on the Config component.
*
* @see \Symfony\Component\DependencyInjection\Loader\YamlFileLoader
* @see https://github.com/symfony/symfony/pull/10920
*
* NOTE: 98% of this code is a literal copy of Symfony's YamlFileLoader.
*
* This file does NOT follow Drupal coding standards, so as to simplify future
* synchronizations.
*/
class YamlFileLoader
{
/**
* @var \Drupal\Core\DependencyInjection\ContainerBuilder $container
*/
protected $container;
/**
* File cache object.
*
* @var \Drupal\Component\FileCache\FileCacheInterface
*/
protected $fileCache;
public function __construct(ContainerBuilder $container)
{
$this->container = $container;
$this->fileCache = FileCacheFactory::get('container_yaml_loader');
}
/**
* Loads a Yaml file.
*
* @param mixed $file The resource
*/
public function load($file)
{
// Load from the file cache, fall back to loading the file.
// @todo Refactor this to cache parsed definition objects in
// https://www.drupal.org/node/2464053
$content = $this->fileCache->get($file);
if (!$content) {
$content = $this->loadFile($file);
$this->fileCache->set($file, $content);
}
// Not supported.
//$this->container->addResource(new FileResource($path));
// empty file
if (null === $content) {
return;
}
// imports
// Not supported.
//$this->parseImports($content, $file);
// parameters
if (isset($content['parameters'])) {
if (!is_array($content['parameters'])) {
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['parameters'] as $key => $value) {
$this->container->setParameter($key, $this->resolveServices($value));
}
}
// extensions
// Not supported.
//$this->loadFromExtensions($content);
// services
$this->parseDefinitions($content, $file);
}
/**
* Parses definitions
*
* @param array $content
* @param string $file
*/
private function parseDefinitions($content, $file)
{
if (!isset($content['services'])) {
return;
}
if (!is_array($content['services'])) {
throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['services'] as $id => $service) {
$this->parseDefinition($id, $service, $file);
}
}
/**
* Parses a definition.
*
* @param string $id
* @param array $service
* @param string $file
*
* @throws InvalidArgumentException When tags are invalid
*/
private function parseDefinition($id, $service, $file)
{
if (is_string($service) && 0 === strpos($service, '@')) {
$this->container->setAlias($id, substr($service, 1));
return;
}
if (!is_array($service)) {
throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
}
if (isset($service['alias'])) {
$public = !array_key_exists('public', $service) || (bool) $service['public'];
$this->container->setAlias($id, new Alias($service['alias'], $public));
return;
}
if (isset($service['parent'])) {
$definition = new DefinitionDecorator($service['parent']);
} else {
$definition = new Definition();
}
if (isset($service['class'])) {
$definition->setClass($service['class']);
}
if (isset($service['scope'])) {
$definition->setScope($service['scope']);
}
if (isset($service['synthetic'])) {
$definition->setSynthetic($service['synthetic']);
}
if (isset($service['synchronized'])) {
$definition->setSynchronized($service['synchronized'], 'request' !== $id);
}
if (isset($service['lazy'])) {
$definition->setLazy($service['lazy']);
}
if (isset($service['public'])) {
$definition->setPublic($service['public']);
}
if (isset($service['abstract'])) {
$definition->setAbstract($service['abstract']);
}
if (isset($service['factory'])) {
if (is_string($service['factory'])) {
if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) {
$parts = explode(':', $service['factory']);
$definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1]));
} else {
$definition->setFactory($service['factory']);
}
} else {
$definition->setFactory(array($this->resolveServices($service['factory'][0]), $service['factory'][1]));
}
}
if (isset($service['factory_class'])) {
$definition->setFactoryClass($service['factory_class']);
}
if (isset($service['factory_method'])) {
$definition->setFactoryMethod($service['factory_method']);
}
if (isset($service['factory_service'])) {
$definition->setFactoryService($service['factory_service']);
}
if (isset($service['file'])) {
$definition->setFile($service['file']);
}
if (isset($service['arguments'])) {
$definition->setArguments($this->resolveServices($service['arguments']));
}
if (isset($service['properties'])) {
$definition->setProperties($this->resolveServices($service['properties']));
}
if (isset($service['configurator'])) {
if (is_string($service['configurator'])) {
$definition->setConfigurator($service['configurator']);
} else {
$definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
}
}
if (isset($service['calls'])) {
if (!is_array($service['calls'])) {
throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['calls'] as $call) {
if (isset($call['method'])) {
$method = $call['method'];
$args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array();
} else {
$method = $call[0];
$args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
}
$definition->addMethodCall($method, $args);
}
}
if (isset($service['tags'])) {
if (!is_array($service['tags'])) {
throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['tags'] as $tag) {
if (!is_array($tag)) {
throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
if (!isset($tag['name'])) {
throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
}
$name = $tag['name'];
unset($tag['name']);
foreach ($tag as $attribute => $value) {
if (!is_scalar($value) && null !== $value) {
throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file));
}
}
$definition->addTag($name, $tag);
}
}
if (isset($service['decorates'])) {
$renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
$definition->setDecoratedService($service['decorates'], $renameId);
}
$this->container->setDefinition($id, $definition);
}
/**
* Loads a YAML file.
*
* @param string $file
*
* @return array The file content
*
* @throws InvalidArgumentException when the given file is not a local file or when it does not exist
*/
protected function loadFile($file)
{
if (!stream_is_local($file)) {
throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
}
if (!file_exists($file)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
}
return $this->validate(Yaml::decode(file_get_contents($file)), $file);
}
/**
* Validates a YAML file.
*
* @param mixed $content
* @param string $file
*
* @return array
*
* @throws InvalidArgumentException When service file is not valid
*/
private function validate($content, $file)
{
if (null === $content) {
return $content;
}
if (!is_array($content)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
}
if ($invalid_keys = array_diff_key($content, array('parameters' => 1, 'services' => 1))) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid: it contains invalid keys %s. Services have to be added under "services" and Parameters under "parameters".', $file, $invalid_keys));
}
return $content;
}
/**
* Resolves services.
*
* @param string|array $value
*
* @return array|string|Reference
*/
private function resolveServices($value)
{
if (is_array($value)) {
$value = array_map(array($this, 'resolveServices'), $value);
} elseif (is_string($value) && 0 === strpos($value, '@=')) {
// Not supported.
//return new Expression(substr($value, 2));
throw new InvalidArgumentException(sprintf("'%s' is an Expression, but expressions are not supported.", $value));
} elseif (is_string($value) && 0 === strpos($value, '@')) {
if (0 === strpos($value, '@@')) {
$value = substr($value, 1);
$invalidBehavior = null;
} elseif (0 === strpos($value, '@?')) {
$value = substr($value, 2);
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
} else {
$value = substr($value, 1);
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
}
if ('=' === substr($value, -1)) {
$value = substr($value, 0, -1);
$strict = false;
} else {
$strict = true;
}
if (null !== $invalidBehavior) {
$value = new Reference($value, $invalidBehavior, $strict);
}
}
return $value;
}
}