From b16df24b081bd8dbc3affaa3665ec62041cf04db Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Wed, 29 Jun 2022 15:21:43 +0100 Subject: [PATCH] Issue #2896993 by cburschka, andypost, mxr576, jedihe: Decorated services crash on serialization --- .../DependencySerializationTraitPass.php | 22 ++++++++++++ .../decorated_service_test.info.yml | 5 +++ .../decorated_service_test.services.yml | 17 ++++++++++ .../src/TestService.php | 7 ++++ .../src/TestServiceDecorator.php | 7 ++++ .../tests/src/Kernel/DecoratedServiceTest.php | 34 +++++++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 core/modules/system/tests/modules/decorated_service_test/decorated_service_test.info.yml create mode 100644 core/modules/system/tests/modules/decorated_service_test/decorated_service_test.services.yml create mode 100644 core/modules/system/tests/modules/decorated_service_test/src/TestService.php create mode 100644 core/modules/system/tests/modules/decorated_service_test/src/TestServiceDecorator.php create mode 100644 core/modules/system/tests/src/Kernel/DecoratedServiceTest.php diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/DependencySerializationTraitPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/DependencySerializationTraitPass.php index 495228396786..a0ba7f0225e3 100644 --- a/core/lib/Drupal/Core/DependencyInjection/Compiler/DependencySerializationTraitPass.php +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/DependencySerializationTraitPass.php @@ -16,12 +16,34 @@ class DependencySerializationTraitPass implements CompilerPassInterface { * {@inheritdoc} */ public function process(ContainerBuilder $container) { + $decorations = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->getDefinitions() as $service_id => $definition) { // Only add the property to services that are public (as private services // can not be reloaded through Container::get()) and are objects. if (!$definition->hasTag('parameter_service') && $definition->isPublic()) { $definition->setProperty('_serviceId', $service_id); } + + if ($decorated = $definition->getDecoratedService()) { + $decorations->insert([$service_id, $definition], [$decorated[2], --$order]); + } + } + + foreach ($decorations as list($service_id, $definition)) { + list($inner, $renamedId) = $definition->getDecoratedService(); + if (!$renamedId) { + $renamedId = $service_id . '.inner'; + } + + $original = $container->getDefinition($inner); + if ($original->isPublic()) { + // The old service is renamed. + $original->setProperty('_serviceId', $renamedId); + // The decorating service takes over the old ID. + $definition->setProperty('_serviceId', $inner); + } } } diff --git a/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.info.yml b/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.info.yml new file mode 100644 index 000000000000..08d66bdb6904 --- /dev/null +++ b/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.info.yml @@ -0,0 +1,5 @@ +name: 'Decorated Service Test' +type: module +description: 'Support module for decorated service test.' +package: Testing +version: VERSION diff --git a/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.services.yml b/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.services.yml new file mode 100644 index 000000000000..a00c56c7d0cc --- /dev/null +++ b/core/modules/system/tests/modules/decorated_service_test/decorated_service_test.services.yml @@ -0,0 +1,17 @@ +services: + test_service: + class: 'Drupal\decorated_service_test\TestService' + test_service_decorator: + class: 'Drupal\decorated_service_test\TestServiceDecorator' + public: false + decorates: test_service + test_service2: + class: 'Drupal\decorated_service_test\TestService' + test_service2_decorator: + class: 'Drupal\decorated_service_test\TestServiceDecorator' + public: false + decorates: test_service2 + test_service2_decorator2: + class: 'Drupal\decorated_service_test\TestServiceDecorator' + public: false + decorates: test_service2 diff --git a/core/modules/system/tests/modules/decorated_service_test/src/TestService.php b/core/modules/system/tests/modules/decorated_service_test/src/TestService.php new file mode 100644 index 000000000000..8e708019ffae --- /dev/null +++ b/core/modules/system/tests/modules/decorated_service_test/src/TestService.php @@ -0,0 +1,7 @@ +<?php + +namespace Drupal\decorated_service_test; + +class TestService { + +} diff --git a/core/modules/system/tests/modules/decorated_service_test/src/TestServiceDecorator.php b/core/modules/system/tests/modules/decorated_service_test/src/TestServiceDecorator.php new file mode 100644 index 000000000000..30dcb023bd95 --- /dev/null +++ b/core/modules/system/tests/modules/decorated_service_test/src/TestServiceDecorator.php @@ -0,0 +1,7 @@ +<?php + +namespace Drupal\decorated_service_test; + +class TestServiceDecorator extends TestService { + +} diff --git a/core/modules/system/tests/src/Kernel/DecoratedServiceTest.php b/core/modules/system/tests/src/Kernel/DecoratedServiceTest.php new file mode 100644 index 000000000000..854662df18bb --- /dev/null +++ b/core/modules/system/tests/src/Kernel/DecoratedServiceTest.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\Tests\system\Kernel; + +use Drupal\decorated_service_test\TestServiceDecorator; +use Drupal\KernelTests\KernelTestBase; + +/** + * Test handling of decorated services in DependencySerializationTraitPass. + * + * @group system + */ +class DecoratedServiceTest extends KernelTestBase { + + protected static $modules = [ + 'decorated_service_test', + ]; + + /** + * Check that decorated services keep their original service ID. + */ + public function testDecoratedServiceId() { + // Service decorated once. + $test_service = $this->container->get('test_service'); + $this->assertEquals('test_service', $test_service->_serviceId); + $this->assertInstanceOf(TestServiceDecorator::class, $test_service); + + // Service decorated twice. + $test_service2 = $this->container->get('test_service2'); + $this->assertEquals('test_service2', $test_service2->_serviceId); + $this->assertInstanceOf(TestServiceDecorator::class, $test_service2); + } + +} -- GitLab