From 7d9c8109abcf03e7e66a49600bc48f912d6e162b Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 8 Nov 2025 16:30:25 +0100 Subject: [PATCH] [OptionsResolver] Fix missing prototype key in nested error paths --- .../OptionsResolver/OptionsResolver.php | 5 +++ .../Tests/OptionsResolverTest.php | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 9b9fe43cce410..3a232a3802c73 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -964,6 +964,11 @@ public function offsetGet(mixed $option, bool $triggerDeprecation = true): mixed $resolver = new self(); $resolver->prototype = false; $resolver->parentsOptions = $this->parentsOptions; + + if ($this->prototype && null !== $this->prototypeIndex && null !== ($parentOptionKey = array_key_last($resolver->parentsOptions))) { + $resolver->parentsOptions[$parentOptionKey] .= \sprintf('[%s]', $this->prototypeIndex); + } + $resolver->parentsOptions[] = $option; foreach ($this->nested[$option] as $closure) { $closure($resolver, $this); diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index de4a4c2629052..c2c5bc45c5d6c 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -2595,4 +2595,46 @@ public function testRemoveAlsoRemovesDeprecation() restore_error_handler(); } } + + public function testNestedPrototypeErrorPathHasFullContext() + { + $resolver = new OptionsResolver(); + + $resolver->setDefault('connections', static function (OptionsResolver $connResolver) { + $connResolver->setPrototype(true); + $connResolver->setRequired(['host', 'database']); + $connResolver->setDefault('user', 'root'); + + $connResolver->setDefault('replicas', static function (OptionsResolver $replicaResolver) { + $replicaResolver->setPrototype(true); + $replicaResolver->setRequired(['host']); + $replicaResolver->setDefault('user', 'read_only'); + }); + }); + + $this->expectException(MissingOptionsException::class); + $this->expectExceptionMessage('The required option "connections[main_db][replicas][1][host]" is missing.'); + + $options = [ + 'connections' => [ + 'main_db' => [ + 'host' => 'localhost', + 'database' => 'app_db', + 'replicas' => [ + ['host' => 'replica-01.local', 'user' => 'read_only'], + ['user' => 'other_user'], // Index 1 -> "host" is missing here + ], + ], + 'audit_db' => [ + 'host' => 'audit.local', + 'database' => 'audit_db', + 'replicas' => [ + ['host' => 'audit-replica.local'], + ], + ], + ], + ]; + + $resolver->resolve($options); + } }