diff --git a/src/NodeManipulator/ClassDependencyManipulator.php b/src/NodeManipulator/ClassDependencyManipulator.php index 0615ee2a81..218a3b74a1 100644 --- a/src/NodeManipulator/ClassDependencyManipulator.php +++ b/src/NodeManipulator/ClassDependencyManipulator.php @@ -25,6 +25,9 @@ use Rector\ValueObject\MethodName; use Rector\ValueObject\PhpVersionFeature; +/** + * @see \Rector\Tests\NodeManipulator\ClassDependencyManipulatorTest + */ final readonly class ClassDependencyManipulator { public function __construct( @@ -143,6 +146,7 @@ private function addPromotedProperty(Class_ $class, PropertyMetadata $propertyMe } else { $constructClassMethod = $this->nodeFactory->createPublicMethod(MethodName::CONSTRUCT); $constructClassMethod->params[] = $param; + $this->classInsertManipulator->addAsFirstMethod($class, $constructClassMethod); } } diff --git a/src/NodeManipulator/ClassInsertManipulator.php b/src/NodeManipulator/ClassInsertManipulator.php index c56b859fef..710fdfc68d 100644 --- a/src/NodeManipulator/ClassInsertManipulator.php +++ b/src/NodeManipulator/ClassInsertManipulator.php @@ -4,7 +4,6 @@ namespace Rector\NodeManipulator; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassMethod; @@ -23,20 +22,53 @@ public function __construct( /** * @api */ - public function addAsFirstMethod(Class_ $class, Property | ClassConst | ClassMethod $stmt): void + public function addAsFirstMethod(Class_ $class, Property | ClassConst | ClassMethod $addedStmt): void { $scope = $class->getAttribute(AttributeKey::SCOPE); - $stmt->setAttribute(AttributeKey::SCOPE, $scope); + $addedStmt->setAttribute(AttributeKey::SCOPE, $scope); - if ($this->isSuccessToInsertBeforeFirstMethod($class, $stmt)) { + // no stmts? add this one + if ($class->stmts === []) { + $class->stmts[] = $addedStmt; return; } - if ($this->isSuccessToInsertAfterLastProperty($class, $stmt)) { - return; + $newClassStmts = []; + $isAdded = false; + + foreach ($class->stmts as $key => $classStmt) { + $nextStmt = $class->stmts[$key + 1] ?? null; + + if ($isAdded === false) { + // first class method + if ($classStmt instanceof ClassMethod) { + $newClassStmts[] = $addedStmt; + $newClassStmts[] = $classStmt; + $isAdded = true; + continue; + } + + // after last property + if ($classStmt instanceof Property && ! $nextStmt instanceof Property) { + $newClassStmts[] = $classStmt; + $newClassStmts[] = $addedStmt; + $isAdded = true; + continue; + } + } + + $newClassStmts[] = $classStmt; } - $class->stmts[] = $stmt; + // still not added? try after last trait + // @todo + + if ($isAdded === false) { + // keep added at least as first stmt + $class->stmts = array_merge([$addedStmt], $class->stmts); + } else { + $class->stmts = $newClassStmts; + } } /** @@ -52,46 +84,4 @@ public function addPropertyToClass(Class_ $class, string $name, ?Type $type): vo $property = $this->nodeFactory->createPrivatePropertyFromNameAndType($name, $type); $this->addAsFirstMethod($class, $property); } - - /** - * @param Stmt[] $stmts - * @return Stmt[] - */ - private function insertBefore(array $stmts, Stmt $stmt, int $key): array - { - array_splice($stmts, $key, 0, [$stmt]); - - return $stmts; - } - - private function isSuccessToInsertBeforeFirstMethod(Class_ $class, ClassConst|ClassMethod|Property $stmt): bool - { - foreach ($class->stmts as $key => $classStmt) { - if (! $classStmt instanceof ClassMethod) { - continue; - } - - $class->stmts = $this->insertBefore($class->stmts, $stmt, $key); - - return true; - } - - return false; - } - - private function isSuccessToInsertAfterLastProperty(Class_ $class, ClassConst|ClassMethod|Property $stmt): bool - { - $previousElement = null; - foreach ($class->stmts as $key => $classStmt) { - if ($previousElement instanceof Property && ! $classStmt instanceof Property) { - $class->stmts = $this->insertBefore($class->stmts, $stmt, $key); - - return true; - } - - $previousElement = $classStmt; - } - - return false; - } } diff --git a/tests/NodeManipulator/ClassDependencyManipulatorTest.php b/tests/NodeManipulator/ClassDependencyManipulatorTest.php new file mode 100644 index 0000000000..66ba93c281 --- /dev/null +++ b/tests/NodeManipulator/ClassDependencyManipulatorTest.php @@ -0,0 +1,74 @@ +classDependencyManipulator = $this->make(ClassDependencyManipulator::class); + $this->printerStandard = new Standard(); + } + + public function testSingleMethod(): void + { + $someClass = new Class_(new Name('SingleMethodClass')); + $someClass->stmts[] = new ClassMethod('firstMethod'); + + $this->addSingleDependency($someClass); + + $printedClass = $this->printerStandard->prettyPrintFile([$someClass]) . PHP_EOL; + + $this->assertStringEqualsFile(__DIR__ . '/Fixture/expected_single_method.php.inc', $printedClass); + } + + public function testWithProperty(): void + { + $someClass = new Class_(new Name('ClassWithSingleProperty')); + $someClass->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('someProperty')]); + + $this->addSingleDependency($someClass); + + $printedClass = $this->printerStandard->prettyPrintFile([$someClass]) . PHP_EOL; + + $this->assertStringEqualsFile(__DIR__ . '/Fixture/expected_single_property.php.inc', $printedClass); + } + + public function testWithMethodAndProperty(): void + { + $someClass = new Class_(new Name('ClassWithMethodAndProperty')); + $someClass->stmts[] = new Property(Class_::MODIFIER_PRIVATE, [new PropertyProperty('someProperty')]); + $someClass->stmts[] = new ClassMethod(new Identifier('someMethod')); + + $this->addSingleDependency($someClass); + + $printedClass = $this->printerStandard->prettyPrintFile([$someClass]) . PHP_EOL; + $this->assertStringEqualsFile(__DIR__ . '/Fixture/expected_method_and_property.php.inc', $printedClass); + } + + private function addSingleDependency(Class_ $someClass): void + { + $this->classDependencyManipulator->addConstructorDependency($someClass, new PropertyMetadata( + 'eventDispatcher', + new ObjectType('EventDispatcherInterface') + )); + } +} diff --git a/tests/NodeManipulator/Fixture/expected_method_and_property.php.inc b/tests/NodeManipulator/Fixture/expected_method_and_property.php.inc new file mode 100644 index 0000000000..3893fd1d8b --- /dev/null +++ b/tests/NodeManipulator/Fixture/expected_method_and_property.php.inc @@ -0,0 +1,12 @@ +