OxPHP runtime adapter — run Symfony, Laravel and PSR-15 applications on the OxPHP server in both traditional and worker mode.
Extends symfony/runtime. Zero hard
dependencies on any framework or PSR-7 implementation — everything is
auto-detected when the container is resolved.
- PHP 8.4+
- OxPHP server (for production use; the runtime transparently falls back to
symfony/runtimedefaults when not running under the OxPHP SAPI) symfony/runtime^6.4 || ^7.0
composer require oxphp/runtimeThen set the runtime in .env:
APP_RUNTIME=OxPHP\Runtime\RuntimeYour public/index.php stays as it would be under symfony/runtime:
<?php
use App\Kernel;
require_once \dirname(__DIR__).'/vendor/autoload_runtime.php';
return fn(array $context) => new Kernel($context['APP_ENV'], (bool)$context['APP_DEBUG']);Return type from index.php |
Runner |
|---|---|
Psr\Http\Server\RequestHandlerInterface |
Psr15Runner |
Illuminate\Contracts\Http\Kernel |
LaravelRunner |
Symfony\Component\HttpKernel\HttpKernelInterface |
HttpKernelRunner |
Symfony\Component\HttpFoundation\Response |
HttpFoundationResponseRunner |
Psr\Http\Message\ResponseInterface |
Psr7ResponseRunner |
Any other type throws LogicException. Console applications are out of
scope — run them under the stock PHP CLI SAPI.
Mode is auto-detected via oxphp_is_worker(). Nothing to configure on
the runtime side. In worker mode:
- Your application boots once per worker thread.
oxphp_worker()drives the loop internally; the runtime wraps each request intry/catch(\Throwable)so a broken handler cannot poison the worker.- Between requests, OxPHP performs a soft reset (output buffers, headers, superglobals). Framework-specific state you want to reset on top of that goes through the resetter chain.
Register custom resetters via APP_RUNTIME_OPTIONS:
APP_RUNTIME_OPTIONS="{\"resetters\":[\"App\\\\Runtime\\\\EntityManagerResetter\"]}"Your class implements OxPHP\Runtime\Resetter\ResetterInterface:
use OxPHP\Runtime\Resetter\ResetterInterface;
final class EntityManagerResetter implements ResetterInterface
{
public function __construct(private readonly EntityManagerInterface $em) {}
public function reset(): void
{
$this->em->clear();
}
}Symfony users get the container's services_resetter (any service tagged
kernel.reset) wired up automatically — no registration needed.
Used only by PSR-15 and bare PSR-7 Response paths. Auto-detected in this order:
Nyholm\Psr7\Factory\Psr17Factory(recommended)GuzzleHttp\Psr7\HttpFactoryHttpSoft\Message\ServerRequestFactory(+ siblings)Laminas\Diactoros\ServerRequestFactory(+ siblings)
Override via APP_RUNTIME_OPTIONS:
APP_RUNTIME_OPTIONS="{\"psr17_factory\":\"My\\\\Factory\"}"StreamedResponse, StreamedJsonResponse, and PSR-7 bodies with
readable streams are flushed chunk-by-chunk via oxphp_stream_flush() —
ideal for SSE and large payloads.
For "respond early, keep working" patterns use oxphp_finish_request()
directly in your controller:
$response = new JsonResponse(['status' => 'accepted']);
// ... after sending ...
\oxphp_finish_request();
// background work continues; the client already got the responseMIT