From f71318726d3ad50205340e9c418ba25f27ee6816 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Thu, 6 Aug 2026 15:54:35 +0600 Subject: [PATCH] updated hot paths --- .env.example | 2 + .gitattributes | 3 ++ .gitignore | 3 ++ README.md | 20 ++++++- composer.json | 2 +- config/app.php | 13 +++-- tests/Feature/FrameworkRuntimeTest.php | 4 +- tests/Feature/RouteCacheCliTest.php | 63 ++++++++++++++++++++++ tests/Feature/SkeletonDistributionTest.php | 27 ++++++++++ 9 files changed, 130 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 871128d..d6a84f9 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,8 @@ APP_ENV=local APP_DEBUG=true APP_URL=http://localhost APP_CONFIG_CACHE_TYPE=sharded +APP_CONTAINER_COMPILED=bootstrap/cache/container.php +APP_CONTAINER_COMPILED_ACTIVATION=off ROUTER_MATCHER=fused diff --git a/.gitattributes b/.gitattributes index b1dcba0..8d4d429 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ /.gitattributes export-ignore /captainhook.json export-ignore /composer.lock export-ignore +/plan.md export-ignore /CODE_OF_CONDUCT.md export-ignore /CONTRIBUTING.md export-ignore /SECURITY.md export-ignore @@ -34,7 +35,9 @@ /bin export-ignore /bootstrap/cache/config/*.php export-ignore /bootstrap/cache/console/*.php export-ignore +/bootstrap/cache/container.php export-ignore /bootstrap/cache/modules.php export-ignore +/bootstrap/cache/optimize.php export-ignore /bootstrap/cache/routes/*.php export-ignore /database export-ignore /resources export-ignore diff --git a/.gitignore b/.gitignore index 467e32b..af056dd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,11 @@ /.route-cache-* /.vscode/ /.windsurf/ +/bootstrap/cache/container.php /bootstrap/cache/modules.php +/bootstrap/cache/optimize.php /database/*.sqlite /vendor/ *~ composer.lock +plan.md diff --git a/README.md b/README.md index 72cd171..06d90de 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ Existing files are preserved unless `--force` is supplied. Register application commands explicitly in `routes/console.php`, schedules in `routes/schedule.php`, and supervised workers in `routes/workers.php`. +For an intentionally static controller action, use a first-class callable such +as `Route::get('/reports', ReportsController::show(...))`; route caching converts +that safe form to a plain descriptor. Captured closures and instance handlers +remain supported through the general cached-handler path. + ## Optional modules A new project installs only the core runtime. Add or publish capabilities as the @@ -118,7 +123,20 @@ php infbyte app:ready ``` `optimize` compiles configuration, route, command, schedule, and module metadata -so requests do less work. `optimize:clear` removes generated caches. +plus the eligible HTTP container graph so requests do less work. It publishes a +final optimize manifest only after the complete set is ready. `optimize:clear` +removes every generated artifact and leaves uncached execution available. + +`APP_CONFIG_CACHE_TYPE=single` loads one snapshot; `sharded` loads configuration +namespaces on demand. Neither is universally faster: measure the application's +minimal, authenticated, and database-backed routes before selecting one. +Compiled-container activation remains `off` by default because small requests +may not amortize its boot cost; use `always` only for a measured deployment, +commonly a persistent worker with OPcache. + +Webrick automatically selects the response emitter. A known deployment may set +`WEBRICK_EMITTER` to `fpm`, `frankenphp`, `lsapi`, `unit`, `swoole`, +`roadrunner`, or `workerman`; leave it unset when runtime detection is desired. The included deployment helper validates writable runtime paths and builds the same caches: diff --git a/composer.json b/composer.json index ba9ac8a..08f0a6d 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "type": "project", "require": { "php": ">=8.4", - "infocyph/foundation": "^1.2.1" + "infocyph/foundation": "^1.3" }, "require-dev": { "infocyph/phpforge": "dev-main@dev" diff --git a/config/app.php b/config/app.php index 99d0595..8c5ecc6 100644 --- a/config/app.php +++ b/config/app.php @@ -44,11 +44,15 @@ | controls environment-aware definitions. "lazy_loading" defers supported | services, while "request_scope" isolates request-lived entries. | - | "compiled" may point to compiled container metadata. Debug tracing is - | disabled by default; "level" selects the trace detail when enabled. + | "compiled" selects the application-owned resolver artifact. Activation + | remains off unless a measured deployment explicitly selects "always". + | Debug tracing is disabled by default; "level" selects its detail. | | Alias/environment examples: `http` and `production`. Boolean switches use - | `true|false`. Compiled path example: `bootstrap/cache/container.php`. + | `true|false`. The compiled artifact defaults to + | `bootstrap/cache/container.php`; APP_CONTAINER_COMPILED may select another + | application-owned relative or absolute path. + | Compiled activation values: `off|always`. | Trace levels: `off|node|info|warn|warning|error|verbose`. | */ @@ -57,7 +61,8 @@ 'environment' => env('APP_ENV', 'local'), 'lazy_loading' => env('APP_CONTAINER_LAZY_LOADING', false), 'request_scope' => env('APP_CONTAINER_REQUEST_SCOPE', true), - 'compiled' => env('APP_CONTAINER_COMPILED'), + 'compiled' => env('APP_CONTAINER_COMPILED', 'bootstrap/cache/container.php'), + 'compiled_activation' => env('APP_CONTAINER_COMPILED_ACTIVATION', 'off'), 'debug_tracing' => [ 'enabled' => env('APP_CONTAINER_DEBUG_TRACING', false), 'level' => env('APP_CONTAINER_DEBUG_TRACE_LEVEL', 'node'), diff --git a/tests/Feature/FrameworkRuntimeTest.php b/tests/Feature/FrameworkRuntimeTest.php index a415762..b45a8d0 100644 --- a/tests/Feature/FrameworkRuntimeTest.php +++ b/tests/Feature/FrameworkRuntimeTest.php @@ -144,10 +144,12 @@ function expectedNotifierClass(Application $app): string $registered = []; foreach ($router->routes() as $route) { - $registered[$route->getMethod() . ' ' . $route->getPath()] = true; + $registered[$route->getMethod() . ' ' . $route->getPath()] = $route->getHandler(); } expect(array_keys($registered))->toBe(['GET /api/health', 'GET /json']); + expect($registered['GET /api/health'])->toBeInstanceOf(Closure::class) + ->and($registered['GET /json'])->toBeInstanceOf(Closure::class); $http = $app->testing()->http(); $health = $http->get('/api/health') diff --git a/tests/Feature/RouteCacheCliTest.php b/tests/Feature/RouteCacheCliTest.php index 24a1c54..8eed028 100644 --- a/tests/Feature/RouteCacheCliTest.php +++ b/tests/Feature/RouteCacheCliTest.php @@ -3,8 +3,15 @@ declare(strict_types=1); use Composer\InstalledVersions; +use App\Http\Controllers\SystemController; +use Infocyph\Foundation\Auth\AuthManager; +use Infocyph\Foundation\Database\DatabaseManager; use Infocyph\Foundation\Foundation; +use Infocyph\Foundation\Messaging\MessagingManager; use Infocyph\Foundation\Routing\RouteCachePath; +use Infocyph\Foundation\Session\SessionManager; +use Infocyph\Webrick\Request\Request; +use Infocyph\Webrick\Router\Matching\FusedMatcher; it('uses the environment application name and reports the Foundation runtime version', function (): void { $root = dirname(__DIR__, 2); @@ -52,6 +59,62 @@ expect(is_file($cacheFile))->toBeTrue(); expect(filesize($cacheFile))->toBeGreaterThan(0); + $matcher = FusedMatcher::make()->enableCache($cacheFile); + [$cachedRoute] = $matcher->match('GET', 'localhost', '/json'); + $cachedHandler = $cachedRoute->getHandler(); + $webrickVersion = InstalledVersions::getVersion('infocyph/webrick') ?? '0.0.0'; + $usesNativeHandler = version_compare($webrickVersion, '3.3.0', '>='); + if ($usesNativeHandler) { + expect($cachedHandler)->toBe([SystemController::class, 'json']) + ->and(class_exists(\Opis\Closure\Serializer::class, false))->toBeFalse(); + } else { + expect($cachedHandler)->toBeInstanceOf(Closure::class); + } + + $runtime = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) + . '/infbyte-cached-runtime-' . bin2hex(random_bytes(5)); + $runtimeCache = $runtime . '/bootstrap/cache/routes/fused.php'; + mkdir(dirname($runtimeCache), 0775, true); + mkdir($runtime . '/routes', 0775, true); + copy($cacheFile, $runtimeCache); + file_put_contents( + $runtime . '/routes/missing.php', + " $runtime, + '_config_cache' => false, + 'router' => [ + 'cache' => true, + 'files' => ['missing.php'], + 'matcher' => 'fused', + ], + ]); + $repository = $app->container()->getRepository(); + $response = $app->handle(Request::fake(method: 'GET', uri: 'http://localhost/json')); + + expect($response->getStatusCode())->toBe(200) + ->and((string) $response->getBody())->toContain('memory') + ->and($repository->hasResolvedSingleton(AuthManager::class))->toBeFalse() + ->and($repository->hasResolvedSingleton(SessionManager::class))->toBeFalse() + ->and($repository->hasResolvedSingleton(DatabaseManager::class))->toBeFalse() + ->and($repository->hasResolvedSingleton(MessagingManager::class))->toBeFalse() + ->and(get_included_files())->not->toContain($runtime . '/routes/missing.php'); + } finally { + if (isset($app)) { + $app->container()->unset(); + } + unlink($runtimeCache); + rmdir(dirname($runtimeCache)); + rmdir(dirname(dirname($runtimeCache))); + rmdir(dirname(dirname(dirname($runtimeCache)))); + unlink($runtime . '/routes/missing.php'); + rmdir($runtime . '/routes'); + rmdir($runtime); + } + [$clearExitCode, $clearOutput] = runInfbyteCommand([ PHP_BINARY, $root . '/infbyte', diff --git a/tests/Feature/SkeletonDistributionTest.php b/tests/Feature/SkeletonDistributionTest.php index 754c7d7..5da994b 100644 --- a/tests/Feature/SkeletonDistributionTest.php +++ b/tests/Feature/SkeletonDistributionTest.php @@ -140,6 +140,33 @@ expect($installExitCode)->toBe(0) ->and(hash_file('sha256', $project . '/.env'))->toBe($before); + + $deployOutput = []; + $deployExitCode = 0; + exec(sprintf( + 'cd %s && bash ./deploy.sh 2>&1', + escapeshellarg($project), + ), $deployOutput, $deployExitCode); + + expect($deployExitCode)->toBe(0, implode("\n", $deployOutput)) + ->and($project . '/bootstrap/cache/config/__manifest.php')->toBeFile() + ->and($project . '/bootstrap/cache/routes/fused.php')->toBeFile() + ->and($project . '/bootstrap/cache/console/commands.php')->toBeFile() + ->and($project . '/bootstrap/cache/modules.php')->toBeFile(); + + $clearOutput = []; + $clearExitCode = 0; + exec(sprintf( + 'cd %s && %s infbyte optimize:clear 2>&1', + escapeshellarg($project), + escapeshellarg(PHP_BINARY), + ), $clearOutput, $clearExitCode); + + expect($clearExitCode)->toBe(0, implode("\n", $clearOutput)) + ->and($project . '/bootstrap/cache/config/__manifest.php')->not->toBeFile() + ->and($project . '/bootstrap/cache/routes/fused.php')->not->toBeFile() + ->and($project . '/bootstrap/cache/console/commands.php')->not->toBeFile() + ->and($project . '/bootstrap/cache/modules.php')->not->toBeFile(); } finally { removeInfbyteDistributionFixture($fixture); }