coroq/request-handler

A base class for PSR-15 request handlers that reduces the boilerplate

Maintainers

Package info

github.com/coroq-com/request-handler

pkg:composer/coroq/request-handler

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-07-30 05:04 UTC

This package is auto-updated.

Last update: 2026-07-30 05:08:23 UTC


README

A base class for PSR-15 request handlers that reduces the boilerplate.

The same handler, first in plain PSR-15, then with this library:

Plain PSR-15

class ContactController implements RequestHandlerInterface {
  public function __construct(
    private ResponseFactoryInterface $responseFactory,
  ) {
  }

  public function handle(ServerRequestInterface $request): ResponseInterface {
    return match ($request->getMethod()) {
      'GET' => $this->showForm($request),
      'POST' => $this->submit($request),
      default => $this->responseFactory->createResponse(405),
    };
  }

  private function showForm(ServerRequestInterface $request): ResponseInterface {
    $error = $request->getQueryParams()['error'] ?? null;
    // ... render the form ...
  }

  private function submit(ServerRequestInterface $request): ResponseInterface {
    $body = $request->getParsedBody();
    $message = is_array($body) ? ($body['message'] ?? '') : '';
    // ... store it, redirect ...
  }
}

With coroq/request-handler

use Coroq\RequestHandler\RequestHandler;

class ContactController extends RequestHandler {
  public function __construct(
    private ResponseFactoryInterface $responseFactory,
  ) {
  }

  public function handleGet(): ResponseInterface {
    $error = $this->get['error'] ?? null;
    // ... render the form ...
  }

  public function handlePost(): ResponseInterface {
    $message = $this->post['message'] ?? '';
    // ... store it, redirect ...
  }
}

Installation

composer require coroq/request-handler

Requires PHP ^8.0. Depends only on psr/http-message and psr/http-server-handler.

How it works

RequestHandler implements PSR-15 RequestHandlerInterface. handle() does two things:

  • loads the request into properties ($request, $get, $post, etc.),
  • dispatches by HTTP method to handleGet(), handlePost(), etc.

Handler methods

Method HTTP method
handleGet() GET
handlePost() POST
handlePut() PUT
handleDelete() DELETE
handlePatch() PATCH
handleQuery() QUERY (the draft "safe method with a body")
handleHead() HEAD
handleOptions() OPTIONS
handleOthers() any other method

Override the ones your handler supports. Every default implementation throws MethodNotAllowedException — except handleHead(), which falls back to handleGet() (see below). handle() itself is deliberately overridable — for method-agnostic handlers, or to wrap the dispatch with parent::handle().

Request properties

The request and its contents are easily accessible from every handler method:

Property Content
$this->request the ServerRequestInterface itself
$this->get query parameters (like $_GET)
$this->post body parameters (like $_POST)
$this->cookie cookies (like $_COOKIE)
$this->attributes request attributes (path parameters etc.)

Two notes:

  • $this->post is filled only when the parsed body is an array (HTML forms). For JSON, parse the body into an array in an earlier middleware, or read $this->request->getBody() yourself.
  • Request handlers hold the request, so use one instance per request. Don't share instances via a caching container.

Handling HEAD requests

handleHead() falls back to handleGet() by default, so a handler that supports GET answers HEAD automatically. A HEAD response is the GET response without its body — stripping the body is the job of your emitter or web server. Override handleHead() when you want to answer HEAD yourself.

Handling unsupported methods and OPTIONS

The base never builds a response; when a method is not supported it throws. MethodNotAllowedException carries allowedMethods — the methods the handler supports, detected from which handleXxx() are overridden. Catch it once, in a middleware near the front of your queue:

use Coroq\RequestHandler\MethodNotAllowedException;

try {
  return $handler->handle($request);
}
catch (MethodNotAllowedException $exception) {
  // include OPTIONS because this middleware answers it
  $allow = join(', ', array_unique([...$exception->allowedMethods, 'OPTIONS']));
  if ($request->getMethod() === 'OPTIONS') {
    return $this->responseFactory->createResponse(204)->withHeader('Allow', $allow);
  }
  return $this->responseFactory->createResponse(405)->withHeader('Allow', $allow);
}

This one catch gives every handler an RFC 9110 compliant 405 and a generic OPTIONS answer (CORS preflights included).

RequestLoadingTrait on its own

The request loading is available standalone:

use Coroq\RequestHandler\RequestLoadingTrait;

abstract class MyBase implements RequestHandlerInterface {
  use RequestLoadingTrait;

  public function handle(ServerRequestInterface $request): ResponseInterface {
    $this->loadRequest($request);
    // $this->get, $this->post, ... are now ready
  }
}

License

MIT