From 94ee5d5fdb5018603e7c4b91f45488f58b860658 Mon Sep 17 00:00:00 2001 From: Artyom Osepyan Date: Thu, 30 Apr 2026 22:44:51 +0300 Subject: [PATCH 1/3] feat: Implement setNamespace method --- src/Builders/PHPFileBuilder.php | 8 ++++ src/Visitors/SetNamespace.php | 32 ++++++++++++++ tests/PHPFileBuilderTest.php | 42 +++++++++++++++++++ .../Support/OriginStructures/class_empty.php | 5 +++ .../PHPFileBuilderTest/namespace_set.php | 7 ++++ .../PHPFileBuilderTest/namespace_update.php | 14 +++++++ 6 files changed, 108 insertions(+) create mode 100644 src/Visitors/SetNamespace.php create mode 100644 tests/Support/OriginStructures/class_empty.php create mode 100644 tests/fixtures/PHPFileBuilderTest/namespace_set.php create mode 100644 tests/fixtures/PHPFileBuilderTest/namespace_update.php diff --git a/src/Builders/PHPFileBuilder.php b/src/Builders/PHPFileBuilder.php index 0f9128e..5644de7 100644 --- a/src/Builders/PHPFileBuilder.php +++ b/src/Builders/PHPFileBuilder.php @@ -16,6 +16,7 @@ use RonasIT\Larabuilder\Visitors\PropertyVisitors\AddArrayPropertyItem; use RonasIT\Larabuilder\Visitors\PropertyVisitors\RemoveArrayPropertyItem; use RonasIT\Larabuilder\Visitors\PropertyVisitors\SetProperty; +use RonasIT\Larabuilder\Visitors\SetNamespace; class PHPFileBuilder { @@ -41,6 +42,13 @@ public function __construct( $this->traverser = new NodeTraverser(); } + public function setNamespace(string $namespace): self + { + $this->traverser->addVisitor(new SetNamespace($namespace)); + + return $this; + } + public function setProperty(string $name, mixed $value, AccessModifierEnum $accessModifier = AccessModifierEnum::Public): self { $this->traverser->addVisitor(new SetProperty($name, $value, $accessModifier)); diff --git a/src/Visitors/SetNamespace.php b/src/Visitors/SetNamespace.php new file mode 100644 index 0000000..b6f4e7f --- /dev/null +++ b/src/Visitors/SetNamespace.php @@ -0,0 +1,32 @@ +name->toString() === $this->namespace) { + return null; + } + + $node->name = new Name($this->namespace); + + return null; + } + } + + return [new Namespace_(new Name($this->namespace), $nodes)]; + } +} diff --git a/tests/PHPFileBuilderTest.php b/tests/PHPFileBuilderTest.php index 5fb12f4..b814f2c 100644 --- a/tests/PHPFileBuilderTest.php +++ b/tests/PHPFileBuilderTest.php @@ -17,6 +17,48 @@ class PHPFileBuilderTest extends TestCase { use PHPFileBuilderTestMockTrait; + public function testSetNamespaceOnFileWithoutNamespace(): void + { + $file = $this->generateOriginalStructurePath('class_empty.php'); + + $this->mockNativeFunction( + 'RonasIT\Larabuilder\Builders', + $this->callFilePutContent($file, 'namespace_set.php'), + ); + + new PHPFileBuilder($file) + ->setNamespace('App\\Models') + ->save(); + } + + public function testSetNamespaceReplacesExistingNamespace(): void + { + $file = $this->generateOriginalStructurePath('enum.php'); + + $this->mockNativeFunction( + 'RonasIT\Larabuilder\Builders', + $this->callFilePutContent($file, 'namespace_update.php'), + ); + + new PHPFileBuilder($file) + ->setNamespace('App\\Models') + ->save(); + } + + public function testSetNamespaceDoesNothingWhenSameNamespace(): void + { + $file = $this->generateOriginalStructurePath('class.php'); + + $this->mockNativeFunction( + 'RonasIT\Larabuilder\Builders', + $this->callFilePutContent($file, 'class_unchanged.php'), + ); + + new PHPFileBuilder($file) + ->setNamespace('RonasIT\Larabuilder\Tests\Support') + ->save(); + } + public function testSetProperty(): void { $file = $this->generateOriginalStructurePath('class_with_properties.php'); diff --git a/tests/Support/OriginStructures/class_empty.php b/tests/Support/OriginStructures/class_empty.php new file mode 100644 index 0000000..b36f6aa --- /dev/null +++ b/tests/Support/OriginStructures/class_empty.php @@ -0,0 +1,5 @@ + Date: Thu, 30 Apr 2026 23:00:35 +0300 Subject: [PATCH 2/3] fix: keep declare() outside namespace when adding namespace to file without one --- src/Visitors/SetNamespace.php | 14 +++++++++++++- tests/Support/OriginStructures/class_empty.php | 2 ++ .../fixtures/PHPFileBuilderTest/namespace_set.php | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Visitors/SetNamespace.php b/src/Visitors/SetNamespace.php index b6f4e7f..ff50c4d 100644 --- a/src/Visitors/SetNamespace.php +++ b/src/Visitors/SetNamespace.php @@ -3,6 +3,7 @@ namespace RonasIT\Larabuilder\Visitors; use PhpParser\Node\Name; +use PhpParser\Node\Stmt\Declare_; use PhpParser\Node\Stmt\Namespace_; use PhpParser\NodeVisitorAbstract; @@ -27,6 +28,17 @@ public function afterTraverse(array $nodes): ?array } } - return [new Namespace_(new Name($this->namespace), $nodes)]; + $declares = []; + $stmts = []; + + foreach ($nodes as $node) { + if ($node instanceof Declare_) { + $declares[] = $node; + } else { + $stmts[] = $node; + } + } + + return [...$declares, new Namespace_(new Name($this->namespace), $stmts)]; } } diff --git a/tests/Support/OriginStructures/class_empty.php b/tests/Support/OriginStructures/class_empty.php index b36f6aa..0cf3f47 100644 --- a/tests/Support/OriginStructures/class_empty.php +++ b/tests/Support/OriginStructures/class_empty.php @@ -1,5 +1,7 @@ Date: Thu, 30 Apr 2026 23:17:05 +0300 Subject: [PATCH 3/3] docs: add setNamespace() to README --- README.md | 4 ++++ src/Visitors/SetNamespace.php | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 11ff580..9923012 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ new PHPFileBuilder(app_path('Models/User.php')) ### Features +#### setNamespace + +Set or replace the namespace declaration in a PHP file. If the file has no namespace, it will be added. If the namespace is already the same, no changes are made. + #### setProperty Add new class property with the passed value and passed access level in case property does not exist in the class. Otherwise diff --git a/src/Visitors/SetNamespace.php b/src/Visitors/SetNamespace.php index ff50c4d..b3ddf90 100644 --- a/src/Visitors/SetNamespace.php +++ b/src/Visitors/SetNamespace.php @@ -16,29 +16,29 @@ public function __construct( public function afterTraverse(array $nodes): ?array { - foreach ($nodes as $node) { + $declares = []; + + foreach ($nodes as $key => $node) { if ($node instanceof Namespace_) { - if ($node->name->toString() === $this->namespace) { - return null; - } + return $this->updateNamespace($node); + } - $node->name = new Name($this->namespace); + if ($node instanceof Declare_) { + $declares[] = $node; - return null; + unset($nodes[$key]); } } - $declares = []; - $stmts = []; + return [...$declares, new Namespace_(new Name($this->namespace), array_values($nodes))]; + } - foreach ($nodes as $node) { - if ($node instanceof Declare_) { - $declares[] = $node; - } else { - $stmts[] = $node; - } + protected function updateNamespace(Namespace_ $node): ?array + { + if ($node->name->toString() !== $this->namespace) { + $node->name = new Name($this->namespace); } - return [...$declares, new Namespace_(new Name($this->namespace), $stmts)]; + return null; } }