Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ traits, and enums.
Both `$value` and `$key` are passed as strings. A bare identifier (`logo`, `datetime`) is inserted as a string literal;
anything else — `null`, `true`, `false`, numbers, `RoleEnum::class`, `self::Second`, `['admin', 'editor']` — is inserted as raw PHP code.

When an item is appended, the formatting of the array follows two rules: an array that was already multiline stays
multiline, and an array that grows past two items becomes multiline. Otherwise the original style is kept.

```php
new PHPFileBuilder(app_path('Models/User.php'))
->addReturnedArrayItem('casts', 'RoleEnum::class', 'role') // 'role' => RoleEnum::class
Expand Down
2 changes: 2 additions & 0 deletions src/Enums/StatementAttributeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ enum StatementAttributeEnum: string
case Parent = 'parent';
case Previous = 'previous';
case Comments = 'comments';
case Modified = 'modified';
case Multiline = 'multiline';
}
51 changes: 49 additions & 2 deletions src/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace RonasIT\Larabuilder;

use Illuminate\Support\Arr;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\PropertyItem;
Expand Down Expand Up @@ -40,13 +41,59 @@ protected function removeDuplicateEmptyLines(string $code): string
return preg_replace("/(\r?\n){3,}/", "\n\n", $code);
}

protected function pArray(
array $nodes,
array $origNodes,
int &$pos,
int $indentAdjustment,
string $parentNodeClass,
string $subNodeName,
?int $fixup,
): ?string {
$modifiedArray = ($parentNodeClass === Array_::class && $subNodeName === 'items')
? $this->findModifiedArray($nodes)
: null;

if (!is_null($modifiedArray)) {
$isMultiline = $this->wasMultiline($origNodes, $pos) || count($nodes) > 2;

$modifiedArray->setAttribute(StatementAttributeEnum::Multiline->value, $isMultiline);

return null;
}

return parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeClass, $subNodeName, $fixup);
}

protected function pExpr_Array(Array_ $node): string
{
if ($this->hasParentOfType($node, PropertyItem::class)) {
$isMultiline = $node->getAttribute(StatementAttributeEnum::Multiline->value);

if ($this->hasParentOfType($node, PropertyItem::class) || $isMultiline === true) {
return '[' . $this->pCommaSeparatedMultiline($node->items, true) . $this->nl . ']';
}

return parent::pExpr_Array($node);
return ($isMultiline === false)
? '[' . $this->pCommaSeparated($node->items) . ']'
Comment on lines +72 to +77

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve long array syntax when reprinting

When an item is appended to a returned array written with array(...), pArray() now forces the entire expression through this override, which always emits square brackets. Consequently, a formatting-only update unexpectedly converts long array syntax to short syntax; select delimiters based on the array's kind attribute or delegate that part to the parent printer.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short array syntax is the standard in Laravel projects, so the linter rewrites array(...) to [...] on any file it touches anyway. Preserving long syntax here would only postpone an edit the linter makes regardless, so not supporting it is a deliberate choice.

: parent::pExpr_Array($node);
}

protected function findModifiedArray(array $items): ?Array_
{
$modifiedItem = Arr::first(
$items,
fn (?Node $item) => $item?->getAttribute(StatementAttributeEnum::Modified->value) === true,
);

return $modifiedItem?->getAttribute(StatementAttributeEnum::Parent->value);
}

protected function wasMultiline(array $origNodes, int $pos): bool
{
$firstItem = $origNodes[0] ?? null;
$endPos = $firstItem?->getStartTokenPos() ?? $this->origTokens->findRight($pos, ']');
Comment thread
artengin marked this conversation as resolved.

return str_contains($this->origTokens->getTokenCode($pos, $endPos, 0), "\n");
Comment on lines +93 to +96

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect the whole original array for multiline formatting

When a one-item array places its first item on the opening line but its closing bracket on a later line, such as return ['first',\n];, this stops scanning at the first item's start and misses the newline after it. Appending a second item does not satisfy count($nodes) > 2, so the array is marked non-multiline and collapsed onto one line despite the documented rule that an already multiline array stays multiline.

Useful? React with 👍 / 👎.

@vitgrams vitgrams Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artengin I noticed this case as well. I don't think it's critical, though. Looks good for me

    protected function getSortableFields(): array
    {
        return ['id',
        ];
    }

VS

    protected function getSortableFields(): array
    {
        return ['id', 'name'];
    }

}

protected function hasParentOfType(Node $node, string $type): bool
Expand Down
12 changes: 10 additions & 2 deletions src/Visitors/MethodVisitors/AddReturnedArrayItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Return_;
use RonasIT\Larabuilder\Enums\StatementAttributeEnum;
use RonasIT\Larabuilder\Exceptions\MultipleReturnStatementsException;
use RonasIT\Larabuilder\Exceptions\UnexpectedReturnTypeException;
use RonasIT\Larabuilder\Nodes\PreformattedExpression;
Expand Down Expand Up @@ -43,7 +44,7 @@ public function updateNode(Node $node): void
}

if (is_null($this->key)) {
$returnNode->expr->items[] = new ArrayItem($this->value);
$this->appendItem($returnNode->expr, new ArrayItem($this->value));

return;
}
Expand All @@ -56,7 +57,14 @@ public function updateNode(Node $node): void
}
}

$returnNode->expr->items[] = new ArrayItem($this->value, $this->key);
$this->appendItem($returnNode->expr, new ArrayItem($this->value, $this->key));
}

protected function appendItem(Array_ $array, ArrayItem $item): void
{
$item->setAttribute(StatementAttributeEnum::Modified->value, true);

$array->items[] = $item;
}

protected function isSameKey(?Expr $itemKey): bool
Expand Down
3 changes: 3 additions & 0 deletions tests/PHPFileBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,8 @@ public function testAddReturnedArrayItemInTrait(): void

new PHPFileBuilder($file)
->addReturnedArrayItem('getUserData', "['admin', 'editor']", 'roles')
->addReturnedArrayItem('getDefaultFilters', 'is_published')
->addReturnedArrayItem('getInlineOptions', '3', 'third')
->save();
}

Expand All @@ -788,6 +790,7 @@ public function testAddReturnedArrayItemInEnum(): void

new PHPFileBuilder($file)
->addReturnedArrayItem('updatableStatuses', 'self::Second')
->addReturnedArrayItem('defaultStatuses', 'self::First')
->save();
}

Expand Down
5 changes: 5 additions & 0 deletions tests/Support/OriginStructures/enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
12 changes: 12 additions & 0 deletions tests/Support/OriginStructures/trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/add_imports_to_enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/add_imports_to_trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/add_traits_to_enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/add_traits_to_trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/enum_method_removed.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ enum SomeEnum
case First = 'first';
case Second = 'second';

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/enum_with_added_method.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [];
}

public static function updatableStatuses(): array
{
return [self::First];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static function toArray(): array
return self::cases();
}

public static function defaultStatuses(): array
{
return [self::First];
}

public static function updatableStatuses(): array
{
return [self::First, self::Second];
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artengin It might be worth adding some of the test cases below to explicitly confirm/document the existing behavior. Please take a look.

I added these locally, and everything worked as expected for me.

  1. Comments associated with array elements are preserved

Before:

protected function getValidationRules(): array
{
    return [
        // profile
        'name' => 'required|string',
        'email' => 'required|email',

        // address
        'city' => 'nullable|string'
    ];
}

After:

protected function getValidationRules(): array
{
    return [
        // profile
        'name' => 'required|string',
        'email' => 'required|email',
        // address
        'city' => 'nullable|string',
        'zip' => 'nullable|string',
    ];
}
  1. Nested arrays preserve their original formatting

Before:

protected function getUserData(): array
{
    return [
        'name' => 'John',
        'settings' => [
            'locale' => 'en',
            'timezone' => 'UTC',
        ],
    ];
}

After:

protected function getUserData(): array
{
    return [
        'name' => 'John',
        'settings' => [
            'locale' => 'en',
            'timezone' => 'UTC',
        ],
        'roles' => ['admin', 'editor'],
    ];
}
  1. Empty multiline array → append

This is the only case I found where the wasMultiline fallback branch is triggered (findRight(']')), preserving the original multiline array syntax even when adding the first element.

Before:

protected function getExtraFilters(): array
{
    return [
    ];
}

After:

protected function getExtraFilters(): array
{
    return [
        'is_hidden',
    ];
}

{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/trait_method_removed.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/trait_with_added_method.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/PHPFileBuilderTest/trait_with_method_code_added.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return ['first' => 1, 'second' => 2];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
];
}

protected function getUserData(): array
{
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ public function method3()
]);
}

protected function getInlineOptions(): array
{
return [
'first' => 1,
'second' => 2,
'third' => 3,
];
}

protected function getDefaultFilters(): array
{
return [
'is_active',
'is_published',
];
}

protected function getUserData(): array
{
return [
Expand Down