Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Runtime Laravel Package

symfony/runtime

Symfony Runtime decouples PHP applications from global state by handling environment, input, output, and exit through a front controller. It simplifies bootstrapping, makes code more testable, and supports multiple runtimes and frameworks.

View on GitHub
Deep Wiki
Context7

Getting Started

Symfony Runtime is a bootstrapping abstraction, not a Laravel integration. It gives you a generated entrypoint (vendor/autoload_runtime.php), a resolver that injects context/arguments into your app closure, and a runner that knows how to execute the returned application. Laravel still boots itself; there is no first-class Illuminate support. The day-to-day value is therefore concrete: unify HTTP/CLI/worker entrypoints, isolate global-state reads to the edge, and make bootstrapping configurable without editing entrypoint files.

Adopt it only if you have a real target such as FrankenPHP worker mode, RoadRunner, Lambda/Bref, or a need to standardise bootstrapping across HTTP and CLI. Otherwise Laravel’s default public/index.php + artisan is simpler and has fewer moving parts.

Minimal steps

  1. Install the package and allow its Composer plugin:

    composer require symfony/runtime
    

    In composer.json, Composer 2.2+ requires plugin approval:

    {
        "config": {
            "allow-plugins": {
                "symfony/runtime": true
            }
        }
    }
    

    Pin the major version to match your PHP/Laravel constraints. Symfony Runtime 8.x requires PHP 8.4; Laravel projects commonly need ^7.4 or ^6.4.

  2. Look first at these files/classes:

    • vendor/autoload_runtime.php — the generated file that replaces vendor/autoload.php in entrypoints.
    • Symfony\Component\Runtime\SymfonyRuntime — getResolver() and getRunner() are the core decisions.
    • Symfony\Component\Runtime\Resolver\ClosureResolver — how $context and other arguments are injected into your closure.
    • Symfony\Component\Runtime\Runner\... — especially the HTTP kernel and console application runners.
    • Official docs: https://symfony.com/doc/current/components/runtime.html.
  3. First use case: prove the plumbing with a tiny response, then wrap Laravel’s kernel. Replace public/index.php with:

    <?php
    
    use App\Runtime\LaravelHttpRunner;
    use Illuminate\Contracts\Http\Kernel;
    
    require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
    
    return static function (array $context) {
        /** @var \Illuminate\Foundation\Application $app */
        $app = require dirname(__DIR__).'/bootstrap/app.php';
    
        return new LaravelHttpRunner($app->make(Kernel::class));
    };
    

    And own the runner:

    namespace App\Runtime;
    
    use Illuminate\Contracts\Http\Kernel;
    use Illuminate\Http\Request;
    use Symfony\Component\Runtime\RunnerInterface;
    
    final class LaravelHttpRunner implements RunnerInterface
    {
        public function __construct(private Kernel $kernel)
        {
        }
    
        public function run(): int
        {
            $request = Request::capture();
            $response = $this->kernel->handle($request);
            $response->send();
            $this->kernel->terminate($request, $response);
    
            return 0;
        }
    }
    

    This is the key mental model: Symfony Runtime resolves and runs; you supply the Laravel adapter.

Implementation Patterns

Wrap Laravel objects; do not return them directly. SymfonyRuntime recognises Symfony HttpKernelInterface, Symfony Console Application, Symfony Response, RunnerInterface, and callables. Laravel’s Illuminate\Foundation\Application and Illuminate\Contracts\Http\Kernel are not Symfony kernels. Returning them will not produce a useful runner. Return a RunnerInterface implementation or a custom runtime that knows how to map Laravel kernels to runners.

Share one bootstrap closure across HTTP and CLI. Keep bootstrap/app.php as the single source of application construction. In each entrypoint, return the appropriate runner:

// artisan
use App\Runtime\LaravelConsoleRunner;
use Illuminate\Contracts\Console\Kernel;

require_once __DIR__.'/vendor/autoload_runtime.php';

return static function (array $context) {
    $app = require __DIR__.'/bootstrap/app.php';

    return new LaravelConsoleRunner($app->make(Kernel::class));
};
final class LaravelConsoleRunner implements RunnerInterface
{
    public function __construct(private Kernel $kernel)
    {
    }

    public function run(): int
    {
        return $this->kernel->handle(
            new Symfony\Component\Console\Input\ArgvInput(),
            new Symfony\Component\Console\Output\ConsoleOutput()
        );
    }
}

This pattern lets the runtime own the require/exit lifecycle while Laravel owns request/command handling.

Use a custom runtime when you need centralised policy. If every entrypoint needs the same environment/debug/project-dir handling, register a custom runtime class via APP_RUNTIME:

APP_RUNTIME='App\Runtime\LaravelRuntime'

The class implements Symfony\Component\Runtime\RuntimeInterface and returns a resolver/runner combination. Keep it in Composer’s autoload map; it must be loadable before bootstrap/app.php runs. Treat APP_RUNTIME as trusted configuration, never as user input.

Configure through runtime options, not entrypoint edits. SymfonyRuntime accepts options such as env, debug, dotenv_path, project_dir, and use_putenv. In 7.4+, runtime options can be supplied as a string via APP_RUNTIME_OPTIONS; older versions generally expect an array in $_SERVER/$_ENV. Use this in Docker/CI to vary APP_ENV, APP_DEBUG, or .env paths without rebuilding images.

Be deliberate about dotenv. Symfony Runtime can load .env before Laravel’s LoadEnvironmentVariables runs. Laravel uses an immutable dotenv repository, so values already present in $_ENV/$_SERVER usually win. That can be useful, but it also means changing .env may not behave as expected if the runtime loaded it first. Pick one loader: either let the runtime load dotenv and skip Laravel’s loader, or keep Laravel’s loader and do not configure dotenv_path in the runtime. Test with Env::get() and getenv() to confirm which values are visible.

Worker/serverless runtimes are the strongest use case. FrankenPHP worker mode can be auto-detected by newer runtime versions; RoadRunner, Swoole, and Lambda adapters exist in the wider ecosystem. In a Laravel project, prefer Laravel Octane where possible; it already handles container reset, request lifecycle, and state leakage. If you use Symfony Runtime for workers, your runner must reset the Laravel container and any per-request state between requests. Do not stack Octane and a custom runtime worker unless you understand which one owns the loop.

Test the runner, not the generated entrypoint. PHPUnit runs under CLI SAPI, so booting autoload_runtime.php in tests will select CLI semantics and may call exit(). Unit-test LaravelHttpRunner/LaravelConsoleRunner with fake kernels and Request::create()/ArrayInput. Use an actual web server or php -S for entrypoint-level integration tests.

Debug runner selection by inspecting the returned value. When you see “Unable to determine the runner” or a type error, dump get_debug_type($app) inside the closure. Confirm whether the value is a RunnerInterface, a Symfony Response, a Symfony Application, or an unsupported Laravel object. Most integration failures are a missing adapter, not a runtime bug.

Gotchas and Tips

  • symfony/runtime is a Composer plugin. If allow-plugins is missing or you run composer install --no-plugins, vendor/autoload_runtime.php will not be generated. Your entrypoints then fail at require. Check for the file in every deployment image.
  • Never edit vendor/autoload_runtime.php. It is generated and overwritten by Composer. Customise via APP_RUNTIME, APP_RUNTIME_OPTIONS, or a custom runtime class.
  • autoload_runtime.php depends on $_SERVER['SCRIPT_FILENAME']. It returns early if that value is empty. This bites under RoadRunner, Swoole, Lambda, embedded servers, or custom worker bootstraps where SCRIPT_FILENAME is absent or points at the wrong file. Set it explicitly or bypass autoload_runtime.php in worker bootstraps.
  • The generated file calls exit($runner->run()). A runner that calls exit() itself prevents testing and may bypass termination paths. Return an integer status from run() and let the entrypoint exit.
  • Laravel’s kernel is not a Symfony kernel. This is the largest adoption trap. return $app->make(Kernel::class) will not work out of the box. Wrap it in RunnerInterface.
  • Terminable middleware needs the kernel runner path. If your closure returns an Illuminate\Http\Response directly, the runtime may send it without calling Laravel’s terminate(). Terminable middleware, session persistence, and post-response jobs may not run. Use a kernel-based runner for normal requests.
  • Environment and config caching can mask runtime options. php artisan config:cache freezes APP_ENV/APP_DEBUG values into the cached config. Changing APP_RUNTIME_OPTIONS or runtime dotenv values afterward will not take effect until you recache. Add config cache rebuilds to deployment steps.
  • Security history matters. CVE-2024-50340 involved reading from argv on non-CLI SAPIs; the patch was later bypassed and hardened again (CVE-2026-46626). Use patched releases and never let request/query data influence APP_ENV, APP_DEBUG, APP_RUNTIME, or APP_RUNTIME_OPTIONS. Runtime options are trusted deployment configuration.
  • APP_RUNTIME instantiates a class name. If the class is not autoloadable before your app bootstrap, you get a class-not-found error. Run composer dump-autoload after adding it and avoid defining it in files that are only required later.
  • Double dotenv and use_putenv differences can create confusing env values. getenv() versus $_ENV/$_SERVER may disagree depending on use_putenv. Laravel’s Env repository checks several sources; verify with a small diagnostic route or command when values differ between web and CLI.
  • project_dir may be wrong in symlinked deployments. Newer versions expose APP_PROJECT_DIR and make project_dir configurable. If vendor/ is symlinked or the app lives outside the expected root, set it explicitly and verify that relative paths in your bootstrap resolve correctly.
  • Error-handler ordering can change deprecation behaviour. Symfony Runtime registers error/exception handling; Laravel also registers its own. You may see deprecations silenced or converted differently. Do not rely on runtime error handling for Laravel application errors; set debug deliberately and keep Laravel’s handler authoritative.
  • Do not use the runtime to “fix” flaky tests by itself. It isolates the entrypoint, but Laravel tests that manipulate $_ENV, $_SERVER, or getenv() can still leak state. The real fix is dependency injection and explicit request/context objects, not just wrapping index.php.
  • CI/static analysis needs Composer installed first. Tools like PHPStan may complain about vendor/autoload_runtime.php not existing before composer install. Exclude the generated file from analysis and ensure CI runs Composer before static analysis.
  • Extension points are interfaces, not config flags. The practical extension points are RuntimeInterface (choose resolver/runner), ResolverInterface (inject Laravel Request or context), and RunnerInterface (own the request/command lifecycle). Prefer those over hacks inside the closure.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route