From eef28137dc5900d922fe1c6bb8cda0669fc0a674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Parafi=C5=84ski?= Date: Wed, 4 Sep 2024 16:34:15 +0200 Subject: [PATCH] IBX-8805: Added Rector for deprecated twig functions & filters (#8) For more details see https://issues.ibexa.co/browse/IBX-8805 and https://github.com/ibexa/rector/pull/8 Key changes: * Added Rector for deprecated twig functions & filters * Dropped dependency on twig --- .../RemoveDeprecatedTwigDefinitionsRector.php | 156 ++++++++++++++++++ .../Fixture/class_with_twig_function.php.inc | 84 ++++++++++ ...oveDeprecatedTwigDefinitionsRectorTest.php | 37 +++++ .../config/configured_rule.php | 19 +++ 4 files changed, 296 insertions(+) create mode 100644 src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php create mode 100644 tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/Fixture/class_with_twig_function.php.inc create mode 100644 tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/RemoveDeprecatedTwigDefinitionsRectorTest.php create mode 100644 tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/config/configured_rule.php diff --git a/src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php b/src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php new file mode 100644 index 0000000..54ddc18 --- /dev/null +++ b/src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php @@ -0,0 +1,156 @@ + ['html'], + 'needs_environment' => true, + 'deprecated' => '4.0', + 'alternative' => 'new_function_name', + ] + ), + new TwigFunction( + 'new_function_name', + null, + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + ] + ), + ]; + } +CODE_SAMPLE + , + <<<'CODE_SAMPLE' + public function getFunctions(): array + { + return [ + new TwigFunction( + 'new_function_name', + null, + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + ] + ), + ]; + } +CODE_SAMPLE + , + ['var_dump'] + )]); + } + + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + public function refactor(Node $node): ?Node + { + // Ensure the method is named "getFunctions" + if (!$node instanceof ClassMethod + || ( + !$this->isName($node, 'getFunctions') + && !$this->isName($node, 'getFilters') + ) + ) { + return null; + } + + if ($node->stmts === null) { + return null; + } + + // Look for the return statement within the method + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Return_ && $stmt->expr instanceof Array_) { + $arrayNode = $stmt->expr; + + // Filter out TwigFunction declarations with the 'deprecated' option + $arrayNode->items = array_filter($arrayNode->items, function (?ArrayItem $item) { + if ($item === null || !$item->value instanceof New_) { + return true; + } + + /** @var \PhpParser\Node\Expr\New_ $newExpr */ + $newExpr = $item->value; + + // Ensure it's a 'Twig\TwigFunction' instantiation + if (!$newExpr->class instanceof Node\Name + || (!$this->isName($newExpr->class, 'Twig\TwigFunction') + && !$this->isName($newExpr->class, 'Twig\TwigFilter')) + ) { + return true; + } + + // Check if the third argument (options array) contains the 'deprecated' key + if (isset($newExpr->args[2]) + && $newExpr->args[2] instanceof Node\Arg + && $newExpr->args[2]->value instanceof Array_ + ) { + $optionsArray = $newExpr->args[2]->value; + + foreach ($optionsArray->items as $optionItem) { + if ($optionItem instanceof ArrayItem && + $optionItem->key instanceof Node\Scalar\String_ && + $optionItem->key->value === 'deprecated' && + $optionItem->value instanceof Node\Scalar\String_ && + $optionItem->value->value === $this->version + ) { + // Skip this item if 'deprecated' is found + return false; + } + } + } + + return true; + }); + } + } + + return $node; + } + + public function configure(array $configuration): void + { + $this->version = $configuration['version'] ?? true; + } +} diff --git a/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/Fixture/class_with_twig_function.php.inc b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/Fixture/class_with_twig_function.php.inc new file mode 100644 index 0000000..178f64a --- /dev/null +++ b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/Fixture/class_with_twig_function.php.inc @@ -0,0 +1,84 @@ + ['html'], + 'needs_environment' => true, + 'deprecated' => '4.0', + 'alternative' => 'new_function_name', + ] + ), + new TwigFunction( + 'new_function_name', + null, + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + ] + ), + ]; + } + + public function getFilters(): array + { + return [ + new TwigFilter( + 'old_filter', + [$this, 'someCallback'], + ['deprecated' => '4.0'] + ), + new TwigFilter( + 'new_filter', + [$this, 'someCallback'], + ), + ]; + } +} + +?> +----- + ['html'], + 'needs_environment' => true, + ] + ), + ]; + } + + public function getFilters(): array + { + return [ + new TwigFilter( + 'new_filter', + [$this, 'someCallback'], + ), + ]; + } +} + +?> diff --git a/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/RemoveDeprecatedTwigDefinitionsRectorTest.php b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/RemoveDeprecatedTwigDefinitionsRectorTest.php new file mode 100644 index 0000000..7fa0c98 --- /dev/null +++ b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/RemoveDeprecatedTwigDefinitionsRectorTest.php @@ -0,0 +1,37 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/config/configured_rule.php b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/config/configured_rule.php new file mode 100644 index 0000000..e13bc18 --- /dev/null +++ b/tests/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector/config/configured_rule.php @@ -0,0 +1,19 @@ +ruleWithConfiguration( + RemoveDeprecatedTwigDefinitionsRector::class, + [ + 'version' => '4.0', + ] + ); +};