You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feature #27277 [OptionsResolver] Introduce ability to deprecate options, allowed types and values (yceruto)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[OptionsResolver] Introduce ability to deprecate options, allowed types and values
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #27216
| License | MIT
| Doc PR | symfony/symfony-docs#9859
**Deprecating an option**
```php
$resolver = (new OptionsResolver())
->setDefined(['foo', 'bar'])
->setDeprecated('foo')
;
$resolver->resolve(['foo' => 'baz']); // PHP Deprecated: The option "foo" is deprecated.
```
With custom message:
```php
$resolver = (new OptionsResolver())
->setDefined('foo')
->setDefault('bar', function (Options $options) {
return $options['foo'];
})
->setDeprecated('foo', 'The option "foo" is deprecated, use "bar" option instead.')
;
$resolver->resolve(['foo' => 'baz']); // PHP Deprecated: The option "foo" is deprecated, use "bar" option instead.
$resolver->resolve(['bar' => 'baz']); // OK.
```
**Deprecating allowed types**
```php
$resolver = (new OptionsResolver())
->setDefault('type', null)
->setAllowedTypes('type', ['null', 'string', FormTypeInterface::class])
->setDeprecated('type', function ($value) {
if ($value instanceof FormTypeInterface) {
return sprintf('Passing an instance of "%s" to option "type" is deprecated, pass its FQCN instead.', FormTypeInterface::class);
}
})
;
$resolver->resolve(['type' => new ChoiceType()]); // PHP Deprecated: Passing an instance of "Symfony\Component\Form\FormTypeInterface" to option "type" is deprecated, pass its FQCN instead.
$resolver->resolve(['type' => ChoiceType::class]); // OK.
```
The closure is invoked when `resolve()` is called. The closure must return a string (the deprecation message) or an empty string to ignore the option deprecation.
Multiple types and normalizer:
```php
$resolver = (new OptionsResolver())
->setDefault('percent', 0.0)
->setAllowedTypes('percent', ['null', 'int', 'float'])
->setDeprecated('percent', function ($value) {
if (null === $value) {
return 'Passing "null" to option "percent" is deprecated, pass a float number instead.';
}
if (is_int($value)) {
return sprintf('Passing an integer "%d" to option "percent" is deprecated, pass a float number instead.', $value);
}
})
->setNormalizer('percent', function (Options $options, $value) {
return (float) $value;
})
;
$resolver->resolve(['percent' => null]); // PHP Deprecated: Passing "null" to option "percent" is deprecated, pass a float number instead.
$resolver->resolve(['percent' => 20]); // PHP Deprecated: Passing an integer "20" to option "percent" is deprecated, pass a float number instead.
$resolver->resolve(['percent' => 20.0]); // OK.
```
The parameter passed to the closure is the value of the option after validating it and before normalizing it.
**Deprecating allowed values**
```php
$resolver = (new OptionsResolver())
->setDefault('percent', 0.0)
->setAllowedTypes('percent', 'float')
->setDeprecated('percent', function ($value) {
if ($value < 0) {
return 'Passing a number less than 0 to option "percent" is deprecated.';
}
})
;
$resolver->resolve(['percent' => -50.0]); // PHP Deprecated: Passing a number less than 0 to option "percent" is deprecated.
```
Commits
-------
f8746ce Add ability to deprecate options
@@ -75,6 +76,11 @@ class OptionsResolver implements Options
75
76
*/
76
77
private$calling = array();
77
78
79
+
/**
80
+
* A list of deprecated options.
81
+
*/
82
+
private$deprecated = array();
83
+
78
84
/**
79
85
* Whether the instance is locked for reading.
80
86
*
@@ -348,6 +354,57 @@ public function getDefinedOptions()
348
354
returnarray_keys($this->defined);
349
355
}
350
356
357
+
/**
358
+
* Deprecates an option, allowed types or values.
359
+
*
360
+
* Instead of passing the message, you may also pass a closure with the
361
+
* following signature:
362
+
*
363
+
* function ($value) {
364
+
* // ...
365
+
* }
366
+
*
367
+
* The closure receives the value as argument and should return a string.
368
+
* Returns an empty string to ignore the option deprecation.
369
+
*
370
+
* The closure is invoked when {@link resolve()} is called. The parameter
371
+
* passed to the closure is the value of the option after validating it
372
+
* and before normalizing it.
373
+
*
374
+
* @param string|\Closure $deprecationMessage
375
+
*/
376
+
publicfunctionsetDeprecated(string$option, $deprecationMessage = 'The option "%name%" is deprecated.'): self
377
+
{
378
+
if ($this->locked) {
379
+
thrownewAccessException('Options cannot be deprecated from a lazy option or normalizer.');
380
+
}
381
+
382
+
if (!isset($this->defined[$option])) {
383
+
thrownewUndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
384
+
}
385
+
386
+
if (!\is_string($deprecationMessage) && !$deprecationMessageinstanceof \Closure) {
387
+
thrownewInvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', \gettype($deprecationMessage)));
388
+
}
389
+
390
+
// ignore if empty string
391
+
if ('' === $deprecationMessage) {
392
+
return$this;
393
+
}
394
+
395
+
$this->deprecated[$option] = $deprecationMessage;
396
+
397
+
// Make sure the option is processed
398
+
unset($this->resolved[$option]);
399
+
400
+
return$this;
401
+
}
402
+
403
+
publicfunctionisDeprecated(string$option): bool
404
+
{
405
+
returnisset($this->deprecated[$option]);
406
+
}
407
+
351
408
/**
352
409
* Sets the normalizer for an option.
353
410
*
@@ -620,6 +677,7 @@ public function clear()
620
677
$this->normalizers = array();
621
678
$this->allowedTypes = array();
622
679
$this->allowedValues = array();
680
+
$this->deprecated = array();
623
681
624
682
return$this;
625
683
}
@@ -836,6 +894,19 @@ public function offsetGet($option)
836
894
}
837
895
}
838
896
897
+
// Check whether the option is deprecated
898
+
if (isset($this->deprecated[$option])) {
899
+
$deprecationMessage = $this->deprecated[$option];
900
+
901
+
if ($deprecationMessageinstanceof \Closure && !\is_string($deprecationMessage = $deprecationMessage($value))) {
902
+
thrownewInvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", returns an empty string to ignore.', \gettype($deprecationMessage)));
0 commit comments