diff --git a/.gitignore b/.gitignore index 5dae3d3..4d804f2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.idea .phpunit.result.cache .phpunit.cache +composer.lock diff --git a/README.md b/README.md index b9fa114..96ba2ac 100644 --- a/README.md +++ b/README.md @@ -115,8 +115,9 @@ new AppBootstrapBuilder(bootstrap_path('app.php')) MyMiddleware::class, 'throttle:60,1', ]) - ->addMiddlewarePrependToGroup('web', WebMiddleware::class,, InsertPositionEnum::Start) + ->addMiddlewarePrependToGroup('web', WebMiddleware::class, InsertPositionEnum::Start) ->save(); +``` **Note:** Provide the full class name (FQCN) for class-based middleware — the method imports it automatically. diff --git a/src/Enums/ExpressionAttributeEnum.php b/src/Enums/ExpressionAttributeEnum.php new file mode 100644 index 0000000..32ca234 --- /dev/null +++ b/src/Enums/ExpressionAttributeEnum.php @@ -0,0 +1,8 @@ +hasParentOfType($node, PropertyItem::class)) { + $isMultiline = $node->getAttribute(ExpressionAttributeEnum::IsArrayMultiline->value, false); + + if ($isMultiline) { return '[' . $this->pCommaSeparatedMultiline($node->items, true) . $this->nl . ']'; } return parent::pExpr_Array($node); } - protected function hasParentOfType(Node $node, string $type): bool - { - $parent = $node->getAttribute(StatementAttributeEnum::Parent->value); - - while ($parent !== null) { - if ($parent instanceof $type) { - return true; - } - - $parent = $parent->getAttribute(StatementAttributeEnum::Parent->value); - } - - return false; - } - protected function pStmt_Property(Property $node): string { $newLine = ($this->shouldAddNewlineBeforeIfTypeDiffers($node, Property::class)) ? $this->nl : ''; diff --git a/src/Support/NodeValueFactory.php b/src/Support/NodeValueFactory.php index 405ebff..dd90c87 100644 --- a/src/Support/NodeValueFactory.php +++ b/src/Support/NodeValueFactory.php @@ -14,37 +14,37 @@ class NodeValueFactory { - public static function make(mixed $value): NodeValueDTO + public static function make(mixed $value, array $attributes = []): NodeValueDTO { $type = get_debug_type($value); $node = match ($type) { - 'int' => new Int_($value), - 'array' => static::makeArrayValue($value), - 'string' => new String_($value), - 'float' => new Float_($value), - 'bool' => static::makeBoolValue($value), - 'null' => new ConstFetch(new Name('null')), + 'int' => new Int_($value, $attributes), + 'array' => static::makeArrayValue($value, $attributes), + 'string' => new String_($value, $attributes), + 'float' => new Float_($value, $attributes), + 'bool' => static::makeBoolValue($value, $attributes), + 'null' => new ConstFetch(new Name('null'), $attributes), }; return new NodeValueDTO($node, new Identifier($type)); } - protected static function makeBoolValue(bool $value): ConstFetch + protected static function makeBoolValue(bool $value, array $attributes): ConstFetch { $name = new Name($value ? 'true' : 'false'); - return new ConstFetch($name); + return new ConstFetch($name, $attributes); } - protected static function makeArrayValue(array $values): Array_ + protected static function makeArrayValue(array $values, array $attributes): Array_ { $items = []; foreach ($values as $key => $val) { - $items[] = new ArrayItem(static::make($val)->node, static::make($key)->node); + $items[] = new ArrayItem(static::make($val, $attributes)->node, static::make($key)->node); } - return new Array_($items); + return new Array_($items, $attributes); } } diff --git a/src/Visitors/AppBootstrapVisitors/AddMiddlewarePrependToGroup.php b/src/Visitors/AppBootstrapVisitors/AddMiddlewarePrependToGroup.php index ea3ca9f..7bfc66a 100644 --- a/src/Visitors/AppBootstrapVisitors/AddMiddlewarePrependToGroup.php +++ b/src/Visitors/AppBootstrapVisitors/AddMiddlewarePrependToGroup.php @@ -2,6 +2,7 @@ namespace RonasIT\Larabuilder\Visitors\AppBootstrapVisitors; +use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\Array_; @@ -11,13 +12,18 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Nop; +use PhpParser\Node\UseItem; +use RonasIT\Larabuilder\Enums\ExpressionAttributeEnum; use RonasIT\Larabuilder\Enums\InsertPositionEnum; class AddMiddlewarePrependToGroup extends AbstractAppBootstrapVisitor { + protected array $importedNamespaces = []; + public function __construct( protected string $group, protected array $middlewares, @@ -29,6 +35,18 @@ public function __construct( ); } + public function leaveNode(Node $node): Node + { + if ($node instanceof UseItem) { + $this->importedNamespaces[] = [ + 'namespace' => $node->name->toString(), + 'alias' => $node->alias?->toString() ?? null, + ]; + } + + return parent::leaveNode($node); + } + protected function insertNode(MethodCall $node): MethodCall { /** @var Closure $closure */ @@ -56,8 +74,9 @@ protected function removeNopPlaceholder(Closure $closure): void protected function findMiddlewareGroupIndex(array $stmts): ?int { - return array_find_key($stmts, function (Expression $stmt) { - return !empty($stmt->expr->name) + return array_find_key($stmts, function (Node $stmt) { + return $stmt instanceof Expression + && !empty($stmt->expr->name) && $stmt->expr->name->toString() === $this->targetMethod && $stmt->expr->args[0]->value->value === $this->group; }); @@ -65,50 +84,57 @@ protected function findMiddlewareGroupIndex(array $stmts): ?int protected function updateMiddlewareGroup(Closure $closure, int $groupIndex): void { - $originalMiddlewares = $closure->stmts[$groupIndex]->expr->args[1]->value->value - ?? $closure->stmts[$groupIndex]->expr->args[1]->value->class->name - ?? $closure->stmts[$groupIndex]->expr->args[1]->value->items; + $middlewares = $closure->stmts[$groupIndex]->expr->args[1]; - $originalMiddlewares = is_array($originalMiddlewares) - ? $originalMiddlewares - : [new ArrayItem($closure->stmts[$groupIndex]->expr->args[1]->value)]; + $originMiddlewares = ($middlewares->value instanceof Array_) + ? $middlewares->value->items + : [new ArrayItem($middlewares->value)]; - $mergedMiddlewares = $this->mergeMiddlewares($originalMiddlewares); + $mergedMiddlewares = $this->mergeMiddlewares($originMiddlewares); $closure->stmts[$groupIndex]->expr->args[1] = $this->buildMiddlewareArg($mergedMiddlewares); } - protected function mergeMiddlewares(array $originalMiddlewareList): array + protected function mergeMiddlewares(array $originMiddlewares): array { + $originalResolved = array_map(fn ($middleware) => $this->resolveMiddlewareName($middleware), $originMiddlewares); + $filteredNewList = []; foreach ($this->middlewares as $middleware) { - $sameMiddlewareKey = array_find_key( - $originalMiddlewareList, - fn ($originalMiddleware) => $this->isSameMiddleware($middleware, $originalMiddleware), - ); - - if (is_null($sameMiddlewareKey)) { + if (!in_array($middleware, $originalResolved, true)) { $filteredNewList[] = $this->makeArrayItem($middleware); } } return match ($this->position) { - InsertPositionEnum::Start => [...$filteredNewList, ...$originalMiddlewareList], - InsertPositionEnum::End => [...$originalMiddlewareList, ...$filteredNewList], + InsertPositionEnum::Start => [...$filteredNewList, ...$originMiddlewares], + InsertPositionEnum::End => [...$originMiddlewares, ...$filteredNewList], }; } - protected function isSameMiddleware(string $newMiddleware, ArrayItem $originalMiddleware): bool + protected function resolveMiddlewareName($middleware): ?string { - if ($originalMiddleware->value instanceof ClassConstFetch) { - $originalName = $originalMiddleware->value->class->name; + if ($middleware->value instanceof String_) { + return $middleware->value->value; + } + + $isClass = $middleware->value instanceof ClassConstFetch; - return $originalName === $newMiddleware - || $originalName === class_basename($newMiddleware); + if ($isClass && $middleware->value->class instanceof FullyQualified) { + return $middleware->value->class->name; } - return $originalMiddleware->value->value === $newMiddleware; + if ($isClass) { + $found = array_find($this->importedNamespaces, function (array $namespace) use ($middleware) { + return $middleware->value->class->name === class_basename($namespace['namespace']) + || $middleware->value->class->name === $namespace['alias']; + }); + + return $found['namespace'] ?? null; + } + + return null; } protected function buildPrependToGroupCall(): Expression @@ -123,14 +149,16 @@ protected function buildPrependToGroupCall(): Expression return new Expression($methodCall); } - protected function buildMiddlewareArg(array $middlewares): Arg + protected function getMiddlewareList(): array { - return new Arg(new Array_($middlewares)); + return array_map(fn ($middleware) => $this->makeArrayItem($middleware), $this->middlewares); } - protected function getMiddlewareList(): array + protected function buildMiddlewareArg(array $middlewares): Arg { - return array_map(fn ($middleware) => $this->makeArrayItem($middleware), $this->middlewares); + return new Arg(new Array_($middlewares, [ + ExpressionAttributeEnum::IsArrayMultiline->value => true, + ])); } protected function makeArrayItem(string $middleware): ArrayItem diff --git a/src/Visitors/PropertyVisitors/AddArrayPropertyItem.php b/src/Visitors/PropertyVisitors/AddArrayPropertyItem.php index aab7085..fc1d0bf 100644 --- a/src/Visitors/PropertyVisitors/AddArrayPropertyItem.php +++ b/src/Visitors/PropertyVisitors/AddArrayPropertyItem.php @@ -8,6 +8,7 @@ use PhpParser\Node\Identifier; use PhpParser\Node\PropertyItem; use PhpParser\Node\Stmt\Property; +use RonasIT\Larabuilder\Enums\ExpressionAttributeEnum; use RonasIT\Larabuilder\Exceptions\UnexpectedPropertyTypeException; class AddArrayPropertyItem extends SetProperty @@ -21,7 +22,9 @@ public function __construct( parent::__construct($name, $value); $this->arrayItem = new ArrayItem($this->property->node); - $arrayNode = new Array_([$this->arrayItem]); + $arrayNode = new Array_([$this->arrayItem], [ + ExpressionAttributeEnum::IsArrayMultiline->value => true, + ]); $this->propertyItem = new PropertyItem($this->name, $arrayNode); $this->typeIdentifier = new Identifier('array'); diff --git a/src/Visitors/PropertyVisitors/SetProperty.php b/src/Visitors/PropertyVisitors/SetProperty.php index d8d1343..040be68 100644 --- a/src/Visitors/PropertyVisitors/SetProperty.php +++ b/src/Visitors/PropertyVisitors/SetProperty.php @@ -9,6 +9,7 @@ use RonasIT\Larabuilder\Contracts\InsertNodeContract; use RonasIT\Larabuilder\DTO\NodeValueDTO; use RonasIT\Larabuilder\Enums\AccessModifierEnum; +use RonasIT\Larabuilder\Enums\ExpressionAttributeEnum; use RonasIT\Larabuilder\Support\NodeValueFactory; class SetProperty extends AbstractPropertyVisitor implements InsertNodeContract @@ -24,7 +25,11 @@ public function __construct( ) { parent::__construct($name); - $this->property = NodeValueFactory::make($value); + $attributes = is_array($value) + ? [ExpressionAttributeEnum::IsArrayMultiline->value => true] + : []; + + $this->property = NodeValueFactory::make($value, $attributes); $this->propertyItem = new PropertyItem($this->name, $this->property->node); $this->typeIdentifier = $this->property->typeNode; diff --git a/tests/AppBootstrapBuilderTest.php b/tests/AppBootstrapBuilderTest.php index 8c02c15..59ccb74 100644 --- a/tests/AppBootstrapBuilderTest.php +++ b/tests/AppBootstrapBuilderTest.php @@ -183,6 +183,7 @@ public function testAddMiddlewarePrependToGroupExistsMiddlewares() ->addMiddlewarePrependToGroup('api', [ 'throttle:60,10', Authenticate::class, + \Illuminate\Routing\Controllers\Middleware::class, ]) ->save(); } diff --git a/tests/Support/OriginStructures/bootstrap_with_prepend_group.php b/tests/Support/OriginStructures/bootstrap_with_prepend_group.php index 0052a0e..09bad76 100644 --- a/tests/Support/OriginStructures/bootstrap_with_prepend_group.php +++ b/tests/Support/OriginStructures/bootstrap_with_prepend_group.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Auth\Middleware\Authenticate; +use Illuminate\Routing\Controllers\Middleware as SomeMiddleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( @@ -12,7 +13,15 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->prependToGroup('api', ['throttle:60,10', Authenticate::class]); + if (app()->environment('production')) { + $middleware->append('auth'); + } + + $middleware->prependToGroup('api', [ + 'throttle:60,10', + Authenticate::class, + SomeMiddleware::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group.php b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group.php index 9dd5260..7f2c60d 100644 --- a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group.php +++ b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group.php @@ -12,8 +12,14 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->prependToGroup('api', ['throttle:60,10', FakeClass::class]); - $middleware->prependToGroup('web', ['throttle:10,10', FakeClass::class]); + $middleware->prependToGroup('api', [ + 'throttle:60,10', + FakeClass::class, + ]); + $middleware->prependToGroup('web', [ + 'throttle:10,10', + FakeClass::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string.php b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string.php index bd7306b..820332e 100644 --- a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string.php +++ b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string.php @@ -11,7 +11,10 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->prependToGroup('api', ['throttle:60,10', 'some_middleware']); + $middleware->prependToGroup('api', [ + 'throttle:60,10', + 'some_middleware', + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string_set_class.php b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string_set_class.php index bed591c..39e6ac4 100644 --- a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string_set_class.php +++ b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_with_prepend_group_as_string_set_class.php @@ -11,7 +11,10 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->prependToGroup('api', [\Illuminate\Auth\Middleware\Authenticate::class, 'some_middleware']); + $middleware->prependToGroup('api', [ + \Illuminate\Auth\Middleware\Authenticate::class, + 'some_middleware', + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_without_changed_prepend_group.php b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_without_changed_prepend_group.php index 0052a0e..09bad76 100644 --- a/tests/fixtures/AppBootstrapBuilderTest/bootstrap_without_changed_prepend_group.php +++ b/tests/fixtures/AppBootstrapBuilderTest/bootstrap_without_changed_prepend_group.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Auth\Middleware\Authenticate; +use Illuminate\Routing\Controllers\Middleware as SomeMiddleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( @@ -12,7 +13,15 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->prependToGroup('api', ['throttle:60,10', Authenticate::class]); + if (app()->environment('production')) { + $middleware->append('auth'); + } + + $middleware->prependToGroup('api', [ + 'throttle:60,10', + Authenticate::class, + SomeMiddleware::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { //