symfony/runtime
Symfony Runtime decouples PHP applications from global state by providing a flexible runtime entry point and bootstrapping layer. It standardizes how apps are started across environments and integrations, improving portability and testability.
Installation:
composer require symfony/runtime
For Laravel, ensure compatibility with your PHP version (v8.x requires PHP 8.4+; v7.x works with PHP 7.2+).
Basic Usage:
Create a Runtime instance in your entry point (e.g., public/index.php or artisan):
use Symfony\Component\Runtime\Runner\RunnerInterface;
use Symfony\Component\Runtime\SymfonyRuntime;
$runner = new SymfonyRuntime(); // Default runner
$runtime = new SymfonyRuntime($runner);
$runtime->boot();
First Use Case:
Replace Laravel’s default index.php with a runtime-aware entry point:
// public/index.php
require __DIR__.'/../vendor/autoload.php';
$runtime = new SymfonyRuntime();
$runtime->boot();
// Delegate to Laravel's kernel (now runtime-aware)
$kernel = new App\Kernel();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
Key Files to Review:
SymfonyRuntime (core class)RunnerInterface (runtime abstraction)Extend Laravel’s Kernel to work with Symfony Runtime:
// app/Kernel.php
use Symfony\Component\Runtime\RuntimeInterface;
class Kernel extends HttpKernel
{
public function __construct(RuntimeInterface $runtime = null)
{
$this->runtime = $runtime;
parent::__construct($this->getContainer(), $this->requestStack, $this->httpKernel);
}
protected function getContainer()
{
if ($this->runtime) {
$this->runtime->boot();
}
return parent::getContainer();
}
}
Implement a RunnerInterface to handle Laravel-specific logic (e.g., Artisan commands, HTTP requests):
// app/Runtime/LaravelRunner.php
use Symfony\Component\Runtime\Runner\RunnerInterface;
class LaravelRunner implements RunnerInterface
{
public function run(array $arguments = []): int
{
return (new Artisan())->run($arguments);
}
}
Load .env files per runtime (e.g., separate .env.testing for tests):
$runtime = new SymfonyRuntime(new SymfonyRuntimeOptions([
'env' => 'testing',
'extraEnvFiles' => ['.env.testing'],
]));
Bind the runtime to Laravel’s container:
// config/app.php
'bindings' => [
Symfony\Component\Runtime\RuntimeInterface::class => function ($app) {
return new SymfonyRuntime();
},
];
Use different runners for HTTP/CLI/workers:
// public/index.php (HTTP)
$runtime = new SymfonyRuntime(new HttpRunner());
// artisan (CLI)
$runtime = new SymfonyRuntime(new ArtisanRunner());
// worker.php (RoadRunner)
$runtime = new SymfonyRuntime(new WorkerRunner());
Replace Global State:
$_SERVER['HTTP_HOST'] with dependency-injected Request or Runtime:
$host = $runtime->getOptions()->get('http_host'); // From runtime options
RuntimeInterface::getProjectDir() instead of __DIR__ or base_path().Artisan Commands: Wrap commands in a runtime-aware closure:
$runtime->run(function () {
Artisan::call('migrate');
});
Middleware:
Inject RuntimeInterface into middleware:
public function handle($request, Closure $next, RuntimeInterface $runtime)
{
$runtime->boot();
return $next($request);
}
Service Providers: Boot the runtime in a provider:
public function boot()
{
$this->app->make(RuntimeInterface::class)->boot();
}
Testing: Mock the runtime for isolated tests:
$runtime = $this->createMock(RuntimeInterface::class);
$runtime->method('getOptions')->willReturn(new SymfonyRuntimeOptions(['env' => 'testing']));
$this->app->instance(RuntimeInterface::class, $runtime);
$runtime = new SymfonyRuntime(new FrankenPhpRunner());
$runtime = new SymfonyRuntime();
$runtime->bootIfNotBooted(); // Only boots if not already booted
.env files to prevent leaks:
$runtime = new SymfonyRuntime(new SymfonyRuntimeOptions([
'extraEnvFiles' => ['.env.production', '.env.tenant-' . $tenantId],
]));
symfony/options-resolver to validate env vars:
$options = new SymfonyRuntimeOptions([
'requiredEnvVars' => ['APP_KEY', 'DB_PASSWORD'],
]);
Global State Residue:
$_SERVER/$_ENV in legacy code.symfony/var-dumper to audit globals:
var_dump($_SERVER, $_ENV); // Check for unintended usage
php -d display_errors=1 to catch deprecated global access.Circular Dependencies:
$this->app->booting(function () {
$this->app->make(RuntimeInterface::class)->bootIfNotBooted();
});
Environment Mismatches:
.env files not loaded in expected order.extraEnvFiles:
$runtime = new SymfonyRuntime(new SymfonyRuntimeOptions([
'env' => 'production',
'extraEnvFiles' => ['.env', '.env.production.local'],
]));
Runner Conflicts:
RunnerInterface::supports() to validate:
if (!$runner->supports($this->getSapiName())) {
throw new \RuntimeException('Unsupported SAPI');
}
PHP Version Mismatches:
composer require symfony/runtime:^7.4
Request Object Reuse:
Request objects in kernel methods.$kernel = new Kernel($runtime);
$response = $kernel->handle($request); // Request reused if already created
Runtime Introspection:
$runtime->isBooted(); // bool
$runtime->getOptions()->all(); // array
Logging:
$runtime = new SymfonyRuntime(new SymfonyRuntimeOptions([
'debug' => true,
]));
Common Errors:
bootIfNotBooted() instead of boot().cli, fpm).requiredEnvVars in SymfonyRuntimeOptions.Profiling:
$start = microtime(true);
$runtime->boot();
echo microtime(true) - $start; // Boot duration
RunnerInterface for new environments (e.gHow can I help you explore Laravel packages today?