From c52ba06ea0a29e1149046ab24d21d53fb2f17453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 16 Jun 2023 15:26:49 +0200 Subject: [PATCH] adapt rector rules for chained builder->add --- ...aultDataOnExportFilterAggregatorRector.php | 113 ++++++++++++------ ...-default-data-with-chained-builder.php.inc | 111 +++++++++++++++++ 2 files changed, 188 insertions(+), 36 deletions(-) create mode 100644 utils/rector/tests/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector/Fixture/filter-multiple-reuse-data-on-form-default-data-with-chained-builder.php.inc diff --git a/utils/rector/src/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector.php b/utils/rector/src/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector.php index fe996c987..cc54a24f1 100644 --- a/utils/rector/src/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector.php +++ b/utils/rector/src/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector.php @@ -183,43 +183,11 @@ PHP */ 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_ + && false !== ($results = $this->handleMethodCallBuilderAdd($stmt->expr, $builderName)) ) { - - // 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; + ['stmt' => $newMethodCAll, 'emptyDataToReplace' => $newEmptyDataToReplace] = $results; + $newStmts[] = new Node\Stmt\Expression($newMethodCAll); + $emptyDataToReplace = [...$emptyDataToReplace, ...$newEmptyDataToReplace]; } else { $newStmts[] = $stmt; } @@ -229,4 +197,77 @@ PHP */ return ['build_form_method' => $buildFormMethod, "empty_to_replace" => $emptyDataToReplace]; } + + private function handleMethodCallBuilderAdd(Node\Expr\MethodCall $methodCall, string $builderName): array|false + { + $emptyDataToReplace = []; + // check for chained method call + if ( + // this means that the MethodCall apply on another method call: a chained + $methodCall->var instanceof Node\Expr\MethodCall + ) { + // as this is chained, we make a recursion on this method + + $resultFormDeepMethodCall = $this->handleMethodCallBuilderAdd($methodCall->var, $builderName); + + if (false === $resultFormDeepMethodCall) { + return false; + } + + ['stmt' => $chainedMethodCall, 'emptyDataToReplace' => $newEmptyDataToReplace] = $resultFormDeepMethodCall; + $emptyDataToReplace = $newEmptyDataToReplace; + $methodCall->var = $chainedMethodCall; + } + + if ( + $methodCall->var instanceof Node\Expr\Variable + ) { + if ($methodCall->var->name !== $builderName) { + // ho, this does not apply on a builder, so we cancel all the method calls + return false; + } + } + + if ($methodCall->name instanceof Node\Identifier && $methodCall->name->name !== 'add') { + return ['stmt' => $methodCall, 'emptyDataToReplace' => $emptyDataToReplace]; + } + + if ( + // the method call must be "add" + $methodCall->name instanceof Node\Identifier + && $methodCall->name->name === 'add' + // it must have a first argument, a string + // TODO what happens if a value, or a const ? + && ($methodCall->args[0] ?? null) instanceof Node\Arg + && $methodCall->args[0]->value instanceof Node\Scalar\String_ + // and a third argument, an array + && ($methodCall->args[2] ?? null) instanceof Node\Arg + && $methodCall->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 ($methodCall->args[2]->value->items as $arrayItemIndex => $item) { + /* @phpstan-ignore-next-line */ + if ($item->key->value === 'data' or $item->key->value === 'empty_data') { + $k = $methodCall->args[0]->value->value; + $emptyDataToReplace[$k] = $item->value; + $emptyDataIndex = $arrayItemIndex; + } + } + + if (null !== $emptyDataIndex) { + $methodCall->args[2]->value->items = array_values( + array_filter( + $methodCall->args[2]->value->items, + /* @phpstan-ignore-next-line */ + fn (Node\Expr\ArrayItem $item) => $item->key->value !== 'data' + ) + ); + } + + return ['stmt' => $methodCall, 'emptyDataToReplace' => $emptyDataToReplace]; + } + + throw new \RuntimeException("Not supported situation"); + } } diff --git a/utils/rector/tests/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector/Fixture/filter-multiple-reuse-data-on-form-default-data-with-chained-builder.php.inc b/utils/rector/tests/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector/Fixture/filter-multiple-reuse-data-on-form-default-data-with-chained-builder.php.inc new file mode 100644 index 000000000..ed46f6381 --- /dev/null +++ b/utils/rector/tests/Rector/ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector/Fixture/filter-multiple-reuse-data-on-form-default-data-with-chained-builder.php.inc @@ -0,0 +1,111 @@ +add('foo', PickRollingDateType::class, [ + 'label' => 'Test thing', + 'data' => new RollingDate(RollingDate::T_TODAY) + ]) + ->anotherCall('test') + ->add('baz', TextType::class, [ + 'label' => 'OrNiCar', + 'data' => 'Castor' + ]) + ->baz('foo'); + } + + public function getTitle() + { + // TODO: Implement getTitle() method. + } + + public function addRole(): ?string + { + // TODO: Implement addRole() method. + } + + public function alterQuery(QueryBuilder $qb, $data) + { + // TODO: Implement alterQuery() method. + } + + public function applyOn() + { + // TODO: Implement applyOn() method. + } +} +?> +----- +add('foo', PickRollingDateType::class, [ + 'label' => 'Test thing' + ]) + ->anotherCall('test') + ->add('baz', TextType::class, [ + 'label' => 'OrNiCar' + ]) + ->baz('foo'); + } + public function getFormDefaultData(): array + { + return ['foo' => new RollingDate(RollingDate::T_TODAY), 'baz' => 'Castor']; + } + + public function getTitle() + { + // TODO: Implement getTitle() method. + } + + public function addRole(): ?string + { + // TODO: Implement addRole() method. + } + + public function alterQuery(QueryBuilder $qb, $data) + { + // TODO: Implement alterQuery() method. + } + + public function applyOn() + { + // TODO: Implement applyOn() method. + } +} +?>