softwarepunt/minirouter

Fast and lightweight PSR-7 request router for PHP

v0.3.0 2024-09-30 14:39 UTC

This package is auto-updated.

Last update: 2024-10-30 14:47:07 UTC


README

A fast and lightweight router for PHP, compatible with PSR-7:

  • ⏩ Super fast request-response routing
  • 🌟 Static or dynamic routes with unlimited URL variables
  • 💉 Automatic parameter injection for requests and route variables

Installation

Install the package using Composer:

composer require softwarepunt/minirouter

This package is compatible with PHP 8.2+.

Usage

Registering routes

Initialize a new instance of MiniRouter and start registering routes:

<?php

use SoftwarePunt\MiniRouter\MiniRouter;

$router = new MiniRouter();
$router->register('/ping', function () {
    return 'pong';
});

Incoming requests (PSR-7)

You will need a PSR-7 implementation, such as guzzlehttp/psr7, to provide incoming request data. For example:

use GuzzleHttp\Psr7\ServerRequest;

$request = ServerRequest::fromGlobals();

Dispatching requests

Once your routes are registered, pass your request object (any PSR-7 compatible RequestInterface) to the router:

$response = $router->dispatch($request);

This call will return a PSR-7 compatible ResponseInterface.

If a matching route is found, your target function will be executed and its response will be returned. It can return its own response object, or a string.

Accessing the request

When your target function is called, you can ask for an instance of RequestInterface to access the request directly:

use Psr\Http\Message\RequestInterface;

$router->register('/show-user-agent', function (RequestInterface $request) {
    return "Your user agent is: {$request->getHeaderLine('User-Agent')}";
});

The request object will be injected automatically. The name and order of the parameter doesn't matter.

Routes with variables

When you register your routes, you can also use one or more URL variables that can then be injected into your target function:

$router->register('/echo/$myUrlVar/$varTwo', function (string $myUrlVar, string $varTwo) {
    return "echo: {$myUrlVar} - {$varTwo}";
});

Variables are defined in the route by using the $ prefix. Their values are automatically extracted from the request URL, and injected into your target function as named parameters (strings).

Routing to (controller) classes

You can also register routes that will construct a class instance, and invoke a specific method. This can help you organize your code, and is more typical for a model-view-controller (MVC) architecture.

<?php

use SoftwarePunt\MiniRouter\MiniRouter;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

$router = new MiniRouter();
$router->registerController('/greet/$name', SomeControllerClass::class, "targetMethod");

class SomeControllerClass
{
    public function before(RequestInterface $request): ?ResponseInterface
    {
        // This method will be called *before* routing to the target method
        // You can access the request and other variables here
        if (!exampleAuthCheck())
            return new Response(403, body: "Access denied!");
        // If before() returns a response, routing is aborted and that response is returned
        // This is a good place to handle things like pre-flight checks and authentication
        return null;
    }

    public function targetMethod(string $name): ResponseInterface
    {
        return new Response(200, body: "Hello, {$name}!");
    }
}

You can use the optional before method to perform pre-flight checks and run common code - for example, to handle authentication before allowing routing to proceed.

Simple redirects

You can also use the registerRedirect utility function to quickly register HTTP 301 or 302 redirects:

// 301 Moved Permanently
$router->registerRedirect('/from', '/to', true);
// 302 Found (Temporary redirect) 
$router->registerRedirect('/from', '/to');

Examples

Complete index.php

<?php

use MyProject\HomeController;
use GuzzleHttp\Psr7\ServerRequest;
use SoftwarePunt\MiniRouter\MiniRouter;

// ---------------------------------------------------------------------------------------------------------------------
// Init

require_once "../bootstrap.php";

$router = new MiniRouter();

// ---------------------------------------------------------------------------------------------------------------------
// Routes

$router->registerController('/', HomeController::class, 'serveHome');

// ---------------------------------------------------------------------------------------------------------------------
// Main

$request = ServerRequest::fromGlobals();
$response = $router->dispatch($request);

// ---------------------------------------------------------------------------------------------------------------------
// Serve

http_response_code($response->getStatusCode());

foreach ($response->getHeaders() as $name => $values) {
    foreach ($values as $value) {
        header(sprintf('%s: %s', $name, $value), false);
    }
}

echo $response->getBody()->getContents();
exit(0);