From c0053e636e125efe3d156370846cefc6337f4895 Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Thu, 24 Oct 2024 12:11:28 +1000
Subject: [PATCH] Issue #3293926 by kingdutch, geekygnr, godotislate, m4olivei,
 longwave: Error decorating non-existent service when inner service's module
 not installed

---
 .../DependencyInjection/YamlFileLoader.php    | 26 +++++++++++++--
 .../YamlFileLoaderTest.php                    | 32 +++++++++++++++++++
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
index 39c4a771f576..c16f7d75da22 100644
--- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -390,10 +390,32 @@ private function parseDefinition(string $id, $service, string $file, array $defa
             $definition->addTag($name, $tag);
         }
 
-        if (isset($service['decorates'])) {
+        if (null !== $decorates = $service['decorates'] ?? null) {
+            if ('' !== $decorates && '@' === $decorates[0]) {
+                throw new InvalidArgumentException(\sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($decorates, 1)));
+            }
+
+            $decorationOnInvalid = \array_key_exists('decoration_on_invalid', $service) ? $service['decoration_on_invalid'] : 'exception';
+            if ('exception' === $decorationOnInvalid) {
+                $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
+            }
+            elseif ('ignore' === $decorationOnInvalid) {
+                $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
+            }
+            elseif (null === $decorationOnInvalid) {
+                $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
+            }
+            elseif ('null' === $decorationOnInvalid) {
+                throw new InvalidArgumentException(\sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean null (without quotes) in "%s"?', $decorationOnInvalid, $id, $file));
+            }
+            else {
+                throw new InvalidArgumentException(\sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean "exception", "ignore" or null in "%s"?', $decorationOnInvalid, $id, $file));
+            }
+
             $renameId = $service['decoration_inner_name'] ?? null;
             $priority = $service['decoration_priority'] ?? 0;
-            $definition->setDecoratedService($service['decorates'], $renameId, $priority);
+
+            $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior);
         }
 
         if (isset($service['autowire'])) {
diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
index b4ed2ca57c3a..717026fe3f46 100644
--- a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
+++ b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
@@ -200,6 +200,38 @@ public static function providerTestExceptions() {
       YAML,
         'The service file "vfs://drupal/modules/example/example.yml" is not valid: it contains invalid root key(s) "do not". Services have to be added under "services" and Parameters under "parameters".',
       ],
+      'decorates must be without @' => [<<<YAML
+      services:
+        example_service_1:
+          class: \Drupal\Core\ExampleClass
+        example_decoration:
+          class: \Drupal\Core\ExampleClass
+          decorates: "@example_service_1"
+      YAML,
+        'The value of the "decorates" option for the "example_decoration" service must be the id of the service without the "@" prefix (replace "@example_service_1" with "example_service_1").',
+      ],
+      'decorates_on_invalid may not be "null" with quotes' => [<<<YAML
+      services:
+        example_service_1:
+          class: \Drupal\Core\ExampleClass
+        example_decoration:
+          class: \Drupal\Core\ExampleClass
+          decorates: example_service_1
+          decoration_on_invalid: "null"
+      YAML,
+        'Invalid value "null" for attribute "decoration_on_invalid" on service "example_decoration". Did you mean null (without quotes) in "vfs://drupal/modules/example/example.yml"?',
+      ],
+      'decoration_on_invalid must be valid' => [<<<YAML
+      services:
+        example_service_1:
+          class: \Drupal\Core\ExampleClass
+        example_decoration:
+          class: \Drupal\Core\ExampleClass
+          decorates: example_service_1
+          decoration_on_invalid: foo
+      YAML,
+        'Invalid value "foo" for attribute "decoration_on_invalid" on service "example_decoration". Did you mean "exception", "ignore" or null in "vfs://drupal/modules/example/example.yml"?',
+      ],
     ];
   }
 
-- 
GitLab