mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-23 16:13:50 +00:00
[export][rector] first rector rule to add new method to filters
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Utils\Rector\Rector;
|
||||
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use PhpParser\Node;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Symfony\NodeAnalyzer\ClassAnalyzer;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
class ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector extends AbstractRector
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClassAnalyzer $classAnalyzer,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition(
|
||||
'Add a getFormDefault data method on exports, filters and aggregators, filled with default data',
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Node\Stmt\Class_::class];
|
||||
}
|
||||
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (!$node instanceof Node\Stmt\Class_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->classAnalyzer->hasImplements($node, FilterInterface::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$buildFormStmtIndex = null;
|
||||
$hasGetFormDefaultDataMethod = false;
|
||||
foreach ($node->stmts as $k => $stmt) {
|
||||
if (!$stmt instanceof Node\Stmt\ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('buildForm' === $stmt->name->name) {
|
||||
$buildFormStmtIndex = $k;
|
||||
}
|
||||
|
||||
if ('getFormDefaultData' === $stmt->name->name) {
|
||||
$hasGetFormDefaultDataMethod = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasGetFormDefaultDataMethod || null === $buildFormStmtIndex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmtBefore = array_slice($node->stmts, 0, $buildFormStmtIndex, false);
|
||||
$stmtAfter = array_slice($node->stmts, $buildFormStmtIndex + 1);
|
||||
|
||||
// lines to satisfay phpstan parser
|
||||
if (!$node->stmts[$buildFormStmtIndex] instanceof Node\Stmt\ClassMethod) {
|
||||
throw new \LogicException();
|
||||
}
|
||||
|
||||
['build_form_method' => $buildFormMethod, 'empty_to_replace' => $emptyToReplace]
|
||||
= $this->filterBuildFormMethod($node->stmts[$buildFormStmtIndex]);
|
||||
|
||||
$node->stmts = [
|
||||
...$stmtBefore,
|
||||
$buildFormMethod,
|
||||
$this->makeGetFormDefaultData($node->stmts[$buildFormStmtIndex], $emptyToReplace),
|
||||
...$stmtAfter,
|
||||
];
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function makeGetFormDefaultData(Node\Stmt\ClassMethod $buildFormMethod, array $emptyToReplace): Node\Stmt\ClassMethod
|
||||
{
|
||||
$method = new Node\Stmt\ClassMethod('getFormDefaultData');
|
||||
$method->flags = Node\Stmt\Class_::MODIFIER_PUBLIC;
|
||||
$method->returnType = new Node\Identifier('array');
|
||||
|
||||
$data = new Node\Expr\Array_([]);
|
||||
|
||||
foreach ($emptyToReplace as $key => $value) {
|
||||
$item = new Node\Expr\ArrayItem($value, new Node\Scalar\String_($key));
|
||||
$data->items[] = $item;
|
||||
}
|
||||
|
||||
$method->stmts[] = new Node\Stmt\Return_($data);
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node\Stmt\ClassMethod $buildFormMethod
|
||||
* @return array{"build_form_method": Node\Stmt\ClassMethod, "empty_to_replace": array<string, mixed>}
|
||||
*/
|
||||
private function filterBuildFormMethod(Node\Stmt\ClassMethod $buildFormMethod): array
|
||||
{
|
||||
$builderName = $buildFormMethod->params[0]->var->name;
|
||||
|
||||
$newStmts = [];
|
||||
$emptyDataToReplace = [];
|
||||
|
||||
foreach ($buildFormMethod->stmts as $stmt) {
|
||||
if ($stmt instanceof Node\Stmt\Expression
|
||||
// it must be a method call
|
||||
&& $stmt->expr instanceof Node\Expr\MethodCall
|
||||
// the method call must be "add"
|
||||
&& $stmt->expr->name instanceof Node\Identifier
|
||||
&& $stmt->expr->name->name === 'add'
|
||||
// and the method call must apply on the builder (compare with builderName)
|
||||
&& $stmt->expr->var instanceof Node\Expr\Variable
|
||||
&& $stmt->expr->var->name === $builderName
|
||||
// it must have a first argument, a string
|
||||
// TODO what happens if a value, or a const ?
|
||||
&& ($stmt->expr->args[0] ?? null) instanceof Node\Arg
|
||||
&& $stmt->expr->args[0]->value instanceof Node\Scalar\String_
|
||||
// and a third argument, an array
|
||||
&& ($stmt->expr->args[2] ?? null) instanceof Node\Arg
|
||||
&& $stmt->expr->args[2]->value instanceof Node\Expr\Array_
|
||||
) {
|
||||
|
||||
// we parse on the 3rd argument, to find if there is an 'empty_data' key
|
||||
$emptyDataIndex = null;
|
||||
foreach ($stmt->expr->args[2]->value->items as $arrayItemIndex => $item) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
if ($item->key->value === 'data') {
|
||||
$k = $stmt->expr->args[0]->value->value;
|
||||
$emptyDataToReplace[$k] = $item->value;
|
||||
$emptyDataIndex = $arrayItemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $emptyDataIndex) {
|
||||
$stmt->expr->args[2]->value->items = array_values(
|
||||
array_filter(
|
||||
$stmt->expr->args[2]->value->items,
|
||||
/* @phpstan-ignore-next-line */
|
||||
fn (Node\Expr\ArrayItem $item) => $item->key->value !== 'data'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$newStmts[] = $stmt;
|
||||
} else {
|
||||
$newStmts[] = $stmt;
|
||||
}
|
||||
}
|
||||
|
||||
$buildFormMethod->stmts = $newStmts;
|
||||
|
||||
return ['build_form_method' => $buildFormMethod, "empty_to_replace" => $emptyDataToReplace];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user