Skip to content

Commit

Permalink
IBX-8805: Added Rector for deprecated twig functions & filters
Browse files Browse the repository at this point in the history
  • Loading branch information
ViniTou committed Aug 21, 2024
1 parent 0def678 commit c58fbf4
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 0 deletions.
150 changes: 150 additions & 0 deletions src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?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;
use Twig\TwigFilter;
use Twig\TwigFunction;

/**
* @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;
}

// Look for the return statement within the method
foreach ($node->stmts as $stmt) {

Check failure on line 101 in src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php

View workflow job for this annotation

GitHub Actions / Tests (8.3)

Argument of an invalid type array<PhpParser\Node\Stmt>|null supplied for foreach, only iterables are supported.
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, TwigFunction::class)

Check failure on line 116 in src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php

View workflow job for this annotation

GitHub Actions / Tests (8.3)

Class Twig\TwigFunction not found.
&& !$this->isName($newExpr->class, TwigFilter::class))

Check failure on line 117 in src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php

View workflow job for this annotation

GitHub Actions / Tests (8.3)

Class Twig\TwigFilter not found.
) {
return true;
}

// Check if the third argument (options array) contains the 'deprecated' key
if (isset($newExpr->args[2]) && $newExpr->args[2]->value instanceof Array_) {

Check failure on line 123 in src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php

View workflow job for this annotation

GitHub Actions / Tests (8.3)

Access to an undefined property PhpParser\Node\Arg|PhpParser\Node\VariadicPlaceholder::$value.
$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->value === $this->version

Check failure on line 130 in src/lib/Rule/Internal/RemoveDeprecatedTwigDefinitionsRector.php

View workflow job for this annotation

GitHub Actions / Tests (8.3)

Access to an undefined property PhpParser\Node\Expr::$value.
) {
// 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 c58fbf4

Please sign in to comment.