Skip to content

[HttpKernel][Framework] Locale aware services #28929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions UPGRADE-4.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ HttpKernel
* Renamed `GetResponseForControllerResultEvent` to `ViewEvent`
* Renamed `GetResponseForExceptionEvent` to `ExceptionEvent`
* Renamed `PostResponseEvent` to `TerminateEvent`
* Deprecated `TranslatorListener` in favor of `LocaleAwareListener`

Messenger
---------
Expand Down
1 change: 1 addition & 0 deletions UPGRADE-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------
Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Service\ResetInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;

/**
* FrameworkExtension.
Expand Down Expand Up @@ -370,6 +371,8 @@ public function load(array $configs, ContainerBuilder $container)
->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']);

Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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;
Expand Down Expand Up @@ -121,6 +122,7 @@ public function build(ContainerBuilder $container)
$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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we deprecate the service instead in case someone is relying on its presence?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we deprecate without it conflicting with the new LocaleAwareListener ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it do any harm if both listeners are executed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right. There is no undesirable side effect beside setting the locale twice

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the translator_listener service completely as keeping it would trigger deprecation notices. I don't think that's an issue.

<argument type="service" id="translator" />
<argument type="service" id="request_stack" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>
4 changes: 3 additions & 1 deletion src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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))
;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +27,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since Symfony 4.3
* @deprecated since Symfony 4.3, use LocaleAwareListener instead
*/
class TranslatorListener implements EventSubscriberInterface
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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'));
}
}
Loading