Locale aware service registration

This commit is contained in:
neghmurken 2018-10-11 00:18:00 +02:00 committed by Nicolas Grekas
parent 4ad54dad28
commit b9ac645d8b
15 changed files with 339 additions and 10 deletions

View File

@ -86,6 +86,7 @@ HttpKernel
* Renamed `GetResponseForControllerResultEvent` to `ViewEvent`
* Renamed `GetResponseForExceptionEvent` to `ExceptionEvent`
* Renamed `PostResponseEvent` to `TerminateEvent`
* Deprecated `TranslatorListener` in favor of `LocaleAwareListener`
Messenger
---------

View File

@ -232,6 +232,7 @@ HttpKernel
* Removed `GetResponseForControllerResultEvent`, use `ViewEvent` instead
* Removed `GetResponseForExceptionEvent`, use `ExceptionEvent` instead
* Removed `PostResponseEvent`, use `TerminateEvent` instead
* Removed `TranslatorListener` in favor of `LocaleAwareListener`
Messenger
---------

View File

@ -4,13 +4,14 @@ CHANGELOG
4.3.0
-----
* added `WebTestAssertions` trait (included by default in `WebTestCase`)
* renamed `Client` to `KernelBrowser`
* Added `WebTestAssertionsTrait` (included by default in `WebTestCase`)
* Renamed `Client` to `KernelBrowser`
* Not passing the project directory to the constructor of the `AssetsInstallCommand` is deprecated. This argument will
be mandatory in 5.0.
* Deprecated the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead
* Added the ability to specify a custom `serializer` option for each
transport under`framework.messenger.transports`.
* Added the `RegisterLocaleAwareServicesPass` and configured the `LocaleAwareListener`
* [BC Break] When using Messenger, the default transport changed from
using Symfony's serializer service to use `PhpSerializer`, which uses
PHP's native `serialize()` and `unserialize()` functions. To use the

View File

@ -40,6 +40,7 @@ class UnusedTagsPass implements CompilerPassInterface
'kernel.event_listener',
'kernel.event_subscriber',
'kernel.fragment_renderer',
'kernel.locale_aware',
'messenger.bus',
'messenger.receiver',
'messenger.message_handler',

View File

@ -119,6 +119,7 @@ use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Service\ResetInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
/**
* FrameworkExtension.
@ -370,6 +371,8 @@ class FrameworkExtension extends Extension
->addTag('kernel.cache_warmer');
$container->registerForAutoconfiguration(EventSubscriberInterface::class)
->addTag('kernel.event_subscriber');
$container->registerForAutoconfiguration(LocaleAwareInterface::class)
->addTag('kernel.locale_aware');
$container->registerForAutoconfiguration(ResetInterface::class)
->addTag('kernel.reset', ['method' => 'reset']);

View File

@ -41,6 +41,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueReso
use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass;
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterLocaleAwareServicesPass;
use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass;
use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass;
use Symfony\Component\HttpKernel\KernelEvents;
@ -121,6 +122,7 @@ class FrameworkBundle extends Bundle
$this->addCompilerPassIfExists($container, FormPass::class);
$container->addCompilerPass(new WorkflowGuardListenerPass());
$container->addCompilerPass(new ResettableServicePass());
$container->addCompilerPass(new RegisterLocaleAwareServicesPass());
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
$container->addCompilerPass(new TestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
$this->addCompilerPassIfExists($container, AddMimeTypeGuesserPass::class);

View File

@ -112,5 +112,11 @@
<argument type="service_locator" />
</service>
<service id="Symfony\Component\DependencyInjection\ReverseContainer" alias="reverse_container" />
<service id="locale_aware_listener" class="Symfony\Component\HttpKernel\EventListener\LocaleAwareListener">
<argument type="collection" /> <!-- locale aware services -->
<argument type="service" id="request_stack" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

View File

@ -19,6 +19,7 @@
<call method="setConfigCacheFactory">
<argument type="service" id="config_cache_factory" />
</call>
<tag name="kernel.locale_aware" />
</service>
<service id="Symfony\Component\Translation\TranslatorInterface" alias="translator" />
<service id="Symfony\Contracts\Translation\TranslatorInterface" alias="translator" />
@ -140,11 +141,5 @@
<tag name="kernel.cache_warmer" />
<argument type="service" id="Psr\Container\ContainerInterface" />
</service>
<service id="translator_listener" class="Symfony\Component\HttpKernel\EventListener\TranslatorListener">
<argument type="service" id="translator" />
<argument type="service" id="request_stack" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

View File

@ -8,7 +8,9 @@ CHANGELOG
* `KernelInterface` doesn't extend `Serializable` anymore
* deprecated the `Kernel::serialize()` and `unserialize()` methods
* increased the priority of `Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener`
* made `Symfony\Component\HttpKernel\EventListenerLocaleListener` set the default locale early
* made `Symfony\Component\HttpKernel\EventListener\LocaleListener` set the default locale early
* deprecated `TranslatorListener` in favor of `LocaleAwareListener`
* added the registration of all `LocaleAwareInterface` implementations into the `LocaleAwareListener`
* made `FileLinkFormatter` final and not implement `Serializable` anymore
* the base `DataCollector` doesn't implement `Serializable` anymore, you should
store all the serialized state in the data property instead

View File

@ -0,0 +1,58 @@
<?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\HttpKernel\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Register all services that have the "kernel.locale_aware" tag into the listener.
*
* @author Pierre Bobiet <pierrebobiet@gmail.com>
*/
class RegisterLocaleAwareServicesPass implements CompilerPassInterface
{
private $listenerServiceId;
private $localeAwareTag;
public function __construct(string $listenerServiceId = 'locale_aware_listener', string $localeAwareTag = 'kernel.locale_aware')
{
$this->listenerServiceId = $listenerServiceId;
$this->localeAwareTag = $localeAwareTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->listenerServiceId)) {
return;
}
$services = [];
foreach ($container->findTaggedServiceIds($this->localeAwareTag) as $id => $tags) {
$services[] = new Reference($id);
}
if (!$services) {
$container->removeDefinition($this->listenerServiceId);
return;
}
$container
->getDefinition($this->listenerServiceId)
->setArgument(0, new IteratorArgument($services))
;
}
}

View File

@ -0,0 +1,76 @@
<?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\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\Translation\LocaleAwareInterface;
/**
* Pass the current locale to the provided services.
*
* @author Pierre Bobiet <pierrebobiet@gmail.com>
*/
class LocaleAwareListener implements EventSubscriberInterface
{
private $localeAwareServices;
private $requestStack;
/**
* @param LocaleAwareInterface[] $localeAwareServices
*/
public function __construct(iterable $localeAwareServices, RequestStack $requestStack)
{
$this->localeAwareServices = $localeAwareServices;
$this->requestStack = $requestStack;
}
public function onKernelRequest(RequestEvent $event): void
{
$this->setLocale($event->getRequest()->getLocale(), $event->getRequest()->getDefaultLocale());
}
public function onKernelFinishRequest(FinishRequestEvent $event): void
{
if (null === $parentRequest = $this->requestStack->getParentRequest()) {
$this->setLocale($event->getRequest()->getDefaultLocale());
return;
}
$this->setLocale($parentRequest->getLocale(), $parentRequest->getDefaultLocale());
}
public static function getSubscribedEvents()
{
return [
// must be registered after the Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 15]],
KernelEvents::FINISH_REQUEST => [['onKernelFinishRequest', -15]],
];
}
private function setLocale(string $locale, ?string $defaultLocale = null): void
{
foreach ($this->localeAwareServices as $service) {
try {
$service->setLocale($locale);
} catch (\InvalidArgumentException $e) {
$service->setLocale($defaultLocale);
}
}
}
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\HttpKernel\EventListener;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3 and will be removed in 5.0, use LocaleAwareListener instead.', TranslatorListener::class), E_USER_DEPRECATED);
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
@ -25,7 +27,7 @@ use Symfony\Contracts\Translation\LocaleAwareInterface;
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since Symfony 4.3
* @deprecated since Symfony 4.3, use LocaleAwareListener instead
*/
class TranslatorListener implements EventSubscriberInterface
{

View File

@ -0,0 +1,59 @@
<?php
namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterLocaleAwareServicesPass;
use Symfony\Component\HttpKernel\EventListener\LocaleAwareListener;
use Symfony\Contracts\Translation\LocaleAwareInterface;
class RegisterLocaleAwareServicesPassTest extends TestCase
{
public function testCompilerPass()
{
$container = new ContainerBuilder();
$container->register('locale_aware_listener', LocaleAwareListener::class)
->setPublic(true)
->setArguments([null, null]);
$container->register('some_locale_aware_service', LocaleAwareInterface::class)
->setPublic(true)
->addTag('kernel.locale_aware');
$container->register('another_locale_aware_service', LocaleAwareInterface::class)
->setPublic(true)
->addTag('kernel.locale_aware');
$container->addCompilerPass(new RegisterLocaleAwareServicesPass());
$container->compile();
$this->assertEquals(
[
new IteratorArgument([
0 => new Reference('some_locale_aware_service'),
1 => new Reference('another_locale_aware_service'),
]),
null,
],
$container->getDefinition('locale_aware_listener')->getArguments()
);
}
public function testListenerUnregisteredWhenNoLocaleAwareServices()
{
$container = new ContainerBuilder();
$container->register('locale_aware_listener', LocaleAwareListener::class)
->setPublic(true)
->setArguments([null, null]);
$container->addCompilerPass(new RegisterLocaleAwareServicesPass());
$container->compile();
$this->assertFalse($container->hasDefinition('locale_aware_listener'));
}
}

View File

@ -0,0 +1,119 @@
<?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\HttpKernel\Tests\EventListener;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\EventListener\LocaleAwareListener;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
class LocaleAwareListenerTest extends TestCase
{
private $listener;
private $localeAwareService;
private $requestStack;
protected function setUp()
{
$this->localeAwareService = $this->getMockBuilder(LocaleAwareInterface::class)->getMock();
$this->requestStack = new RequestStack();
$this->listener = new LocaleAwareListener(new \ArrayIterator([$this->localeAwareService]), $this->requestStack);
}
public function testLocaleIsSetInOnKernelRequest()
{
$this->localeAwareService
->expects($this->once())
->method('setLocale')
->with($this->equalTo('fr'));
$event = new RequestEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST);
$this->listener->onKernelRequest($event);
}
public function testDefaultLocaleIsUsedOnExceptionsInOnKernelRequest()
{
$this->localeAwareService
->expects($this->at(0))
->method('setLocale')
->will($this->throwException(new \InvalidArgumentException()));
$this->localeAwareService
->expects($this->at(1))
->method('setLocale')
->with($this->equalTo('en'));
$event = new RequestEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST);
$this->listener->onKernelRequest($event);
}
public function testLocaleIsSetInOnKernelFinishRequestWhenParentRequestExists()
{
$this->localeAwareService
->expects($this->once())
->method('setLocale')
->with($this->equalTo('fr'));
$this->requestStack->push($this->createRequest('fr'));
$this->requestStack->push($subRequest = $this->createRequest('de'));
$event = new FinishRequestEvent($this->createHttpKernel(), $subRequest, HttpKernelInterface::SUB_REQUEST);
$this->listener->onKernelFinishRequest($event);
}
public function testLocaleIsSetToDefaultOnKernelFinishRequestWhenParentRequestDoesNotExist()
{
$this->localeAwareService
->expects($this->once())
->method('setLocale')
->with($this->equalTo('en'));
$this->requestStack->push($subRequest = $this->createRequest('de'));
$event = new FinishRequestEvent($this->createHttpKernel(), $subRequest, HttpKernelInterface::SUB_REQUEST);
$this->listener->onKernelFinishRequest($event);
}
public function testDefaultLocaleIsUsedOnExceptionsInOnKernelFinishRequest()
{
$this->localeAwareService
->expects($this->at(0))
->method('setLocale')
->will($this->throwException(new \InvalidArgumentException()));
$this->localeAwareService
->expects($this->at(1))
->method('setLocale')
->with($this->equalTo('en'));
$this->requestStack->push($this->createRequest('fr'));
$this->requestStack->push($subRequest = $this->createRequest('de'));
$event = new FinishRequestEvent($this->createHttpKernel(), $subRequest, HttpKernelInterface::SUB_REQUEST);
$this->listener->onKernelFinishRequest($event);
}
private function createHttpKernel()
{
return $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
}
private function createRequest($locale)
{
$request = new Request();
$request->setLocale($locale);
return $request;
}
}

View File

@ -19,6 +19,9 @@ use Symfony\Component\HttpKernel\EventListener\TranslatorListener;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
/**
* @group legacy
*/
class TranslatorListenerTest extends TestCase
{
private $listener;