From 29360510db26953f61fbcbaf438439b3228b6217 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Fri, 5 Oct 2018 09:29:45 -0400 Subject: [PATCH] [OptionsResolver] Passing Options argument to deprecation closure --- .../OptionsResolver/OptionsResolver.php | 20 ++++++-- .../Debug/OptionsResolverIntrospectorTest.php | 2 +- .../Tests/OptionsResolverTest.php | 48 +++++++++++++++++-- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index aa15a2de1b827..89e651400b4c4 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -360,12 +360,12 @@ public function getDefinedOptions() * Instead of passing the message, you may also pass a closure with the * following signature: * - * function ($value) { + * function (Options $options, $value): string { * // ... * } * * The closure receives the value as argument and should return a string. - * Returns an empty string to ignore the option deprecation. + * Return an empty string to ignore the option deprecation. * * The closure is invoked when {@link resolve()} is called. The parameter * passed to the closure is the value of the option after validating it @@ -860,8 +860,20 @@ public function offsetGet($option) if (isset($this->deprecated[$option])) { $deprecationMessage = $this->deprecated[$option]; - if ($deprecationMessage instanceof \Closure && !\is_string($deprecationMessage = $deprecationMessage($value))) { - throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", returns an empty string to ignore.', \gettype($deprecationMessage))); + if ($deprecationMessage instanceof \Closure) { + // If the closure is already being called, we have a cyclic dependency + if (isset($this->calling[$option])) { + throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling)))); + } + + $this->calling[$option] = true; + try { + if (!\is_string($deprecationMessage = $deprecationMessage($this, $value))) { + throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', \gettype($deprecationMessage))); + } + } finally { + unset($this->calling[$option]); + } } if ('' !== $deprecationMessage) { diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php index b146d28d6624e..4bdce6f807a07 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php @@ -215,7 +215,7 @@ public function testGetClosureDeprecationMessage() { $resolver = new OptionsResolver(); $resolver->setDefined('foo'); - $resolver->setDeprecated('foo', $closure = function ($value) {}); + $resolver->setDeprecated('foo', $closure = function (Options $options, $value) {}); $debug = new OptionsResolverIntrospector($resolver); $this->assertSame($closure, $debug->getDeprecationMessage('foo')); diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 2ed6face53378..d94169ad7184d 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -486,19 +486,38 @@ public function testSetDeprecatedFailsIfInvalidDeprecationMessageType() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidArgumentException - * @expectedExceptionMessage Invalid type for deprecation message, expected string but got "boolean", returns an empty string to ignore. + * @expectedExceptionMessage Invalid type for deprecation message, expected string but got "boolean", return an empty string to ignore. */ public function testLazyDeprecationFailsIfInvalidDeprecationMessageType() { $this->resolver ->setDefault('foo', true) - ->setDeprecated('foo', function ($value) { + ->setDeprecated('foo', function (Options $options, $value) { return false; }) ; $this->resolver->resolve(); } + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException + * @expectedExceptionMessage The options "foo", "bar" have a cyclic dependency. + */ + public function testFailsIfCyclicDependencyBetweenDeprecation() + { + $this->resolver + ->setDefault('foo', null) + ->setDefault('bar', null) + ->setDeprecated('foo', function (Options $options, $value) { + $options['bar']; + }) + ->setDeprecated('bar', function (Options $options, $value) { + $options['foo']; + }) + ; + $this->resolver->resolve(); + } + public function testIsDeprecated() { $this->resolver @@ -590,7 +609,7 @@ function (OptionsResolver $resolver) { $resolver ->setDefault('foo', null) ->setAllowedTypes('foo', array('null', 'string', \stdClass::class)) - ->setDeprecated('foo', function ($value) { + ->setDeprecated('foo', function (Options $options, $value) { if ($value instanceof \stdClass) { return sprintf('Passing an instance of "%s" to option "foo" is deprecated, pass its FQCN instead.', \stdClass::class); } @@ -621,7 +640,7 @@ function (OptionsResolver $resolver) { function (OptionsResolver $resolver) { $resolver ->setDefault('foo', null) - ->setDeprecated('foo', function ($value) { + ->setDeprecated('foo', function (Options $options, $value) { return ''; }) ; @@ -629,6 +648,27 @@ function (OptionsResolver $resolver) { array('foo' => Bar::class), null, ); + + yield 'It deprecates value depending on other option value' => array( + function (OptionsResolver $resolver) { + $resolver + ->setDefault('widget', null) + ->setDefault('date_format', null) + ->setDeprecated('date_format', function (Options $options, $dateFormat) { + if (null !== $dateFormat && 'single_text' === $options['widget']) { + return 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.'; + } + + return ''; + }) + ; + }, + array('widget' => 'single_text', 'date_format' => 2), + array( + 'type' => E_USER_DEPRECATED, + 'message' => 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.', + ), + ); } /** pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy