Skip to content

Commit

Permalink
IBX-8805: Added Rector for deprecated twig functions & filters (#8)
Browse files Browse the repository at this point in the history
For more details see https://issues.ibexa.co/browse/IBX-8805 and #8

Key changes:

* Added Rector for deprecated twig functions & filters

* Dropped dependency on twig
  • Loading branch information
ViniTou authored Sep 4, 2024
1 parent 4e3b809 commit eef2813
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 0 deletions.
156 changes: 156 additions & 0 deletions src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Rector\Rule\Internal;

use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @internal This rule is internal, for Ibexa 1st party packages
*/
final class RemoveDeprecatedTwigDefinitionsRector extends AbstractRector implements ConfigurableRectorInterface
{
/** @var string|int|true */
private $version;

/**
* @throws \Exception
*/
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove legacy eZ Systems & Ibexa class_alias calls', [new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
public function getFunctions(): array
{
return [
new TwigFunction(
'old_function_name',
null,
[
'is_safe' => ['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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php /** @noinspection ALL */

use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;

class SomeClassWithTwigFunctions extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction(
'old_function_name',
null,
[
'is_safe' => ['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'],
),
];
}
}

?>
-----
<?php /** @noinspection ALL */

use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;

class SomeClassWithTwigFunctions extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction(
'new_function_name',
null,
[
'is_safe' => ['html'],
'needs_environment' => true,
]
),
];
}

public function getFilters(): array
{
return [
new TwigFilter(
'new_filter',
[$this, 'someCallback'],
),
];
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Rector\Tests\Rule\Internal\RemoveDeprecatedTwigDefinitionsRector;

use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

/**
* @covers \Ibexa\Rector\Rule\Internal\RemoveDeprecatedTwigDefinitionsRector
*/
final class RemoveDeprecatedTwigDefinitionsRectorTest extends AbstractRectorTestCase
{
/**
* @throws \Rector\Exception\ShouldNotHappenException
*/
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): \Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

use Ibexa\Rector\Rule\Internal\RemoveDeprecatedTwigDefinitionsRector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(
RemoveDeprecatedTwigDefinitionsRector::class,
[
'version' => '4.0',
]
);
};

0 comments on commit eef2813

Please sign in to comment.