Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
47
core/lib/Drupal/Core/DependencyInjection/ClassResolver.php
Normal file
47
core/lib/Drupal/Core/DependencyInjection/ClassResolver.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
core/lib/Drupal/Core/DependencyInjection/Container.php
Normal file
37
core/lib/Drupal/Core/DependencyInjection/Container.php
Normal 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));
|
||||
}
|
||||
|
||||
}
|
125
core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php
Normal file
125
core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php
Normal 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
375
core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
Normal file
375
core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue