-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[RFC][OptionsResolver] Add ability to deprecate options #27216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
What if only certain usage scenario are deprecated? Could add a 3rd argument that receives a callable and acts based on the value passed to the option resolver (not the resolved value) |
@iltar That could be a good Idea! I think that's possible too and cover other use case like deprecating just one value/type from many feasible. I updated the description to include that, thanks. |
It's an interesting feature, it would be nice to make possible to cover all the aspects supported by the component, like argument values and its types. |
@phansys yes, argument values and their types are already covered by @iltar's idea: into callback func you could do that, e.g: ->setDeprecated('foo', 'Passing "null", an integer or instance of "\Bar" to option "foo" is deprecated since ...', function ($value) {
return null === $value || is_int($value) || $value instanceof \Bar;
}) |
Although to be more accurate, probably we should accept a ->setDeprecated('foo', function ($value) {
if (null === $value) {
$str = '"null"';
} elseif (is_int($value)) {
$str = sprintf('an integer "%d"', $value);
} elseif ($value instanceof \Bar) {
$str = sprintf('an instance of \Bar "%s"', get_class($value));
} else {
return;
}
return sprintf('Passing %s to option "foo" is deprecated.', $str);
}); |
I like more to have three arguments each one with its purpose than have two. |
I think the purpose of the 2nd argument can be documented as well, or better: /**
* @param string|callable $deprecationMessage The option deprecation message or function
* that accept the value and must return a string
* or anything else to skip
*/ In fact, that'll simplify a bit the implementation as we only have to save two data: option name (string) and deprecation message (string or callable), so with one internal variable I can send the PR this week if there is no one against, it feels like enough 👍 to me :) |
PR created #27277 reviews welcomed |
…ns, 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
Introduction
Currently the consumers of the
OptionsResolver
component (mainly theForm
component, laterLDAP
started to use it and possibly many bundles and libraries too) need sometime to delete or rename an already defined option or change its default value keeping BC, seeing itself in the need to create notes like this:symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
Line 302 in 07d2570
and manage themselves where and how to trigger the corresponding depreciation message:
symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
Lines 250 to 257 in 07d2570
Moreover, it isn't always effective if the option has a default value. For instance, according to the previous code doing this:
it wouldn't show the deprecation message because when the configured default value is equal to the given value -> we don't know that this option is being used. Also, the normalizer function (as just one is allowed) can be overridden by any current child type or type extension in userland, in that case, the message wouldn't show either.
Proposal to improve the current scenario
Introduce a simpler and safer way to deprecate an option using a new method in
OptionsResolver
:Then, if this option is used or has default value, the deprecation message always will be triggered. Conditionally you could deprecate some values of many feasible by using the 3rd argument.
It is a small but powerful feature as this information can be displayed anywhere, in the form profiler,
debug:form
command, etc.Other method that could help to know whether a given option is deprecated or not is:
Example
Deprecating a whole option:
Deprecating an option value:
Let me know first if it's something that core & community need. I've prepared a tiny PR for that.
Cheers!
The text was updated successfully, but these errors were encountered: