From e48be7b9476a5a73e42d394797daaef4fc68c78a Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Fri, 24 Oct 2025 11:04:48 +0530 Subject: [PATCH 1/5] refactor to use first-class callables Signed-off-by: Pushpak Chhajed --- rector.php | 3 --- src/BoostServiceProvider.php | 2 +- src/Install/CodeEnvironmentsDetector.php | 2 +- src/Install/Detection/DetectionStrategyFactory.php | 2 +- src/Install/GuidelineComposer.php | 2 +- src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php | 2 +- src/Support/Composer.php | 4 ++-- tests/Feature/Mcp/ToolExecutorTest.php | 8 ++++---- 10 files changed, 13 insertions(+), 16 deletions(-) diff --git a/rector.php b/rector.php index 35c4e0a9..5f495026 100644 --- a/rector.php +++ b/rector.php @@ -5,7 +5,6 @@ use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; use Rector\Config\RectorConfig; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; -use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector; use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector; return RectorConfig::configure() @@ -17,7 +16,6 @@ ReadOnlyPropertyRector::class, EncapsedStringsToSprintfRector::class, DisallowedEmptyRuleFixerRector::class, - BooleanInBooleanNotRuleFixerRector::class, ]) ->withPreparedSets( deadCode: true, @@ -25,5 +23,4 @@ codingStyle: true, typeDeclarations: true, earlyReturn: true, - strictBooleans: true, )->withPhpSets(php81: true); diff --git a/src/BoostServiceProvider.php b/src/BoostServiceProvider.php index 9ade989e..ca20e46b 100644 --- a/src/BoostServiceProvider.php +++ b/src/BoostServiceProvider.php @@ -73,7 +73,7 @@ public function boot(Router $router): void if (config('boost.browser_logs_watcher', true)) { $this->registerBrowserLogger(); - $this->callAfterResolving('blade.compiler', fn (BladeCompiler $bladeCompiler) => $this->registerBladeDirectives($bladeCompiler)); + $this->callAfterResolving('blade.compiler', $this->registerBladeDirectives(...)); $this->hookIntoResponses($router); } } diff --git a/src/Install/CodeEnvironmentsDetector.php b/src/Install/CodeEnvironmentsDetector.php index c88d962c..16ab7f46 100644 --- a/src/Install/CodeEnvironmentsDetector.php +++ b/src/Install/CodeEnvironmentsDetector.php @@ -55,6 +55,6 @@ public function discoverProjectInstalledCodeEnvironments(string $basePath): arra public function getCodeEnvironments(): Collection { return collect($this->boostManager->getCodeEnvironments()) - ->map(fn (string $className) => $this->container->make($className)); + ->map($this->container->make(...)); // @phpstan-ignore argument.type } } diff --git a/src/Install/Detection/DetectionStrategyFactory.php b/src/Install/Detection/DetectionStrategyFactory.php index 9c6b242a..ea981b81 100644 --- a/src/Install/Detection/DetectionStrategyFactory.php +++ b/src/Install/Detection/DetectionStrategyFactory.php @@ -22,7 +22,7 @@ public function make(string|array $type, array $config = []): DetectionStrategy { if (is_array($type)) { return new CompositeDetectionStrategy( - array_map(fn ($singleType): \Laravel\Boost\Install\Contracts\DetectionStrategy => $this->make($singleType, $config), $type) + array_map(fn (string|array $singleType): \Laravel\Boost\Install\Contracts\DetectionStrategy => $this->make($singleType, $config), $type) ); } diff --git a/src/Install/GuidelineComposer.php b/src/Install/GuidelineComposer.php index 2b654841..0ef66411 100644 --- a/src/Install/GuidelineComposer.php +++ b/src/Install/GuidelineComposer.php @@ -311,7 +311,7 @@ protected function guideline(string $path, bool $thirdParty = false): array protected function processBoostSnippets(string $content): string { - return preg_replace_callback('/(?[\'"])(?P[^\1]*?)\1(?:\s*,\s*(?P[\'"])(?P[^\3]*?)\3)?\s*\)(?P.*?)@endboostsnippet/s', function ($matches): string { + return preg_replace_callback('/(?[\'"])(?P[^\1]*?)\1(?:\s*,\s*(?P[\'"])(?P[^\3]*?)\3)?\s*\)(?P.*?)@endboostsnippet/s', function (array $matches): string { $name = $matches['name']; $lang = empty($matches['lang']) ? 'html' : $matches['lang']; $snippetContent = $matches['content']; diff --git a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php index f0d732b5..f69ec188 100644 --- a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php @@ -43,7 +43,7 @@ public function getFunctions(): array public function getTriggers(?string $table = null): array { try { - if ($table !== null && $table !== '' && $table !== '0') { + if (! in_array($table, [null, '', '0'], true)) { return DB::connection($this->connection)->select('SHOW TRIGGERS WHERE `Table` = ?', [$table]); } diff --git a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php index 75bff37d..00ab08a2 100644 --- a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php @@ -60,7 +60,7 @@ public function getTriggers(?string $table = null): array FROM information_schema.triggers WHERE trigger_schema = current_schema() '; - if ($table !== null && $table !== '' && $table !== '0') { + if (! in_array($table, [null, '', '0'], true)) { $sql .= ' AND event_object_table = ?'; return DB::connection($this->connection)->select($sql, [$table]); diff --git a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php index d1c2752c..13b10015 100644 --- a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php @@ -36,7 +36,7 @@ public function getTriggers(?string $table = null): array { try { $sql = "SELECT name, sql FROM sqlite_master WHERE type = 'trigger'"; - if ($table !== null && $table !== '' && $table !== '0') { + if (! in_array($table, [null, '', '0'], true)) { $sql .= ' AND tbl_name = ?'; return DB::connection($this->connection)->select($sql, [$table]); diff --git a/src/Support/Composer.php b/src/Support/Composer.php index 57dcafd4..d340b04e 100644 --- a/src/Support/Composer.php +++ b/src/Support/Composer.php @@ -13,7 +13,7 @@ public static function packagesDirectories(): array base_path('vendor'), str_replace('/', DIRECTORY_SEPARATOR, $package), ])]) - ->filter(fn (string $path): bool => is_dir($path)) + ->filter(is_dir(...)) ->toArray(); } @@ -45,7 +45,7 @@ public static function packagesDirectoriesWithBoostGuidelines(): array 'resources', 'boost', 'guidelines', - ]))->filter(fn (string $path): bool => is_dir($path)) + ]))->filter(is_dir(...)) ->toArray(); } } diff --git a/tests/Feature/Mcp/ToolExecutorTest.php b/tests/Feature/Mcp/ToolExecutorTest.php index 59db3a7f..b2d41272 100644 --- a/tests/Feature/Mcp/ToolExecutorTest.php +++ b/tests/Feature/Mcp/ToolExecutorTest.php @@ -11,7 +11,7 @@ ->shouldAllowMockingProtectedMethods(); $executor->shouldReceive('buildCommand') ->once() - ->andReturnUsing(fn ($toolClass, $arguments): array => buildSubprocessCommand($toolClass, $arguments)); + ->andReturnUsing(buildSubprocessCommand(...)); $response = $executor->execute(GetConfig::class, ['key' => 'app.name']); @@ -42,7 +42,7 @@ $executor = Mockery::mock(ToolExecutor::class)->makePartial() ->shouldAllowMockingProtectedMethods(); $executor->shouldReceive('buildCommand') - ->andReturnUsing(fn ($toolClass, $arguments): array => buildSubprocessCommand($toolClass, $arguments)); + ->andReturnUsing(buildSubprocessCommand(...)); $response1 = $executor->execute(Tinker::class, ['code' => 'return getmypid();']); $response2 = $executor->execute(Tinker::class, ['code' => 'return getmypid();']); @@ -62,7 +62,7 @@ $executor = Mockery::mock(ToolExecutor::class)->makePartial() ->shouldAllowMockingProtectedMethods(); $executor->shouldReceive('buildCommand') - ->andReturnUsing(fn ($toolClass, $arguments): array => buildSubprocessCommand($toolClass, $arguments)); + ->andReturnUsing(buildSubprocessCommand(...)); // Path to the GetConfig tool that we'll temporarily modify // TODO: Improve for parallelisation @@ -136,7 +136,7 @@ function buildSubprocessCommand(string $toolClass, array $arguments): array ->shouldAllowMockingProtectedMethods(); $executor->shouldReceive('buildCommand') - ->andReturnUsing(fn ($toolClass, $arguments): array => buildSubprocessCommand($toolClass, $arguments)); + ->andReturnUsing(buildSubprocessCommand(...)); // Test with custom timeout - should succeed with fast code $response = $executor->execute(Tinker::class, [ From fe45f8486c3a5fb663c49f3ee1f45ac05d28dc6c Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Fri, 24 Oct 2025 11:41:38 +0530 Subject: [PATCH 2/5] fix test Signed-off-by: Pushpak Chhajed --- rector.php | 4 ++++ src/Install/Cli/DisplayHelper.php | 2 +- src/Install/CodeEnvironmentsDetector.php | 2 +- src/Mcp/Tools/ListAvailableEnvVars.php | 2 +- src/Mcp/Tools/SearchDocs.php | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rector.php b/rector.php index 5f495026..8df4c89b 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; +use Rector\CodingStyle\Rector\FunctionLike\FunctionLikeToFirstClassCallableRector; use Rector\Config\RectorConfig; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector; @@ -16,6 +17,9 @@ ReadOnlyPropertyRector::class, EncapsedStringsToSprintfRector::class, DisallowedEmptyRuleFixerRector::class, + FunctionLikeToFirstClassCallableRector::class => [ + __DIR__.'src/Install/CodeEnvironmentsDetector.php', + ], ]) ->withPreparedSets( deadCode: true, diff --git a/src/Install/Cli/DisplayHelper.php b/src/Install/Cli/DisplayHelper.php index 09a934c5..03ad3c18 100644 --- a/src/Install/Cli/DisplayHelper.php +++ b/src/Install/Cli/DisplayHelper.php @@ -87,7 +87,7 @@ public static function grid(array $items, int $maxWidth = 80): void } $maxWidth -= 2; // account for grid margins - $maxItemLength = max(array_map('mb_strlen', $items)); + $maxItemLength = max(array_map(mb_strlen(...), $items)); $cellWidth = $maxItemLength + self::GRID_CELL_PADDING; $cellsPerRow = max(1, (int) floor(($maxWidth - 1) / ($cellWidth + 1))); $rows = array_chunk($items, $cellsPerRow); diff --git a/src/Install/CodeEnvironmentsDetector.php b/src/Install/CodeEnvironmentsDetector.php index 16ab7f46..c88d962c 100644 --- a/src/Install/CodeEnvironmentsDetector.php +++ b/src/Install/CodeEnvironmentsDetector.php @@ -55,6 +55,6 @@ public function discoverProjectInstalledCodeEnvironments(string $basePath): arra public function getCodeEnvironments(): Collection { return collect($this->boostManager->getCodeEnvironments()) - ->map($this->container->make(...)); // @phpstan-ignore argument.type + ->map(fn (string $className) => $this->container->make($className)); } } diff --git a/src/Mcp/Tools/ListAvailableEnvVars.php b/src/Mcp/Tools/ListAvailableEnvVars.php index 183d59d9..0615eecd 100644 --- a/src/Mcp/Tools/ListAvailableEnvVars.php +++ b/src/Mcp/Tools/ListAvailableEnvVars.php @@ -60,7 +60,7 @@ public function handle(Request $request): Response return Response::error('Failed to parse .env file'); } - $envVars = array_map('trim', $matches[1]); + $envVars = array_map(trim(...), $matches[1]); sort($envVars); diff --git a/src/Mcp/Tools/SearchDocs.php b/src/Mcp/Tools/SearchDocs.php index 510dc8e5..0ca065a9 100644 --- a/src/Mcp/Tools/SearchDocs.php +++ b/src/Mcp/Tools/SearchDocs.php @@ -54,7 +54,7 @@ public function handle(Request $request): Response|Generator $packagesFilter = $request->get('packages'); $queries = array_filter( - array_map('trim', $request->get('queries')), + array_map(trim(...), $request->get('queries')), fn (string $query): bool => $query !== '' && $query !== '*' ); From 02a74fea299df7de0217db33f0b290851502964c Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Fri, 24 Oct 2025 13:05:20 +0530 Subject: [PATCH 3/5] update roster package Signed-off-by: Pushpak Chhajed --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 99e33a48..6cf5a337 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "illuminate/support": "^10.49.0|^11.45.3|^12.28.1", "laravel/mcp": "^0.2.0|^0.3.0", "laravel/prompts": "0.1.25|^0.3.6", - "laravel/roster": "^0.2.8" + "laravel/roster": "^0.2.9" }, "require-dev": { "laravel/pint": "1.20", From 21e9ba561a07222473d339dd1c65745bcd28c9f4 Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Wed, 29 Oct 2025 21:59:12 +0530 Subject: [PATCH 4/5] Centralize table validation logic in `isTableProvided` method Signed-off-by: Pushpak Chhajed --- src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php | 5 +++++ src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php index abd2550c..a0ec1ad4 100644 --- a/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php @@ -8,6 +8,11 @@ abstract class DatabaseSchemaDriver { public function __construct(protected $connection = null) {} + protected function isTableProvided(?string $table): bool + { + return ! in_array($table, [null, '', '0'], true); + } + abstract public function getViews(): array; abstract public function getStoredProcedures(): array; diff --git a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php index f69ec188..0653354a 100644 --- a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php @@ -43,7 +43,7 @@ public function getFunctions(): array public function getTriggers(?string $table = null): array { try { - if (! in_array($table, [null, '', '0'], true)) { + if ($this->isTableProvided($table)) { return DB::connection($this->connection)->select('SHOW TRIGGERS WHERE `Table` = ?', [$table]); } diff --git a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php index 00ab08a2..d9d550a3 100644 --- a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php @@ -60,7 +60,7 @@ public function getTriggers(?string $table = null): array FROM information_schema.triggers WHERE trigger_schema = current_schema() '; - if (! in_array($table, [null, '', '0'], true)) { + if ($this->isTableProvided($table)) { $sql .= ' AND event_object_table = ?'; return DB::connection($this->connection)->select($sql, [$table]); diff --git a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php index 13b10015..bac40335 100644 --- a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php @@ -36,7 +36,7 @@ public function getTriggers(?string $table = null): array { try { $sql = "SELECT name, sql FROM sqlite_master WHERE type = 'trigger'"; - if (! in_array($table, [null, '', '0'], true)) { + if ($this->isTableProvided($table)) { $sql .= ' AND tbl_name = ?'; return DB::connection($this->connection)->select($sql, [$table]); From e25cf4d54c0bc35084c3b2eae693234076232cfa Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Thu, 30 Oct 2025 12:52:27 -0400 Subject: [PATCH 5/5] isTableProvided -> hasTable --- src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php | 2 +- src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php index a0ec1ad4..324ee2e8 100644 --- a/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/DatabaseSchemaDriver.php @@ -8,7 +8,7 @@ abstract class DatabaseSchemaDriver { public function __construct(protected $connection = null) {} - protected function isTableProvided(?string $table): bool + protected function hasTable(?string $table): bool { return ! in_array($table, [null, '', '0'], true); } diff --git a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php index 0653354a..62599238 100644 --- a/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/MySQLSchemaDriver.php @@ -43,7 +43,7 @@ public function getFunctions(): array public function getTriggers(?string $table = null): array { try { - if ($this->isTableProvided($table)) { + if ($this->hasTable($table)) { return DB::connection($this->connection)->select('SHOW TRIGGERS WHERE `Table` = ?', [$table]); } diff --git a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php index d9d550a3..fa4c84d4 100644 --- a/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/PostgreSQLSchemaDriver.php @@ -60,7 +60,7 @@ public function getTriggers(?string $table = null): array FROM information_schema.triggers WHERE trigger_schema = current_schema() '; - if ($this->isTableProvided($table)) { + if ($this->hasTable($table)) { $sql .= ' AND event_object_table = ?'; return DB::connection($this->connection)->select($sql, [$table]); diff --git a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php index bac40335..26d56f5a 100644 --- a/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php +++ b/src/Mcp/Tools/DatabaseSchema/SQLiteSchemaDriver.php @@ -36,7 +36,7 @@ public function getTriggers(?string $table = null): array { try { $sql = "SELECT name, sql FROM sqlite_master WHERE type = 'trigger'"; - if ($this->isTableProvided($table)) { + if ($this->hasTable($table)) { $sql .= ' AND tbl_name = ?'; return DB::connection($this->connection)->select($sql, [$table]);