diff --git a/src/bundle/Core/Resources/config/routing.yml b/src/bundle/Core/Resources/config/routing.yml index b746b92942..2b9d7cb2e6 100644 --- a/src/bundle/Core/Resources/config/routing.yml +++ b/src/bundle/Core/Resources/config/routing.yml @@ -14,12 +14,64 @@ services: calls: - [setContext, ["@router.request_context"]] + ibexa.core.mvc.serializer: + class: Symfony\Component\Serializer\Serializer + arguments: + $normalizers: + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\MatcherDenormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\MapNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\URITextNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\HostTextNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\RegexURINormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\RegexHostNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\RegexNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\URIElementNormalizer' + - '@Ibexa\Core\MVC\Symfony\Component\Serializer\SimplifiedRequestNormalizer' + - '@ibexa.core.mvc.serializer.normalizer.json_serializable_normalizer' + - '@ibexa.core.mvc.serializer.normalizer.property_normalizer' + $encoders: + - '@ibexa.core.mvc.serializer.json_encoder' + + ibexa.core.mvc.serializer.json_encoder: + class: Symfony\Component\Serializer\Encoder\JsonEncoder + + Ibexa\Core\MVC\Symfony\Component\Serializer\MatcherDenormalizer: + arguments: + $registry: '@Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface' + + Ibexa\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\MapNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\URITextNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\HostTextNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\RegexURINormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\RegexHostNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\RegexNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\URIElementNormalizer: ~ + + Ibexa\Core\MVC\Symfony\Component\Serializer\SimplifiedRequestNormalizer: ~ + + ibexa.core.mvc.serializer.normalizer.json_serializable_normalizer: + class: Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer + + ibexa.core.mvc.serializer.normalizer.property_normalizer: + class: Symfony\Component\Serializer\Normalizer\PropertyNormalizer + ibexa.siteaccess_match_listener: class: Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener arguments: $siteAccessRouter: '@Ibexa\Core\MVC\Symfony\SiteAccess\Router' $eventDispatcher: '@event_dispatcher' $siteAccessMatcherRegistry: '@Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface' + $serializer: '@ibexa.core.mvc.serializer' tags: - { name: kernel.event_subscriber } diff --git a/src/lib/MVC/Symfony/Component/Serializer/MatcherDenormalizer.php b/src/lib/MVC/Symfony/Component/Serializer/MatcherDenormalizer.php new file mode 100644 index 0000000000..d1e42a0eed --- /dev/null +++ b/src/lib/MVC/Symfony/Component/Serializer/MatcherDenormalizer.php @@ -0,0 +1,43 @@ +registry = $registry; + } + + public function denormalize($data, string $type, ?string $format = null, array $context = []) + { + $matcher = $this->registry->getMatcher($type); + + $this->denormalizer->denormalize($data, $type, $format, $context + [ + AbstractNormalizer::OBJECT_TO_POPULATE => $matcher, + self::MATCHER_NORMALIZER_ALREADY_WORKED => true, + ]); + } + + public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool + { + if ($context[self::MATCHER_NORMALIZER_ALREADY_WORKED] ?? false) { + return false; + } + + return $this->registry->hasMatcher($type); + } +} diff --git a/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php index 452738312a..9eaabdd9dd 100644 --- a/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php +++ b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php @@ -41,14 +41,18 @@ class SiteAccessMatchListener implements EventSubscriberInterface /** @var \Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface */ private $siteAccessMatcherRegistry; + private SerializerInterface $serializer; + public function __construct( SiteAccessRouter $siteAccessRouter, EventDispatcherInterface $eventDispatcher, - SiteAccessMatcherRegistryInterface $siteAccessMatcherRegistry + SiteAccessMatcherRegistryInterface $siteAccessMatcherRegistry, + SerializerInterface $serializer ) { $this->siteAccessRouter = $siteAccessRouter; $this->eventDispatcher = $eventDispatcher; $this->siteAccessMatcherRegistry = $siteAccessMatcherRegistry; + $this->serializer = $serializer; } public static function getSubscribedEvents() @@ -76,7 +80,6 @@ public function onKernelRequest(RequestEvent $event): void $siteAccess = $serializer->deserialize($request->attributes->get('serialized_siteaccess'), SiteAccess::class, 'json'); if ($siteAccess->matcher !== null) { $siteAccess->matcher = $this->deserializeMatcher( - $serializer, $siteAccess->matcher, $request->attributes->get('serialized_siteaccess_matcher'), $request->attributes->get('serialized_siteaccess_sub_matchers') @@ -133,7 +136,6 @@ private function getSiteAccessFromRequest(Request $request) * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function deserializeMatcher( - SerializerInterface $serializer, string $matcherFQCN, string $serializedMatcher, ?array $serializedSubMatchers @@ -141,7 +143,6 @@ private function deserializeMatcher( $matcher = null; if (in_array(SiteAccess\Matcher::class, class_implements($matcherFQCN), true)) { $matcher = $this->buildMatcherFromSerializedClass( - $serializer, $matcherFQCN, $serializedMatcher ); @@ -159,7 +160,6 @@ private function deserializeMatcher( $subMatchers = []; foreach ($serializedSubMatchers as $subMatcherFQCN => $serializedData) { $subMatchers[$subMatcherFQCN] = $this->buildMatcherFromSerializedClass( - $serializer, $subMatcherFQCN, $serializedData ); @@ -190,27 +190,14 @@ static function (array $serializedGroup): SiteAccessGroup { * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ private function buildMatcherFromSerializedClass( - SerializerInterface $serializer, string $matcherClass, string $serializedMatcher ): SiteAccess\Matcher { - if ($this->siteAccessMatcherRegistry->hasMatcher($matcherClass)) { - $matcher = $this->siteAccessMatcherRegistry->getMatcher($matcherClass); - $matcher = $serializer->deserialize( - $serializedMatcher, - $matcherClass, - 'json', - [AbstractNormalizer::OBJECT_TO_POPULATE => $matcher] - ); - } else { - $matcher = $serializer->deserialize( - $serializedMatcher, - $matcherClass, - 'json' - ); - } - - return $matcher; + return $this->serializer->deserialize( + $serializedMatcher, + $matcherClass, + 'json', + ); } } diff --git a/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php index 8ad10bd8c2..b9a43f1b30 100644 --- a/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php +++ b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php @@ -7,7 +7,18 @@ namespace Ibexa\Tests\Core\MVC\Symfony\EventListener; use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface; +use Ibexa\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\HostTextNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\MapNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\MatcherDenormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexHostNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexURINormalizer; use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SimplifiedRequestNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\URIElementNormalizer; +use Ibexa\Core\MVC\Symfony\Component\Serializer\URITextNormalizer; use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; use Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener; use Ibexa\Core\MVC\Symfony\MVCEvents; @@ -22,32 +33,39 @@ use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; +use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\SerializerInterface; class SiteAccessMatchListenerTest extends TestCase { - use SerializerTrait; - /** @var \PHPUnit\Framework\MockObject\MockObject */ - private $saRouter; + /** @var \PHPUnit\Framework\MockObject\MockObject & \Ibexa\Core\MVC\Symfony\SiteAccess\Router */ + private Router $saRouter; - /** @var \PHPUnit\Framework\MockObject\MockObject */ - private $eventDispatcher; + /** @var \PHPUnit\Framework\MockObject\MockObject & \Symfony\Component\EventDispatcher\EventDispatcherInterface */ + private EventDispatcherInterface $eventDispatcher; - /** @var \Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener */ - private $listener; + /** @var \PHPUnit\Framework\MockObject\MockObject & \Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface */ + private SiteAccessMatcherRegistryInterface $registry; + + private SiteAccessMatchListener $listener; protected function setUp(): void { parent::setUp(); $this->saRouter = $this->createMock(Router::class); $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); - $matcherRegistryMock = $this->createMock(SiteAccessMatcherRegistryInterface::class); - $matcherRegistryMock->method('hasMatcher')->willReturn(false); + $this->registry = $this->createMock(SiteAccessMatcherRegistryInterface::class); + $this->registry->method('hasMatcher')->willReturn(false); $this->listener = new SiteAccessMatchListener( $this->saRouter, $this->eventDispatcher, - $matcherRegistryMock + $this->registry, + $this->getSerializer() ); } @@ -185,20 +203,20 @@ public function testOnKernelRequestSerializedSAWithCompoundMatcher() public function testOnKernelRequestSerializedSAWithMatcherInMatcherRegistry(): void { $matcher = new CustomMatcher([]); - $matcherRegistryMock = $this->createMock(SiteAccessMatcherRegistryInterface::class); - $matcherRegistryMock->method('hasMatcher')->willReturn(true); - $matcherRegistryMock->method('getMatcher')->willReturn($matcher); + $this->registry->method('hasMatcher')->willReturn(true); + $this->registry->method('getMatcher')->willReturn($matcher); $matcher2 = new CustomMatcher([]); $matcher2->setMapKey('key_foobar'); - $matcherRegistryMock + $this->registry ->expects($this->once()) ->method('getMatcher') ->with('Ibexa\Tests\Core\MVC\Symfony\EventListener\CustomMatcher'); $this->listener = new SiteAccessMatchListener( $this->saRouter, $this->eventDispatcher, - $matcherRegistryMock + $this->registry, + $this->getSerializer(), ); $siteAccess = $this->createSiteAccess( @@ -324,6 +342,28 @@ public function testOnKernelRequestUserHashWithOriginalRequest() $this->listener->onKernelRequest($event); $this->assertSame($siteAccess, $request->attributes->get('siteaccess')); } + + private function getSerializer(): SerializerInterface + { + return new Serializer( + [ + new MatcherDenormalizer($this->registry), + new CompoundMatcherNormalizer(), + new HostElementNormalizer(), + new MapNormalizer(), + new URITextNormalizer(), + new HostTextNormalizer(), + new RegexURINormalizer(), + new RegexHostNormalizer(), + new RegexNormalizer(), + new URIElementNormalizer(), + new SimplifiedRequestNormalizer(), + new JsonSerializableNormalizer(), + new PropertyNormalizer(), + ], + [new JsonEncoder()] + ); + } } class_alias(SiteAccessMatchListenerTest::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\Tests\SiteAccessMatchListenerTest');