Alpha ZealPHP is early-stage and under active development. APIs may change between minor versions until v1.0. Feedback and bug reports welcome on GitHub.
API Index — Namespaces, Packages, Reports, Indices

HostRouterMiddleware
in package
implements MiddlewareInterface

Host-Router Middleware (nginx server_name virtual-host equivalent)

Dispatches the request to a per-host handler based on the Host: request header. The handler is a normal callable returning any of ZealPHP's supported return shapes (string body, PSR-7 Response, Generator, array → JSON, int → status code).

nginx equivalent: server { server_name a.com; location / { ... } } server { server_name b.com; location / { ... } }

If no host matches and no '*' (catch-all) handler is registered, the middleware passes through to the next handler. This lets you mix host-routed and host-agnostic apps inside one ZealPHP instance:

$app->addMiddleware(new \ZealPHP\Middleware\HostRouterMiddleware([ 'docs.example.com' => fn() => 'docs landing page', 'api.example.com' => fn() => ['status' => 'ok'], '*.example.com' => fn() => 'subdomain fallback', 'www.*' => fn() => 'trailing-wildcard catch', '~^admin\..+' => fn() => 'regex match', '*' => fn() => 'default site', ]));

Host matching is case-insensitive and ignores port (example.com:8080 matches the rule example.com). IPv6 literals ([::1]:80) are parsed correctly — the port separator is the : after the closing ].

Match precedence (nginx ngx_hash_find_combined order):

  1. Exact match
  2. Leading-wildcard *.example.com
  3. Trailing-wildcard www.*
  4. Regex ~^pattern (in registration order)
  5. Catch-all *

Host validation (nginx parity, only when HostRouterMiddleware is active):

  • HTTP/1.1 with missing Host400
  • Duplicate Host headers → 400
  • Invalid Host characters (outside [a-zA-Z0-9:.\-_~!$&'()*+,;=%@[\]]) → 400
  • Trailing dot normalised: example.com.example.com

Table of Contents

Interfaces

MiddlewareInterface

Properties

$catchAll  : callable|null
$handlers  : array<string, callable>
$regexRules  : array<int, array{pattern: string, handler: callable}>
$trailingWildcards  : array<int, array{host: string, handler: callable}>
$wildcards  : array<int, array{host: string, handler: callable}>

Methods

__construct()  : mixed
process()  : ResponseInterface
coerceResponse()  : ResponseInterface
isValidHostHeader()  : bool
Validate a raw Host header value (nginx ngx_http_validate_host parity).
matchHandler()  : callable|null
Match the normalised (lowercased, port-stripped) host against registered rules in nginx precedence order: 1. Exact 2. Leading-wildcard (*.example.com) 3. Trailing-wildcard (www.*) 4. Regex (~^pattern, registration order) 5. Catch-all (*)
stripPort()  : string
Strip the port from a Host header value.

Properties

$regexRules

private array<int, array{pattern: string, handler: callable}> $regexRules

regex rules in declaration order

$trailingWildcards

private array<int, array{host: string, handler: callable}> $trailingWildcards

trailing-wildcard rules (www.*) in declaration order

$wildcards

private array<int, array{host: string, handler: callable}> $wildcards

leading-wildcard rules (*.x) in declaration order

Methods

__construct()

public __construct(array<string, mixed> $hosts) : mixed
Parameters
$hosts : array<string, mixed>

host => callable, plus optional '*' catch-all. Marked mixed at the type-level because PHP can't enforce callable inside an array; each handler is validated at runtime.

process()

public process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
Parameters
$request : ServerRequestInterface
$handler : RequestHandlerInterface
Return values
ResponseInterface

coerceResponse()

private coerceResponse(mixed $result) : ResponseInterface
Parameters
$result : mixed
Return values
ResponseInterface

isValidHostHeader()

Validate a raw Host header value (nginx ngx_http_validate_host parity).

private isValidHostHeader(string $host) : bool

Allowed characters: a-zA-Z0-9 : . - _ ~ ! $ & ' ( ) * + , ; = % @ [ ] Rejects NUL bytes, control characters, <, >, {, }, |, \, ^, `, space, and consecutive dots (which nginx also rejects).

Parameters
$host : string
Return values
bool

matchHandler()

Match the normalised (lowercased, port-stripped) host against registered rules in nginx precedence order: 1. Exact 2. Leading-wildcard (*.example.com) 3. Trailing-wildcard (www.*) 4. Regex (~^pattern, registration order) 5. Catch-all (*)

private matchHandler(string $host) : callable|null
Parameters
$host : string
Return values
callable|null

stripPort()

Strip the port from a Host header value.

private stripPort(string $host) : string

Handles: example.com:8080example.com [::1]:8080[::1] [::1][::1] ::1::1 (no brackets, no port)

Parameters
$host : string
Return values
string
On this page