diff --git a/UPGRADE-5.1.md b/UPGRADE-5.1.md index fd789861a8c2b..caab7bce14893 100644 --- a/UPGRADE-5.1.md +++ b/UPGRADE-5.1.md @@ -1,6 +1,13 @@ UPGRADE FROM 5.0 to 5.1 ======================= +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 + Console ------- diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index 19fd5f4c15292..90e5f0d0b0650 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -1,6 +1,13 @@ UPGRADE FROM 5.x to 6.0 ======================= +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. + Console ------- diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index 14743e474bf00..60ce63796e9fd 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +5.1.0 +----- + + * 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 + 5.0.0 ----- diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index d4fa55c0ab99c..ddb104fd2f085 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -227,7 +227,8 @@ protected function finalizeValue($value) } if ($child->isDeprecated()) { - trigger_deprecation('', '', $child->getDeprecationMessage($name, $this->getPath())); + $deprecation = $child->getDeprecation($name, $this->getPath()); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } try { diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php index b52769c2e5f2e..28e808f1b5e3c 100644 --- a/src/Symfony/Component/Config/Definition/BaseNode.php +++ b/src/Symfony/Component/Config/Definition/BaseNode.php @@ -35,7 +35,7 @@ abstract class BaseNode implements NodeInterface protected $finalValidationClosures = []; protected $allowOverwrite = true; protected $required = false; - protected $deprecationMessage = null; + protected $deprecation = []; protected $equivalentValues = []; protected $attributes = []; protected $pathSeparator; @@ -198,12 +198,41 @@ public function setRequired(bool $boolean) /** * Sets this node as deprecated. * + * @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 $message The deprecation message to use + * * You can use %node% and %path% placeholders in your message to display, * respectively, the node name and its complete path. */ - public function setDeprecated(?string $message) + public function setDeprecated(?string $package/*, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) { - $this->deprecationMessage = $message; + $args = \func_get_args(); + + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + if (!isset($args[0])) { + trigger_deprecation('symfony/config', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); + + $this->deprecation = []; + + return; + } + + $message = (string) $args[0]; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; } /** @@ -249,7 +278,7 @@ public function isRequired() */ public function isDeprecated() { - return null !== $this->deprecationMessage; + return (bool) $this->deprecation; } /** @@ -259,10 +288,27 @@ public function isDeprecated() * @param string $path the path of the node * * @return string + * + * @deprecated since Symfony 5.1, use "getDeprecation()" instead. */ public function getDeprecationMessage(string $node, string $path) { - return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]); + trigger_deprecation('symfony/config', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); + + return $this->getDeprecation($node, $path)['message']; + } + + /** + * @param string $node The configuration node name + * @param string $path The path of the node + */ + public function getDeprecation(string $node, string $path): array + { + return [ + 'package' => $this->deprecation['package'] ?? '', + 'version' => $this->deprecation['version'] ?? '', + 'message' => strtr($this->deprecation['message'] ?? '', ['%node%' => $node, '%path%' => $path]), + ]; } /** diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 1e11ea520afce..1668e8fcc4bfe 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -435,10 +435,13 @@ protected function createNode() $node->addEquivalentValue(false, $this->falseEquivalent); $node->setPerformDeepMerging($this->performDeepMerging); $node->setRequired($this->required); - $node->setDeprecated($this->deprecationMessage); $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); $node->setNormalizeKeys($this->normalizeKeys); + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } + if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); $node->setXmlRemappings($this->normalization->remappings); diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php index 36519908c0b25..2551abc5fdebc 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -28,7 +28,7 @@ abstract class NodeDefinition implements NodeParentInterface protected $defaultValue; protected $default = false; protected $required = false; - protected $deprecationMessage = null; + protected $deprecation = []; protected $merge; protected $allowEmptyValue = true; protected $nullEquivalent; @@ -159,14 +159,35 @@ public function isRequired() /** * Sets the node as deprecated. * + * @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 $message The deprecation message to use + * * You can use %node% and %path% placeholders in your message to display, * respectively, the node name and its complete path. * * @return $this */ - public function setDeprecated(string $message = 'The child node "%node%" at path "%path%" is deprecated.') + public function setDeprecated(/* string $package, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) { - $this->deprecationMessage = $message; + $args = \func_get_args(); + + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + $message = $args[0] ?? 'The child node "%node%" at path "%path%" is deprecated.'; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; return $this; } diff --git a/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php index 39a564f4cdb76..5f1254c959f0c 100644 --- a/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php @@ -54,7 +54,10 @@ protected function createNode() $node->addEquivalentValue(true, $this->trueEquivalent); $node->addEquivalentValue(false, $this->falseEquivalent); $node->setRequired($this->required); - $node->setDeprecated($this->deprecationMessage); + + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } if (null !== $this->validation) { $node->setFinalValidationClosures($this->validation->rules); diff --git a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php index b7659a364109d..b86f7e77b57d8 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -148,7 +148,8 @@ private function writeNode(NodeInterface $node, int $depth = 0, bool $root = fal } if ($child->isDeprecated()) { - $comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath())); + $deprecation = $child->getDeprecation($child->getName(), $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } if ($child instanceof EnumNode) { diff --git a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php index 1046b0ac2de15..70f740e973254 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php @@ -120,7 +120,8 @@ private function writeNode(NodeInterface $node, NodeInterface $parentNode = null // deprecated? if ($node->isDeprecated()) { - $comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath())); + $deprecation = $node->getDeprecation($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } // example diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index fa91b47b51886..a48b9ad32c681 100644 --- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -12,12 +12,15 @@ namespace Symfony\Component\Config\Tests\Definition; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Config\Definition\ArrayNode; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\ScalarNode; class ArrayNodeTest extends TestCase { + use ExpectDeprecationTrait; + public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() { $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException'); @@ -227,10 +230,13 @@ public function testGetDefaultValueWithoutDefaultValue() public function testSetDeprecated() { $childNode = new ArrayNode('foo'); - $childNode->setDeprecated('"%node%" is deprecated'); + $childNode->setDeprecated('vendor/package', '1.1', '"%node%" is deprecated'); $this->assertTrue($childNode->isDeprecated()); - $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + $deprecation = $childNode->getDeprecation($childNode->getName(), $childNode->getPath()); + $this->assertSame('"foo" is deprecated', $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); $node = new ArrayNode('root'); $node->addChild($childNode); @@ -256,6 +262,37 @@ public function testSetDeprecated() $this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); } + /** + * @group legacy + */ + public function testUnDeprecateANode() + { + $this->expectDeprecation('Since symfony/config 5.1: The signature of method "Symfony\Component\Config\Definition\BaseNode::setDeprecated()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.'); + $this->expectDeprecation('Since symfony/config 5.1: Passing a null message to un-deprecate a node is deprecated.'); + + $node = new ArrayNode('foo'); + $node->setDeprecated('"%node%" is deprecated'); + $node->setDeprecated(null); + + $this->assertFalse($node->isDeprecated()); + } + + /** + * @group legacy + */ + public function testSetDeprecatedWithoutPackageAndVersion() + { + $this->expectDeprecation('Since symfony/config 5.1: The signature of method "Symfony\Component\Config\Definition\BaseNode::setDeprecated()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.'); + + $node = new ArrayNode('foo'); + $node->setDeprecated('"%node%" is deprecated'); + + $deprecation = $node->getDeprecation($node->getName(), $node->getPath()); + $this->assertSame('"foo" is deprecated', $deprecation['message']); + $this->assertSame('', $deprecation['package']); + $this->assertSame('', $deprecation['version']); + } + /** * @dataProvider getDataWithIncludedExtraKeys */ diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index c0b10055430a6..20ba6f35a21b2 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Config\Tests\Definition\Builder; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeDefinition; @@ -21,6 +22,8 @@ class ArrayNodeDefinitionTest extends TestCase { + use ExpectDeprecationTrait; + public function testAppendingSomeNode() { $parent = new ArrayNodeDefinition('root'); @@ -332,13 +335,37 @@ public function testSetDeprecated() $node = new ArrayNodeDefinition('root'); $node ->children() - ->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end() + ->arrayNode('foo')->setDeprecated('vendor/package', '1.1', 'The "%path%" node is deprecated.')->end() + ->end() + ; + $deprecatedNode = $node->getNode()->getChildren()['foo']; + + $this->assertTrue($deprecatedNode->isDeprecated()); + $deprecation = $deprecatedNode->getDeprecation($deprecatedNode->getName(), $deprecatedNode->getPath()); + $this->assertSame('The "root.foo" node is deprecated.', $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); + } + + /** + * @group legacy + */ + public function testSetDeprecatedWithoutPackageAndVersion() + { + $this->expectDeprecation('Since symfony/config 5.1: The signature of method "Symfony\Component\Config\Definition\Builder\NodeDefinition::setDeprecated()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.'); + $node = new ArrayNodeDefinition('root'); + $node + ->children() + ->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end() ->end() ; $deprecatedNode = $node->getNode()->getChildren()['foo']; $this->assertTrue($deprecatedNode->isDeprecated()); - $this->assertSame('The "root.foo" node is deprecated.', $deprecatedNode->getDeprecationMessage($deprecatedNode->getName(), $deprecatedNode->getPath())); + $deprecation = $deprecatedNode->getDeprecation($deprecatedNode->getName(), $deprecatedNode->getPath()); + $this->assertSame('The "root.foo" node is deprecated.', $deprecation['message']); + $this->assertSame('', $deprecation['package']); + $this->assertSame('', $deprecation['version']); } public function testCannotBeEmptyOnConcreteNode() diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php index 6f568a2df64f7..58a2a4272b479 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php @@ -27,11 +27,14 @@ public function testCannotBeEmptyThrowsAnException() public function testSetDeprecated() { $def = new BooleanNodeDefinition('foo'); - $def->setDeprecated('The "%path%" node is deprecated.'); + $def->setDeprecated('vendor/package', '1.1', 'The "%path%" node is deprecated.'); $node = $def->getNode(); $this->assertTrue($node->isDeprecated()); - $this->assertSame('The "foo" node is deprecated.', $node->getDeprecationMessage($node->getName(), $node->getPath())); + $deprecation = $node->getDeprecation($node->getName(), $node->getPath()); + $this->assertSame('The "foo" node is deprecated.', $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); } } diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php index 2e43a1354de11..80705837ab3f2 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php @@ -63,11 +63,14 @@ public function testSetDeprecated() { $def = new EnumNodeDefinition('foo'); $def->values(['foo', 'bar']); - $def->setDeprecated('The "%path%" node is deprecated.'); + $def->setDeprecated('vendor/package', '1.1', 'The "%path%" node is deprecated.'); $node = $def->getNode(); $this->assertTrue($node->isDeprecated()); - $this->assertSame('The "foo" node is deprecated.', $def->getNode()->getDeprecationMessage($node->getName(), $node->getPath())); + $deprecation = $def->getNode()->getDeprecation($node->getName(), $node->getPath()); + $this->assertSame('The "foo" node is deprecated.', $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); } } diff --git a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php index 5bc961bab65cf..1c3c94ad34ed4 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php @@ -38,8 +38,8 @@ private function getConfigurationAsString() return str_replace("\n", PHP_EOL, <<<'EOL' - - + + setDeprecated('"%node%" is deprecated'); + $childNode->setDeprecated('vendor/package', '1.1', '"%node%" is deprecated'); $this->assertTrue($childNode->isDeprecated()); - $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + $deprecation = $childNode->getDeprecation($childNode->getName(), $childNode->getPath()); + $this->assertSame('"foo" is deprecated', $deprecation['message']); + $this->assertSame('vendor/package', $deprecation['package']); + $this->assertSame('1.1', $deprecation['version']); $node = new ArrayNode('root'); $node->addChild($childNode); diff --git a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php index 21b2345ea01af..fb7a31b01bc53 100644 --- a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php +++ b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php @@ -34,8 +34,8 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('scalar_array_empty')->defaultValue([])->end() ->scalarNode('scalar_array_defaults')->defaultValue(['elem1', 'elem2'])->end() ->scalarNode('scalar_required')->isRequired()->end() - ->scalarNode('scalar_deprecated')->setDeprecated()->end() - ->scalarNode('scalar_deprecated_with_message')->setDeprecated('Deprecation custom message for "%node%" at "%path%"')->end() + ->scalarNode('scalar_deprecated')->setDeprecated('vendor/package', '1.1')->end() + ->scalarNode('scalar_deprecated_with_message')->setDeprecated('vendor/package', '1.1', 'Deprecation custom message for "%node%" at "%path%"')->end() ->scalarNode('node_with_a_looong_name')->end() ->enumNode('enum_with_default')->values(['this', 'that'])->defaultValue('this')->end() ->enumNode('enum')->values(['this', 'that'])->end()