amphp/http-server
Asynchronous, non-blocking HTTP server for PHP built on Amp. Create high-performance web apps and APIs with event-driven I/O, middleware, routing, and streaming request/response bodies. Includes HTTP/1.1 and HTTP/2 support and TLS integration.
Start by installing the package via Composer:
composer require amphp/http-server amphp/artax
Note: amphp/artax is not required for the server, but often used alongside for client-side async HTTP. For the server alone, only amphp/http-server is needed.
Create a minimal server in server.php:
#!/usr/bin/env php
<?php
require 'vendor/autoload.php';
use Amp\Http\Server\Server;
use Amp\Http\Server\RequestHandler\CallableRequestHandler;
use Amp\Http\Server\Router;
use Amp\Http\Server\ErrorHandler\DefaultErrorHandler;
use Amp\Http\Server\StaticContent\FileSystemHandler;
use Amp\Http\Server\StandaloneBootstrap;
use Amp\Socket\Socket;
$router = new Router();
$router->addRoute('GET', '/hello', new CallableRequestHandler(function () {
return new Amp\Http\Server\Response(200, ['Content-Type' => 'text/plain'], 'Hello, async world!');
}));
$server = new Server(
[new Socket('0.0.0.0:1337')],
$router,
new DefaultErrorHandler()
);
$server->start();
Amp\Loop::run();
Then run:
php server.php
The first use case is typically building a lightweight JSON API endpoint with non-blocking I/O (e.g., querying a database or external API without blocking the event loop).
Middleware Pipeline: Chain middleware for cross-cutting concerns like auth, rate limiting, or CORS. Middleware can be async functions or classes implementing Amp\Http\Server\RequestHandler.
$middleware = new ClassMiddleware(
new AuthenticateMiddleware(),
new RateLimiterMiddleware(),
$router
);
$server = new Server([...], $middleware, ...);
Streaming for Large Payloads: Use Amp\Http\Server\Stream to pipe file contents or generate responses incrementally (e.g., for large CSV exports or Server-Sent Events):
use Amp\Http\Server\Stream;
use Amp\Artax\DefaultClient;
$handler = new CallableRequestHandler(function (Amp\Http\Server\Request $request): Amp\Http\Server\Response {
$stream = new Stream(fopen('large-file.csv', 'r'));
return new Amp\Http\Server\Response(200, ['Content-Type' => 'text/csv'], $stream);
});
Custom Routing: Prefer Router with route constraints (/api/{id:\d+}), or build fully custom request handlers for advanced logic.
Standalone Bootstrap: Use StandaloneBootstrap for production-friendly daemonization (forking workers, signal handling, graceful shutdown).
Integration with Frameworks: While not tied to Symfony/Laravel, you can embed its router/handlers inside Laravel/Lumen by wrapping in a custom controller that delegates to async handlers.
Amp\Loop is mandatory: Always call Amp\Loop::run() — the server won’t start without it. Never use pcntl_fork() or stream_select() inside async code.
No blocking calls: Avoid file_get_contents(), sleep(), or database queries that don’t use async drivers (e.g., amphp/redis, amphp/postgres). Use Amp\call() or Amp\Promise\wait() only in CLI entrypoints—not inside handlers.
Keep-alive quirks: Clients must send Connection: keep-alive header explicitly if they expect persistence. Server respects Connection: close in requests.
Streaming lifecycle: When returning Stream, ensure the resource is seekable and not fopen('php://memory') — prefer tmpfile() or real files.
Error handling: The DefaultErrorHandler returns generic HTML errors. Override with custom handlers that return JSON (for APIs) or HTML (for web apps), but always ensure async errors (Amp\WrappedPromiseException) are caught and unwrapped.
Extension points: Replace core components via DI-friendly constructor params:
RequestHandler → custom routingErrorHandler → error formattingHttp1ServerCodec → tweak HTTP/1.1 parsing (advanced)Http2ServerCodec → enable HTTP/2 support (experimental, requires amphp/http2)Debugging: Log with Amp\ByteStream\Channel or custom middleware. Use Amp\Loop::debug() to inspect event loop stats.
Tests: Unit-test handlers in isolation (they’re just callables) and use integration tests with Server + Artax\Client (synchronous wait() in test runner is acceptable).
How can I help you explore Laravel packages today?