All guides

HTTP routing and request limits

skyyware/stage v0.1.5

Applies to Stage 0.1.5. Sources checked on 9 October 2026.

Stage\Http\Application accepts Route and Idea objects. Route::get() constructs a GET route. new Route('POST', '/path', $handler) constructs a route for another method. A handler receives Request and returns Response.

Return JSON from a GET route

With PHP 8.4 or a later PHP 8 release, install skyyware/stage:^0.1.5 through Composer. Create public/index.php:

<?php
declare(strict_types=1);

use Stage\Http\Application;
use Stage\Http\Response;
use Stage\Http\Route;

require dirname(__DIR__) . '/vendor/autoload.php';

(new Application(
    Route::get('/', fn () => Response::json(['hello' => 'world'])),
))->run();

Response::json() encodes the value and sets the JSON content type. It returns status 200 unless you pass another status as its second argument. The handler above needs no request data, so it declares no argument.

From the project directory, run:

php -S 127.0.0.1:8080 -t public public/index.php

Open http://127.0.0.1:8080/. The response is {"hello":"world"} with status 200. Stop the development server with Ctrl+C. For production, configure your web server to expose only public/.

Match paths and methods

A named parameter fills one path segment, such as {slug} in /pages/{slug}. The handler reads $request->parameters['slug']. Values are decoded once. Decoded slashes, backslashes, control characters, and empty values do not match. Parameter values still need application validation.

The router checks literal paths before named patterns. Other overlapping patterns use registration order. Application rejects duplicate method and path shapes during construction, even when only the parameter names differ. A method mismatch does not fall through to a less specific path.

Request Behavior
Unknown path 404
Unsupported method on a known path 405 with Allow
OPTIONS on a known path 204 with Allow
HEAD without a dedicated HEAD route Uses the GET handler; run() omits the response body

Applications cannot register their own OPTIONS route. A GET handler may also run for HEAD, so it must account for side effects such as download counters.

Validate input

Request::json() decodes JSON into PHP values. Malformed JSON throws HttpError(400). Valid JSON can be a scalar, array, or null. The application must validate the required shape and values before using them.

Application::run() and Request::fromGlobals() default to a raw body limit of 1,048,576 bytes, or 1 MiB. Oversized raw input produces 413. This limit does not cover PHP-parsed multipart uploads. Server and PHP limits remain separate.

Handle errors and output

handle() converts a handler's HttpError into a JSON error with the same status. Unexpected exceptions escape handle(). run() catches them and returns a generic 500. Stage\Security\Forbidden is not an HttpError: the HTTP adapter must translate it into 403.

Ordinary response bodies are buffered strings. HTML output is not escaped automatically. Each response header has one string value; repeated Set-Cookie headers and arbitrary response streaming are unsupported.

Sources