symfony/runtime
Symfony Runtime decouples PHP applications from global state by centralizing bootstrapping and execution in a runtime layer. It enables flexible entry points, better testability, and smoother integration with different environments and frameworks.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require symfony/runtime:^8.1.0-BETA3
For Laravel, no additional steps are required beyond installation—it integrates seamlessly with the framework’s existing bootstrapping.
First Use Case:
Replace Laravel’s default bootstrap/app.php with a runtime-aware wrapper. Create a file runtime.php in your project root:
<?php
use Symfony\Component\Runtime\Runner\RunnerInterface;
use Symfony\Component\Runtime\SymfonyRuntime;
require __DIR__.'/vendor/autoload.php';
$runtime = new SymfonyRuntime(
__DIR__.'/config/runtime.php', // Path to your runtime config
__DIR__.'/var/cache/runtime' // Cache directory
);
return $runtime->run(
fn (RunnerInterface $runner) => $runner->load(function () {
// Your Laravel bootstrapping logic here
return require __DIR__.'/bootstrap/app.php';
})
);
Update your public/index.php to use this runtime:
require __DIR__.'/../runtime.php';
Runtime Configuration:
Create config/runtime.php to define your runtime options:
return [
'project_dir' => __DIR__.'/..',
'env' => $_SERVER['APP_ENV'] ?? 'dev',
'debug' => (bool) ($_SERVER['APP_DEBUG'] ?? 'true'),
'kernel' => App\Kernel::class,
'http_method_override' => true,
'trusted_proxies' => $_SERVER['TRUSTED_PROXIES'] ?? '127.0.0.1',
'extra_dotenv_files' => [
__DIR__.'/../.env.local',
],
// New: Explicitly gate argv access for security (recommended)
'argv' => $_SERVER['argv'] ?? [],
];
CLI Integration:
For Artisan commands, create a custom artisan script in your project root:
#!/usr/bin/env php
<?php
require __DIR__.'/runtime.php';
Make it executable and use it instead of the default artisan:
chmod +x artisan
./artisan migrate
Pattern: Decouple services from global state by injecting runtime-aware dependencies. Example:
// Before (global state)
class FeatureFlagService {
public function isEnabled(string $flag): bool {
return $_ENV['FEATURE_' . strtoupper($flag)] === 'true';
}
}
// After (runtime-injected config)
class FeatureFlagService {
public function __construct(private array $config) {}
public function isEnabled(string $flag): bool {
return $this->config['features'][$flag] ?? false;
}
}
// Register in runtime config:
$runtime->setConfig([
'features' => [
'new_ui' => true,
'experimental_api' => false,
],
]);
Pattern: Replace $_SERVER-dependent middleware with runtime-scoped alternatives.
Example:
// After (runtime-configured)
$router->middleware('host', function ($request, $next) use ($runtime) {
$allowedHosts = $runtime->getConfig()['allowed_hosts'] ?? [];
if (!in_array($_SERVER['HTTP_HOST'], $allowedHosts)) {
abort(403);
}
return $next($request);
});
Pattern: Use runtime-scoped .env files to avoid cross-environment leaks.
Example:
// runtime.php
$runtime = new SymfonyRuntime(
__DIR__.'/config/runtime.php',
__DIR__.'/var/cache/runtime',
[
'extra_dotenv_files' => [
__DIR__.'/../.env.local',
__DIR__.'/../.env.' . $_SERVER['APP_ENV'],
],
// New: Explicitly gate argv for security
'argv' => $_SERVER['argv'] ?? [],
]
);
Pattern: Leverage Symfony Runtime’s built-in support for non-HTTP runtimes. Example (RoadRunner):
// config/runtime.php
return [
'project_dir' => __DIR__.'/..',
'env' => 'prod',
'debug' => false,
'kernel' => App\Kernel::class,
'runner' => \Symfony\Component\Runtime\Runner\RoadRunnerRunner::class,
'roadrunner' => [
'worker' => 'queue-worker',
'timeout' => 30,
],
// New: Explicit argv gating for CLI workers
'argv' => $_SERVER['argv'] ?? [],
];
Pattern: Combine Laravel’s default bootstrapping with runtime-specific logic. Example:
$runtime->run(function (RunnerInterface $runner) {
// Load Laravel's default bootstrapping
$app = require __DIR__.'/bootstrap/app.php';
// Override or extend runtime-specific logic
$app->extend('config', function ($config, $app) use ($runner) {
return $config->merge($runner->getConfig()['app'] ?? []);
});
return $app;
});
Pattern: Mock runtime dependencies for isolated tests. Example:
use Symfony\Component\Runtime\Runner\RunnerInterface;
$mockRunner = $this->createMock(RunnerInterface::class);
$mockRunner->method('getConfig')->willReturn([
'features' => ['new_ui' => true],
// New: Mock argv for CLI testing
'argv' => ['test:command'],
]);
$runtime = new SymfonyRuntime(__DIR__.'/config/runtime.php', __DIR__.'/var/cache/runtime');
$runtime->setRunner($mockRunner);
$service = new FeatureFlagService();
$this->assertTrue($service->isEnabled('new_ui'));
Global State in Legacy Code:
$_SERVER, $_ENV, or $_FILES. These will break when using Symfony Runtime.Runtime::getOriginalServer() or Runtime::getOriginalEnv() to access global state only when necessary, but refactor to avoid it long-term.Caching Conflicts:
var/cache/runtime is writable and excluded from Laravel’s cache configuration:
// config/cache.php
'default' => env('CACHE_DRIVER', 'file'),
'stores' => [
'runtime' => [
'driver' => 'file',
'path' => runtime_dir('cache'),
],
],
CLI vs. HTTP SAPI Detection:
$runtime = new SymfonyRuntime(
__DIR__.'/config/runtime.php',
__DIR__.'/var/cache/runtime',
['sapi' => PHP_SAPI, 'argv' => $_SERVER['argv'] ?? []]
);
Environment Variable Overrides:
.env files may override Laravel’s default .env loading, causing unexpected behavior.extra_dotenv_files to load additional files after Laravel’s default .env:
$runtime->setConfig([
'extra_dotenv_files' => [
__DIR__.'/../.env.runtime', // Load after Laravel's .env
],
]);
Security: argv Gating (New in 8.1.0-BETA3):
$_SERVER['argv'] may fail unless explicitly gated.'argv' => $_SERVER['argv'] ?? [] in your runtime config or constructor options. For CLI scripts, ensure argv is passed explicitly:
$runtime = new SymfonyRuntime(
__DIR__.'/config/runtime.php',
__DIR__.'/var/cache/runtime',
['argv' => $GLOBALS['argv'] ?? []] // Pass argv explicitly for CLI
);
Dependency Injection Conflicts:
How can I help you explore Laravel packages today?