Skip to content

[OptionsResolver] Improve the deprecation feature by handling package and version #36345

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions UPGRADE-5.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Config
* The signature of method `NodeDefinition::setDeprecated()` has been updated to `NodeDefinition::setDeprecation(string $package, string $version, string $message)`.
* The signature of method `BaseNode::setDeprecated()` has been updated to `BaseNode::setDeprecation(string $package, string $version, string $message)`.
* Passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node is deprecated
* Deprecated `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead

Console
-------
Expand All @@ -21,6 +22,8 @@ DependencyInjection
* The signature of method `DeprecateTrait::deprecate()` has been updated to `DeprecateTrait::deprecation(string $package, string $version, string $message)`.
* Deprecated the `Psr\Container\ContainerInterface` and `Symfony\Component\DependencyInjection\ContainerInterface` aliases of the `service_container` service,
configure them explicitly instead.
* Deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead.
* Deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead.

Dotenv
------
Expand Down Expand Up @@ -86,6 +89,12 @@ Notifier
* [BC BREAK] The `EmailMessage::fromNotification()` and `SmsMessage::fromNotification()`
methods' `$transport` argument was removed.

OptionsResolver
---------------

* The signature of method `OptionsResolver::setDeprecated()` has been updated to `OptionsResolver::setDeprecated(string $option, string $package, string $version, $message)`.
* Deprecated `OptionsResolverIntrospector::getDeprecationMessage()`, use `OptionsResolverIntrospector::getDeprecation()` instead.

PhpUnitBridge
-------------

Expand Down
9 changes: 9 additions & 0 deletions UPGRADE-6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Config
* The signature of method `NodeDefinition::setDeprecated()` has been updated to `NodeDefinition::setDeprecation(string $package, string $version, string $message)`.
* The signature of method `BaseNode::setDeprecated()` has been updated to `BaseNode::setDeprecation(string $package, string $version, string $message)`.
* Passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node is not supported anymore.
* Removed `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead.

Console
-------
Expand All @@ -21,6 +22,8 @@ DependencyInjection
* The signature of method `DeprecateTrait::deprecate()` has been updated to `DeprecateTrait::deprecation(string $package, string $version, string $message)`.
* Removed the `Psr\Container\ContainerInterface` and `Symfony\Component\DependencyInjection\ContainerInterface` aliases of the `service_container` service,
configure them explicitly instead.
* Removed `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead.
* Removed `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead.

Dotenv
------
Expand Down Expand Up @@ -69,6 +72,12 @@ Messenger
* The signature of method `RetryStrategyInterface::isRetryable()` has been updated to `RetryStrategyInterface::isRetryable(Envelope $message, \Throwable $throwable = null)`.
* The signature of method `RetryStrategyInterface::getWaitingTime()` has been updated to `RetryStrategyInterface::getWaitingTime(Envelope $message, \Throwable $throwable = null)`.

OptionsResolver
---------------

* The signature of method `OptionsResolver::setDeprecated()` has been updated to `OptionsResolver::setDeprecated(string $option, string $package, string $version, $message)`.
* Removed `OptionsResolverIntrospector::getDeprecationMessage()`, use `OptionsResolverIntrospector::getDeprecation()` instead.

PhpUnitBridge
-------------

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Config/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* updated the signature of method `NodeDefinition::setDeprecated()` to `NodeDefinition::setDeprecation(string $package, string $version, string $message)`
* updated the signature of method `BaseNode::setDeprecated()` to `BaseNode::setDeprecation(string $package, string $version, string $message)`
* deprecated passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node
* deprecated `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead

5.0.0
-----
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ CHANGELOG
configure them explicitly instead
* added class `Symfony\Component\DependencyInjection\Dumper\Preloader` to help with preloading on PHP 7.4+
* added tags `container.preload`/`.no_preload` to declare extra classes to preload/services to not preload
* deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead
* deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead

5.0.0
-----
Expand Down
8 changes: 5 additions & 3 deletions src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ protected function getOptionDefinition(OptionsResolver $optionsResolver, string
'allowedTypes' => 'getAllowedTypes',
'allowedValues' => 'getAllowedValues',
'normalizers' => 'getNormalizers',
'deprecationMessage' => 'getDeprecationMessage',
'deprecation' => 'getDeprecation',
];

foreach ($map as $key => $method) {
Expand All @@ -140,8 +140,10 @@ protected function getOptionDefinition(OptionsResolver $optionsResolver, string
}
}

if (isset($definition['deprecationMessage']) && \is_string($definition['deprecationMessage'])) {
$definition['deprecationMessage'] = strtr($definition['deprecationMessage'], ['%name%' => $option]);
if (isset($definition['deprecation']) && isset($definition['deprecation']['message']) && \is_string($definition['deprecation']['message'])) {
$definition['deprecationMessage'] = strtr($definition['deprecation']['message'], ['%name%' => $option]);
$definition['deprecationPackage'] = $definition['deprecation']['package'];
$definition['deprecationVersion'] = $definition['deprecation']['version'];
}

return $definition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
if ($definition['deprecated']) {
$map = [
'Deprecated' => 'deprecated',
'Deprecation package' => 'deprecationPackage',
'Deprecation version' => 'deprecationVersion',
'Deprecation message' => 'deprecationMessage',
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('foo');
$resolver->setDefined('bar');
$resolver->setDeprecated('bar');
$resolver->setDeprecated('bar', 'vendor/package', '1.1');
$resolver->setDefault('empty_data', function (Options $options) {
$foo = $options['foo'];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('foo');
$resolver->setDefined('bar');
$resolver->setDeprecated('bar');
$resolver->setDeprecated('bar', 'vendor/package', '1.1');
$resolver->setDefault('empty_data', function (Options $options, $value) {
$foo = $options['foo'];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (bar)

--------------------- -----------------------------------
Deprecated true
--------------------- -----------------------------------
Deprecation package "vendor/package"
--------------------- -----------------------------------
Deprecation version "1.1"
--------------------- -----------------------------------
Deprecation message "The option "bar" is deprecated."
--------------------- -----------------------------------
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/OptionsResolver/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CHANGELOG

* added fluent configuration of options using `OptionResolver::define()`
* added `setInfo()` and `getInfo()` methods
* updated the signature of method `OptionsResolver::setDeprecated()` to `OptionsResolver::setDeprecation(string $option, string $package, string $version, $message)`
* deprecated `OptionsResolverIntrospector::getDeprecationMessage()`, use `OptionsResolverIntrospector::getDeprecation()` instead

5.0.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,20 @@ public function getNormalizers(string $option): array
* @return string|\Closure
*
* @throws NoConfigurationException on no configured deprecation
*
* @deprecated since Symfony 5.1, use "getDeprecation()" instead.
*/
public function getDeprecationMessage(string $option)
{
trigger_deprecation('symfony/options-resolver', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__);

return $this->getDeprecation($option)['message'];
}

/**
* @throws NoConfigurationException on no configured deprecation
*/
public function getDeprecation(string $option): array
{
return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
}
Expand Down
23 changes: 19 additions & 4 deletions src/Symfony/Component/OptionsResolver/OptionConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,28 @@ public function define(string $option): self
/**
* Marks this option as deprecated.
*
* @return $this
* @param string $package The name of the composer package that is triggering the deprecation
* @param string $version The version of the package that introduced the deprecation
* @param string|\Closure $message The deprecation message to use
*
* @param string|\Closure $deprecationMessage
* @return $this
*/
public function deprecated($deprecationMessage = 'The option "%name%" is deprecated.'): self
public function deprecated(/*string $package, string $version, $message = 'The option "%name%" is deprecated.'*/): self
{
$this->resolver->setDeprecated($this->name, $deprecationMessage);
$args = \func_get_args();

if (\func_num_args() < 2) {
trigger_deprecation('symfony/options-resolver', '5.1', 'The signature of method "%s()" requires 2 new arguments: "string $package, string $version", not defining them is deprecated.', __METHOD__);

$message = $args[0] ?? 'The option "%name%" is deprecated.';
$package = (string) $version = '';
} else {
$package = (string) $args[0];
$version = (string) $args[1];
$message = (string) ($args[2] ?? 'The option "%name%" is deprecated.');
}

$this->resolver->setDeprecated($this->name, $package, $version, $message);

return $this;
}
Expand Down
48 changes: 34 additions & 14 deletions src/Symfony/Component/OptionsResolver/OptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,11 @@ public function isNested(string $option): bool
* passed to the closure is the value of the option after validating it
* and before normalizing it.
*
* @param string|\Closure $deprecationMessage
* @param string $package The name of the composer package that is triggering the deprecation
* @param string $version The version of the package that introduced the deprecation
* @param string|\Closure $message The deprecation message to use
*/
public function setDeprecated(string $option, $deprecationMessage = 'The option "%name%" is deprecated.'): self
public function setDeprecated(string $option/*, string $package, string $version, $message = 'The option "%name%" is deprecated.' */): self
{
if ($this->locked) {
throw new AccessException('Options cannot be deprecated from a lazy option or normalizer.');
Expand All @@ -433,16 +435,33 @@ public function setDeprecated(string $option, $deprecationMessage = 'The option
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
}

if (!\is_string($deprecationMessage) && !$deprecationMessage instanceof \Closure) {
throw new InvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', get_debug_type($deprecationMessage)));
$args = \func_get_args();

if (\func_num_args() < 3) {
trigger_deprecation('symfony/options-resolver', '5.1', 'The signature of method "%s()" requires 2 new arguments: "string $package, string $version", not defining them is deprecated.', __METHOD__);

$message = $args[1] ?? 'The option "%name%" is deprecated.';
$package = $version = '';
} else {
$package = $args[1];
$version = $args[2];
$message = $args[3] ?? 'The option "%name%" is deprecated.';
}

if (!\is_string($message) && !$message instanceof \Closure) {
throw new InvalidArgumentException(sprintf('Invalid type for deprecation message argument, expected string or \Closure, but got "%s".', get_debug_type($message)));
}

// ignore if empty string
if ('' === $deprecationMessage) {
if ('' === $message) {
return $this;
}

$this->deprecated[$option] = $deprecationMessage;
$this->deprecated[$option] = [
'package' => $package,
'version' => $version,
'message' => $message,
];

// Make sure the option is processed
unset($this->resolved[$option]);
Expand Down Expand Up @@ -903,8 +922,8 @@ public function offsetGet($option, bool $triggerDeprecation = true)

// Shortcut for resolved options
if (isset($this->resolved[$option]) || \array_key_exists($option, $this->resolved)) {
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option])) {
trigger_deprecation('', '', strtr($this->deprecated[$option], ['%name%' => $option]));
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option]['message'])) {
trigger_deprecation($this->deprecated[$option]['package'], $this->deprecated[$option]['version'], strtr($this->deprecated[$option]['message'], ['%name%' => $option]));
}

return $this->resolved[$option];
Expand Down Expand Up @@ -1048,26 +1067,27 @@ public function offsetGet($option, bool $triggerDeprecation = true)
// Check whether the option is deprecated
// and it is provided by the user or is being called from a lazy evaluation
if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || ($this->calling && \is_string($this->deprecated[$option])))) {
$deprecationMessage = $this->deprecated[$option];
$deprecation = $this->deprecated[$option];
$message = $this->deprecated[$option]['message'];

if ($deprecationMessage instanceof \Closure) {
if ($message 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.', $this->formatOptions(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.', get_debug_type($deprecationMessage)));
if (!\is_string($message = $message($this, $value))) {
throw new InvalidOptionsException(sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', get_debug_type($message)));
}
} finally {
unset($this->calling[$option]);
}
}

if ('' !== $deprecationMessage) {
trigger_deprecation('', '', strtr($deprecationMessage, ['%name%' => $option]));
if ('' !== $message) {
trigger_deprecation($deprecation['package'], $deprecation['version'], strtr($message, ['%name%' => $option]));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ public function testGetNormalizersThrowsOnNotDefinedOption()
$debug->getNormalizers('foo');
}

/**
* @group legacy
*/
public function testGetDeprecationMessage()
{
$resolver = new OptionsResolver();
Expand All @@ -223,6 +226,9 @@ public function testGetDeprecationMessage()
$this->assertSame('The option "foo" is deprecated.', $debug->getDeprecationMessage('foo'));
}

/**
* @group legacy
*/
public function testGetClosureDeprecationMessage()
{
$resolver = new OptionsResolver();
Expand All @@ -233,6 +239,34 @@ public function testGetClosureDeprecationMessage()
$this->assertSame($closure, $debug->getDeprecationMessage('foo'));
}

public function testGetDeprecation()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', 'vendor/package', '1.1', 'The option "foo" is deprecated.');

$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame([
'package' => 'vendor/package',
'version' => '1.1',
'message' => 'The option "foo" is deprecated.',
], $debug->getDeprecation('foo'));
}

public function testGetClosureDeprecation()
{
$resolver = new OptionsResolver();
$resolver->setDefined('foo');
$resolver->setDeprecated('foo', 'vendor/package', '1.1', $closure = function (Options $options, $value) {});

$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame([
'package' => 'vendor/package',
'version' => '1.1',
'message' => $closure,
], $debug->getDeprecation('foo'));
}

public function testGetDeprecationMessageThrowsOnNoConfiguredValue()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\NoConfigurationException');
Expand All @@ -241,7 +275,7 @@ public function testGetDeprecationMessageThrowsOnNoConfiguredValue()
$resolver->setDefined('foo');

$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getDeprecationMessage('foo'));
$debug->getDeprecation('foo');
}

public function testGetDeprecationMessageThrowsOnNotDefinedOption()
Expand All @@ -251,6 +285,6 @@ public function testGetDeprecationMessageThrowsOnNotDefinedOption()
$resolver = new OptionsResolver();

$debug = new OptionsResolverIntrospector($resolver);
$this->assertSame('bar', $debug->getDeprecationMessage('foo'));
$debug->getDeprecation('foo');
}
}
Loading