feature #21708 [DI] Add and wire ServiceSubscriberInterface - aka explicit service locators (nicolas-grekas)
This PR was squashed before being merged into the 3.3-dev branch (closes #21708). Discussion ---------- [DI] Add and wire ServiceSubscriberInterface - aka explicit service locators | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | no test yet | Fixed tickets | #20658 | License | MIT | Doc PR | - This PR implements the second and missing part of #20658: it enables objects to declare their service dependencies in a similar way than we do for EventSubscribers: via a static method. Here is the interface and its current description: ```php namespace Symfony\Component\DependencyInjection; /** * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. * * The getSubscribedServices method returns an array of service types required by such instances, * optionally keyed by the service names used internally. Service types that start with an interrogation * mark "?" are optional, while the other ones are mandatory service dependencies. * * The injected service locators SHOULD NOT allow access to any other services not specified by the method. * * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. * This interface does not dictate any injection method for these service locators, although constructor * injection is recommended. * * @author Nicolas Grekas <p@tchwork.com> */ interface ServiceSubscriberInterface { /** * Returns an array of service types required by such instances, optionally keyed by the service names used internally. * * For mandatory dependencies: * * * array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name * internally to fetch a service which must implement Psr\Log\LoggerInterface. * * array('Psr\Log\LoggerInterface') is a shortcut for * * array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface') * * otherwise: * * * array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency * * array('?Psr\Log\LoggerInterface') is a shortcut for * * array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface') * * @return array The required service types, optionally keyed by service names */ public static function getSubscribedServices(); } ``` We could then have eg a controller-as-a-service implement this interface, and be auto or manually wired according to the return value of this method - using the "kernel.service_subscriber" tag to do so. eg: ```yaml services: App\Controller\FooController: arguments: [service_container] tags: - name: kernel.service_subscriber key: logger service: monolog.logger.foo_channel ``` The benefits are: - it keeps the lazy-behavior gained by service locators / container injection - it allows the referenced services to be made private from the pov of the main Symfony DIC - thus enables some compiler optimizations - it makes dependencies autowirable (while keeping manual wiring possible) - it does not add any strong coupling at the architecture level - and most importantly and contrary to regular container injection, *it makes dependencies explicit* - each classes declaring which services it consumes. Some might argue that: - it requires to be explicit - thus more verbose. Yet many others think it's a good thing - ie it's worth it. - some coupling happens at the dependency level, since you need to get the DI component to get the interface definition. This is something that the PHP-FIG could address at some point. Commits -------c5e80a2b09
implement ServiceSubscriberInterface where applicable9b7df39865
[DI] Add and wire ServiceSubscriberInterface
This commit is contained in:
commit
f99dfb0204
@ -24,6 +24,7 @@ class UnusedTagsPass implements CompilerPassInterface
|
||||
private $whitelist = array(
|
||||
'console.command',
|
||||
'container.service_locator',
|
||||
'container.service_subscriber',
|
||||
'config_cache.resource_checker',
|
||||
'data_collector',
|
||||
'form.type',
|
||||
|
@ -53,14 +53,8 @@
|
||||
|
||||
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service">
|
||||
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
|
||||
<tag name="container.service_locator" />
|
||||
<argument type="collection">
|
||||
<argument key="session" type="service" id="session" on-invalid="ignore" />
|
||||
</argument>
|
||||
</service>
|
||||
</argument>
|
||||
<tag name="container.service_subscriber" id="session" />
|
||||
<argument type="service" id="container" />
|
||||
</service>
|
||||
|
||||
<service id="session.save_listener" class="Symfony\Component\HttpKernel\EventListener\SaveSessionListener">
|
||||
|
@ -22,14 +22,8 @@
|
||||
|
||||
<service id="test.session.listener" class="Symfony\Component\HttpKernel\EventListener\TestSessionListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service">
|
||||
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
|
||||
<tag name="container.service_locator" />
|
||||
<argument type="collection">
|
||||
<argument key="session" type="service" id="session" on-invalid="ignore" />
|
||||
</argument>
|
||||
</service>
|
||||
</argument>
|
||||
<tag name="container.service_subscriber" id="session" />
|
||||
<argument type="service" id="container" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing;
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\Config\ContainerParametersResource;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
use Symfony\Component\Routing\Router as BaseRouter;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
@ -25,7 +27,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Router extends BaseRouter implements WarmableInterface
|
||||
class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberInterface
|
||||
{
|
||||
private $container;
|
||||
private $collectedParameters = array();
|
||||
@ -173,4 +175,14 @@ class Router extends BaseRouter implements WarmableInterface
|
||||
|
||||
return str_replace('%%', '%', $escapedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array(
|
||||
'routing.loader' => LoaderInterface::class,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Bundle\TwigBundle\CacheWarmer;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface;
|
||||
@ -25,7 +26,7 @@ use Symfony\Component\Templating\TemplateReference;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TemplateCacheCacheWarmer implements CacheWarmerInterface
|
||||
class TemplateCacheCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface
|
||||
{
|
||||
protected $container;
|
||||
protected $finder;
|
||||
@ -92,6 +93,16 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array(
|
||||
'twig' => \Twig_Environment::class,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find templates in the given directory.
|
||||
*
|
||||
|
@ -28,7 +28,8 @@
|
||||
|
||||
<service id="twig.cache_warmer" class="Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer" public="false">
|
||||
<tag name="kernel.cache_warmer" />
|
||||
<argument type="service" id="service_container" />
|
||||
<tag name="container.service_subscriber" id="twig" />
|
||||
<argument type="service" id="container" />
|
||||
<argument type="service" id="templating.finder" on-invalid="ignore" />
|
||||
<argument type="collection" /> <!-- Twig paths -->
|
||||
</service>
|
||||
|
@ -4,6 +4,7 @@ CHANGELOG
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions
|
||||
* added "container.service_locator" tag for defining service-locator services
|
||||
* added anonymous services support in YAML configuration files using the `!service` tag.
|
||||
* added "TypedReference" and "ServiceClosureArgument" for creating service-locator services
|
||||
|
@ -55,6 +55,7 @@ class PassConfig
|
||||
new ResolveFactoryClassPass(),
|
||||
new FactoryReturnTypePass($resolveClassPass),
|
||||
new CheckDefinitionValidityPass(),
|
||||
new RegisterServiceSubscribersPass(),
|
||||
new ResolveNamedArgumentsPass(),
|
||||
new AutowirePass(),
|
||||
new ResolveReferencesToAliasesPass(),
|
||||
|
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\TypedReference;
|
||||
|
||||
/**
|
||||
* Compiler pass to register tagged services that require a service locator.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RegisterServiceSubscribersPass extends AbstractRecursivePass
|
||||
{
|
||||
private $serviceLocator;
|
||||
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
if ($value instanceof Reference && $this->serviceLocator && 'container' === (string) $value) {
|
||||
return new Reference($this->serviceLocator);
|
||||
}
|
||||
|
||||
if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
|
||||
$serviceMap = array();
|
||||
|
||||
foreach ($value->getTag('container.service_subscriber') as $attributes) {
|
||||
if (!$attributes) {
|
||||
continue;
|
||||
}
|
||||
ksort($attributes);
|
||||
if (array() !== array_diff(array_keys($attributes), array('id', 'key'))) {
|
||||
throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId));
|
||||
}
|
||||
if (!array_key_exists('id', $attributes)) {
|
||||
throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId));
|
||||
}
|
||||
if (!array_key_exists('key', $attributes)) {
|
||||
$attributes['key'] = $attributes['id'];
|
||||
}
|
||||
if (isset($serviceMap[$attributes['key']])) {
|
||||
continue;
|
||||
}
|
||||
$serviceMap[$attributes['key']] = new Reference($attributes['id']);
|
||||
}
|
||||
$class = $value->getClass();
|
||||
|
||||
if (!is_subclass_of($class, ServiceSubscriberInterface::class)) {
|
||||
if (!class_exists($class, false)) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $this->currentId));
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $this->currentId, ServiceSubscriberInterface::class));
|
||||
}
|
||||
$this->container->addObjectResource($class);
|
||||
$subscriberMap = array();
|
||||
|
||||
foreach ($class::getSubscribedServices() as $key => $type) {
|
||||
if (!is_string($type) || !preg_match('/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $type)) {
|
||||
throw new InvalidArgumentException(sprintf('%s::getSubscribedServices() must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, is_string($type) ? $type : gettype($type)));
|
||||
}
|
||||
if ($optionalBehavior = '?' === $type[0]) {
|
||||
$type = substr($type, 1);
|
||||
$optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
}
|
||||
if (is_int($key)) {
|
||||
$key = $type;
|
||||
}
|
||||
if (!isset($serviceMap[$key])) {
|
||||
$serviceMap[$key] = new Reference($type);
|
||||
}
|
||||
|
||||
$subscriberMap[$key] = new ServiceClosureArgument(new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE));
|
||||
unset($serviceMap[$key]);
|
||||
}
|
||||
|
||||
if ($serviceMap = array_keys($serviceMap)) {
|
||||
$this->container->log($this, sprintf('Service keys "%s" do not exist in the map returned by %s::getSubscribedServices() for service "%s".', implode('", "', $serviceMap), $class, $this->currentId));
|
||||
}
|
||||
|
||||
$serviceLocator = $this->serviceLocator;
|
||||
$this->serviceLocator = 'container.'.$this->currentId.'.'.md5(serialize($value));
|
||||
$this->container->register($this->serviceLocator, ServiceLocator::class)
|
||||
->addArgument($subscriberMap)
|
||||
->setPublic(false)
|
||||
->setAutowired($value->isAutowired())
|
||||
->addTag('container.service_locator');
|
||||
|
||||
try {
|
||||
return parent::processValue($value);
|
||||
} finally {
|
||||
$this->serviceLocator = $serviceLocator;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
/**
|
||||
* A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
|
||||
*
|
||||
* The getSubscribedServices method returns an array of service types required by such instances,
|
||||
* optionally keyed by the service names used internally. Service types that start with an interrogation
|
||||
* mark "?" are optional, while the other ones are mandatory service dependencies.
|
||||
*
|
||||
* The injected service locators SHOULD NOT allow access to any other services not specified by the method.
|
||||
*
|
||||
* It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally.
|
||||
* This interface does not dictate any injection method for these service locators, although constructor
|
||||
* injection is recommended.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface ServiceSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array of service types required by such instances, optionally keyed by the service names used internally.
|
||||
*
|
||||
* For mandatory dependencies:
|
||||
*
|
||||
* * array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name
|
||||
* internally to fetch a service which must implement Psr\Log\LoggerInterface.
|
||||
* * array('Psr\Log\LoggerInterface') is a shortcut for
|
||||
* * array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface')
|
||||
*
|
||||
* otherwise:
|
||||
*
|
||||
* * array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency
|
||||
* * array('?Psr\Log\LoggerInterface') is a shortcut for
|
||||
* * array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface')
|
||||
*
|
||||
* @return array The required service types, optionally keyed by service names
|
||||
*/
|
||||
public static function getSubscribedServices();
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\RegisterServiceSubscribersPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\TypedReference;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/includes/classes.php';
|
||||
|
||||
class RegisterServiceSubscribersPassTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Service "foo" must implement interface "Symfony\Component\DependencyInjection\ServiceSubscriberInterface".
|
||||
*/
|
||||
public function testInvalidClass()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', 'stdClass')
|
||||
->addTag('container.service_subscriber')
|
||||
;
|
||||
|
||||
$pass = new RegisterServiceSubscribersPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "bar" given for service "foo".
|
||||
*/
|
||||
public function testInvalidAttributes()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', 'TestServiceSubscriber')
|
||||
->addTag('container.service_subscriber', array('bar' => '123'))
|
||||
;
|
||||
|
||||
$pass = new RegisterServiceSubscribersPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testNoAttributes()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', 'TestServiceSubscriber')
|
||||
->addArgument(new Reference('container'))
|
||||
->addTag('container.service_subscriber')
|
||||
;
|
||||
|
||||
$pass = new RegisterServiceSubscribersPass();
|
||||
$pass->process($container);
|
||||
|
||||
$foo = $container->getDefinition('foo');
|
||||
$locator = $container->getDefinition((string) $foo->getArgument(0));
|
||||
|
||||
$this->assertFalse($locator->isAutowired());
|
||||
$this->assertFalse($locator->isPublic());
|
||||
$this->assertSame(ServiceLocator::class, $locator->getClass());
|
||||
|
||||
$expected = array(
|
||||
'TestServiceSubscriber' => new ServiceClosureArgument(new TypedReference('TestServiceSubscriber', 'TestServiceSubscriber')),
|
||||
'stdClass' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
|
||||
'bar' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass')),
|
||||
'baz' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $locator->getArgument(0));
|
||||
}
|
||||
|
||||
public function testWithAttributes()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('foo', 'TestServiceSubscriber')
|
||||
->setAutowired(true)
|
||||
->addArgument(new Reference('container'))
|
||||
->addTag('container.service_subscriber', array('key' => 'bar', 'id' => 'bar'))
|
||||
->addTag('container.service_subscriber', array('key' => 'bar', 'id' => 'baz')) // should be ignored: the first wins
|
||||
;
|
||||
|
||||
$pass = new RegisterServiceSubscribersPass();
|
||||
$pass->process($container);
|
||||
|
||||
$foo = $container->getDefinition('foo');
|
||||
$locator = $container->getDefinition((string) $foo->getArgument(0));
|
||||
|
||||
$this->assertTrue($locator->isAutowired());
|
||||
$this->assertFalse($locator->isPublic());
|
||||
$this->assertSame(ServiceLocator::class, $locator->getClass());
|
||||
|
||||
$expected = array(
|
||||
'TestServiceSubscriber' => new ServiceClosureArgument(new TypedReference('TestServiceSubscriber', 'TestServiceSubscriber')),
|
||||
'stdClass' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
|
||||
'bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass')),
|
||||
'baz' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $locator->getArgument(0));
|
||||
}
|
||||
}
|
@ -658,4 +658,23 @@ class PhpDumperTest extends TestCase
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator.php', $dumper->dump());
|
||||
}
|
||||
|
||||
public function testServiceSubscriber()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('foo_service', 'TestServiceSubscriber')
|
||||
->setAutowired(true)
|
||||
->addArgument(new Reference('container'))
|
||||
->addTag('container.service_subscriber', array(
|
||||
'key' => 'test',
|
||||
'id' => 'TestServiceSubscriber',
|
||||
))
|
||||
;
|
||||
$container->register('TestServiceSubscriber', 'TestServiceSubscriber');
|
||||
$container->compile();
|
||||
|
||||
$dumper = new PhpDumper($container);
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_subscriber.php', $dumper->dump());
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
|
||||
function sc_configure($instance)
|
||||
{
|
||||
@ -107,3 +108,20 @@ class LazyContext
|
||||
$this->lazyValues = $lazyValues;
|
||||
}
|
||||
}
|
||||
|
||||
class TestServiceSubscriber implements ServiceSubscriberInterface
|
||||
{
|
||||
public function __construct($container)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array(
|
||||
__CLASS__,
|
||||
'?stdClass',
|
||||
'bar' => 'stdClass',
|
||||
'baz' => '?stdClass',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* ProjectServiceContainer.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class ProjectServiceContainer extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->services = array();
|
||||
$this->normalizedIds = array(
|
||||
'autowired.stdclass' => 'autowired.stdClass',
|
||||
'psr\\container\\containerinterface' => 'Psr\\Container\\ContainerInterface',
|
||||
'stdclass' => 'stdClass',
|
||||
'symfony\\component\\dependencyinjection\\containerinterface' => 'Symfony\\Component\\DependencyInjection\\ContainerInterface',
|
||||
'testservicesubscriber' => 'TestServiceSubscriber',
|
||||
);
|
||||
$this->methodMap = array(
|
||||
'TestServiceSubscriber' => 'getTestServiceSubscriberService',
|
||||
'autowired.stdClass' => 'getAutowired_StdClassService',
|
||||
'foo_service' => 'getFooServiceService',
|
||||
);
|
||||
$this->privates = array(
|
||||
'autowired.stdClass' => true,
|
||||
);
|
||||
|
||||
$this->aliases = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
throw new LogicException('You cannot compile a dumped container that was already compiled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCompiled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFrozen()
|
||||
{
|
||||
@trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'TestServiceSubscriber' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return \TestServiceSubscriber A TestServiceSubscriber instance
|
||||
*/
|
||||
protected function getTestServiceSubscriberService()
|
||||
{
|
||||
return $this->services['TestServiceSubscriber'] = new \TestServiceSubscriber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'foo_service' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* This service is autowired.
|
||||
*
|
||||
* @return \TestServiceSubscriber A TestServiceSubscriber instance
|
||||
*/
|
||||
protected function getFooServiceService()
|
||||
{
|
||||
return $this->services['foo_service'] = new \TestServiceSubscriber(new \Symfony\Component\DependencyInjection\ServiceLocator(array('TestServiceSubscriber' => function () {
|
||||
$f = function (\TestServiceSubscriber $v) { return $v; }; return $f(${($_ = isset($this->services['TestServiceSubscriber']) ? $this->services['TestServiceSubscriber'] : $this->get('TestServiceSubscriber')) && false ?: '_'});
|
||||
}, 'stdClass' => function () {
|
||||
$f = function (\stdClass $v = null) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'});
|
||||
}, 'bar' => function () {
|
||||
$f = function (\stdClass $v) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'});
|
||||
}, 'baz' => function () {
|
||||
$f = function (\stdClass $v = null) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'});
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'autowired.stdClass' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* This service is private.
|
||||
* If you want to be able to request this service from the container directly,
|
||||
* make it public, otherwise you might end up with broken code.
|
||||
*
|
||||
* This service is autowired.
|
||||
*
|
||||
* @return \stdClass A stdClass instance
|
||||
*/
|
||||
protected function getAutowired_StdClassService()
|
||||
{
|
||||
return $this->services['autowired.stdClass'] = new \stdClass();
|
||||
}
|
||||
}
|
@ -8,8 +8,6 @@ CHANGELOG
|
||||
* Deprecated the special `SYMFONY__` environment variables
|
||||
* added the possibility to change the query string parameter used by `UriSigner`
|
||||
* deprecated `LazyLoadingFragmentHandler::addRendererService()`
|
||||
* added `SessionListener`
|
||||
* added `TestSessionListener`
|
||||
|
||||
3.2.0
|
||||
-----
|
||||
|
@ -12,6 +12,8 @@
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* Sets the session in the request.
|
||||
@ -20,7 +22,7 @@ use Psr\Container\ContainerInterface;
|
||||
*
|
||||
* @final since version 3.3
|
||||
*/
|
||||
class SessionListener extends AbstractSessionListener
|
||||
class SessionListener extends AbstractSessionListener implements ServiceSubscriberInterface
|
||||
{
|
||||
private $container;
|
||||
|
||||
@ -37,4 +39,14 @@ class SessionListener extends AbstractSessionListener
|
||||
|
||||
return $this->container->get('session');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array(
|
||||
'session' => '?'.SessionInterface::class,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* Sets the session in the request.
|
||||
@ -20,7 +22,7 @@ use Psr\Container\ContainerInterface;
|
||||
*
|
||||
* @final since version 3.3
|
||||
*/
|
||||
class TestSessionListener extends AbstractTestSessionListener
|
||||
class TestSessionListener extends AbstractTestSessionListener implements ServiceSubscriberInterface
|
||||
{
|
||||
private $container;
|
||||
|
||||
@ -37,4 +39,14 @@ class TestSessionListener extends AbstractTestSessionListener
|
||||
|
||||
return $this->container->get('session');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array(
|
||||
'session' => '?'.SessionInterface::class,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user