Skip to content

Commit

Permalink
[FrameworkBundle][Routing] Add a new tag to be able to use a private …
Browse files Browse the repository at this point in the history
…service as a service route loader
  • Loading branch information
fancyweb committed Apr 7, 2019
1 parent 65b46a5 commit 93584eb
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 0 deletions.
1 change: 1 addition & 0 deletions UPGRADE-4.3.md
Expand Up @@ -101,6 +101,7 @@ Routing
options have been deprecated.
* Implementing `Serializable` for `Route` and `CompiledRoute` is deprecated; if you serialize them, please
ensure your unserialization logic can recover from a failure related to an updated serialization format
* Not tagging the route loader services with `routing.route_loader` has been deprecated.

Security
--------
Expand Down
1 change: 1 addition & 0 deletions UPGRADE-5.0.md
Expand Up @@ -270,6 +270,7 @@ Routing
options have been removed.
* `Route` and `CompiledRoute` don't implement `Serializable` anymore; if you serialize them, please
ensure your unserialization logic can recover from a failure related to an updated serialization format
* The route loader services must be tagged with `routing.route_loader`.

Security
--------
Expand Down
Expand Up @@ -92,6 +92,7 @@
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
use Symfony\Component\Routing\Loader\AnnotationFileLoader;
use Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoaderInterface;
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
use Symfony\Component\Security\Core\Security;
Expand Down Expand Up @@ -421,6 +422,9 @@ public function load(array $configs, ContainerBuilder $container)
if (!$config['disallow_search_engine_index'] ?? false) {
$container->removeDefinition('disallow_search_engine_index_response_listener');
}

$container->registerForAutoconfiguration(ServiceRouterLoaderInterface::class)
->addTag('routing.route_loader');
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -49,6 +49,7 @@
use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass;
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
use Symfony\Component\Routing\DependencyInjection\ServiceRouterLoaderPass;
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass;
use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass;
Expand Down Expand Up @@ -130,6 +131,7 @@ public function build(ContainerBuilder $container)
$this->addCompilerPassIfExists($container, AddAutoMappingConfigurationPass::class);
$container->addCompilerPass(new RegisterReverseContainerPass(true));
$container->addCompilerPass(new RegisterReverseContainerPass(false), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new ServiceRouterLoaderPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
Expand Down
Expand Up @@ -77,6 +77,11 @@ public function registerContainerConfiguration(LoaderInterface $loader)
;
}

$container
->getDefinition('kernel')
->addTag('routing.route_loader')
;

$this->configureContainer($container, $loader);

$container->addObjectResource($this);
Expand Down
Expand Up @@ -12,6 +12,9 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;

class MicroKernelTraitTest extends TestCase
Expand Down Expand Up @@ -39,4 +42,21 @@ public function testAsEventSubscriber()

$this->assertSame('It\'s dangerous to go alone. Take this ⚔', $response->getContent());
}

public function testRoutingRouteLoaderTagIsAdded(): void
{
$frameworkExtension = $this->createMock(ExtensionInterface::class);
$frameworkExtension
->expects($this->atLeastOnce())
->method('getAlias')
->willReturn('framework');

$container = new ContainerBuilder();
$container->registerExtension($frameworkExtension);

$kernel = new ConcreteMicroKernel('test', false);
$kernel->registerContainerConfiguration(new ClosureLoader($container));

$this->assertTrue($container->getDefinition('kernel')->hasTag('routing.route_loader'));
}
}
@@ -0,0 +1,54 @@
<?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\Routing\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoaderContainer;

class ServiceRouterLoaderPass implements CompilerPassInterface
{
private $loaderServiceServiceId;
private $serviceRouterLoaderTag;

public function __construct(string $loaderServiceServiceId = 'routing.loader.service', string $serviceRouterLoaderTag = 'routing.route_loader')
{
$this->loaderServiceServiceId = $loaderServiceServiceId;
$this->serviceRouterLoaderTag = $serviceRouterLoaderTag;
}

/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container): void
{
if (!$container->hasDefinition($this->loaderServiceServiceId)) {
return;
}

$servicesRouterLoaders = [];
foreach ($container->findTaggedServiceIds($this->serviceRouterLoaderTag, true) as $id => $attributes) {
$servicesRouterLoaders[$id] = new Reference($id);
}

$container
->getDefinition($this->loaderServiceServiceId)
->replaceArgument(0, new Definition(ServiceRouterLoaderContainer::class, [
new Reference('service_container'),
ServiceLocatorTagPass::register($container, $servicesRouterLoaders),
$this->serviceRouterLoaderTag,
]));
}
}
@@ -0,0 +1,53 @@
<?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\Routing\Loader\DependencyInjection;

use Psr\Container\ContainerInterface;

/**
* @internal
*/
class ServiceRouterLoaderContainer implements ContainerInterface
{
private $container;
private $serviceLocator;
private $serviceRouterLoaderTag;

public function __construct(ContainerInterface $container, ContainerInterface $serviceLocator, string $serviceRouterLoaderTag)
{
$this->container = $container;
$this->serviceLocator = $serviceLocator;
$this->serviceRouterLoaderTag = $serviceRouterLoaderTag;
}

/**
* {@inheritdoc}
*/
public function get($id)
{
if ($this->serviceLocator->has($id)) {
return $this->serviceLocator->get($id);
}

@trigger_error(sprintf('Not tagging the "%s" service with the "%s" tag is deprecated since Symfony 4.3.', $id, $this->serviceRouterLoaderTag), E_USER_DEPRECATED);

return $this->container->get($id);
}

/**
* {@inheritdoc}
*/
public function has($id)
{
return $this->serviceLocator->has($id) || $this->container->has($id);
}
}
@@ -0,0 +1,16 @@
<?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\Routing\Loader\DependencyInjection;

interface ServiceRouterLoaderInterface
{
}
@@ -0,0 +1,49 @@
<?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\Routing\Tests\DependencyInjection;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Routing\DependencyInjection\ServiceRouterLoaderPass;
use Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoader;
use Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoaderContainer;

class ServiceRouterLoaderPassTest extends TestCase
{
public function testProcess(): void
{
$loaderServiceServiceId = 'foo';
$serviceRouterLoaderTag = 'bar';

$container = new ContainerBuilder();
$serviceRouterLoaderDefinition = $container->setDefinition($loaderServiceServiceId, new Definition(ServiceRouterLoader::class, [
new Definition(Container::class),
]));
$container->register('route_service_loader_1')->addTag($serviceRouterLoaderTag);
$container->register('route_service_loader_2')->addTag($serviceRouterLoaderTag);

(new ServiceRouterLoaderPass($loaderServiceServiceId, $serviceRouterLoaderTag))->process($container);

/** @var Definition $serviceRouterLoaderArgument */
$serviceRouterLoaderArgument = $serviceRouterLoaderDefinition->getArgument(0);
$this->assertSame(ServiceRouterLoaderContainer::class, $serviceRouterLoaderArgument->getClass());
$this->assertEquals(new Reference('service_container'), $serviceRouterLoaderArgument->getArgument(0));
/** @var ServiceLocator $serviceLocator */
$serviceLocator = $container->get((string) $serviceRouterLoaderArgument->getArgument(1));
$this->assertTrue($serviceLocator->has('route_service_loader_1'));
$this->assertTrue($serviceLocator->has('route_service_loader_2'));
}
}
@@ -0,0 +1,66 @@
<?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\Routing\Tests\Loader;

use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoaderContainer;

class ServiceRouterLoaderContainerTest extends TestCase
{
/**
* @var ContainerInterface
*/
private $container;

/**
* @var ContainerInterface
*/
private $serviceLocator;

/**
* @var ServiceRouterLoaderContainer
*/
private $serviceRouterLoaderContainer;

/**
* {@inheritdoc}
*/
protected function setUp(): void
{
$this->container = new Container();
$this->container->set('foo', new \stdClass());

$this->serviceLocator = new Container();
$this->serviceLocator->set('bar', new \stdClass());

$this->serviceRouterLoaderContainer = new ServiceRouterLoaderContainer($this->container, $this->serviceLocator, 'ccc');
}

/**
* @group legacy
* @expectedDeprecation Not tagging the "foo" service with the "ccc" tag is deprecated since Symfony 4.3.
*/
public function testGet(): void
{
$this->assertSame($this->container->get('foo'), $this->serviceRouterLoaderContainer->get('foo'));
$this->assertSame($this->serviceLocator->get('bar'), $this->serviceRouterLoaderContainer->get('bar'));
}

public function testHas(): void
{
$this->assertTrue($this->serviceRouterLoaderContainer->has('foo'));
$this->assertTrue($this->serviceRouterLoaderContainer->has('bar'));
$this->assertFalse($this->serviceRouterLoaderContainer->has('ccc'));
}
}

0 comments on commit 93584eb

Please sign in to comment.