Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Router Laravel Package

hoa/router

Hoa\Router is a standalone PHP routing component for mapping HTTP and CLI requests to handlers. It offers route patterns, variable constraints, HTTP method matching, and flexible callbacks, making it easy to build clean, maintainable routing layers for custom PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

hoa/router is a small, standalone routing component: it matches an incoming HTTP (or CLI) request against named route patterns and gives you back a Hoa\Router\Route object containing the matched callable and extracted parameters. It does not dispatch for you, nor does it provide controllers, middleware, or a PSR-7 bridge — you wire those yourself.

Install with Composer:

composer require hoa/router

Before writing code, look at four classes:

  • Hoa\Router\Router — abstract base, route registry, route(), unroute(), getRoutes().
  • Hoa\Router\Http — HTTP request matching (get(), post(), any(), etc.).
  • Hoa\Router\Route — matched route metadata: rule, methods, callable, substitutes, binds.
  • Hoa\Router\Exception\NotFound — thrown when nothing matches.

Minimal front-controller use case:

require __DIR__ . '/vendor/autoload.php';

$router = new Hoa\Router\Http();

$router->get('home', '/', function () {
    echo 'Home';
});

$router->get('user', '/user/(?<id>\d+)', function ($id) {
    echo 'User #', $id;
});

try {
    $route    = $router->route();
    $callable = $route->getCallable();

    // Substitutes are keyed by placeholder name; pass them positionally.
    $callable(...array_values($route->getSubstitutes()));
} catch (Hoa\Router\Exception\NotFound $e) {
    http_response_code(404);
    echo 'Not found';
}

First practical use case: replace a hand-rolled switch ($_SERVER['REQUEST_URI']) in a small PHP app with named routes, then later add a router-aware dispatcher or integrate it into Hoa's MVC stack.

Implementation Patterns

Named routes and lookup. Every route has an ID as its first argument. You can retrieve a route definition without matching the current request:

$router->get('blog.show', '/blog/(?<slug>[a-z0-9-]+)', $callable);

$route = $router->route('blog.show'); // no matching performed

Use named routes when you need to inspect or mutate a definition later. Remove one with $router->unroute('blog.show').

HTTP verbs. Use the verb-specific helpers for clarity:

$router->get('list',   '/posts',        $list);
$router->post('create','/posts',        $create);
$router->put('update', '/posts/(?<id>\d+)', $update);
$router->delete('destroy', '/posts/(?<id>\d+)', $destroy);
$router->any('catchall', '/.*', $fallback);

any() matches all methods; ordering matters because the first matching rule wins.

Pattern placeholders. Placeholders use named PCRE groups:

$router->get('post', '/post/(?<id>\d+)/(?<slug>[^/]+)', function ($id, $slug) {
    // ...
});

After matching, getSubstitutes() returns an associative array (['id' => '42', 'slug' => 'hello']). Values are strings; cast/validate them before use. Because the regex is your validation layer, keep patterns tight rather than relying on post-match checks.

Callables. The third argument is anything Hoa\Consistency\Xcallable understands: closures, invokable objects, [ClassName::class, 'method'], 'function_name', etc. Route::getCallable() returns the wrapped callable; invoke it yourself. A reusable dispatcher is usually cleaner than repeating invocation logic:

function dispatch(Hoa\Router\Router $router): void
{
    try {
        $route    = $router->route();
        $callable = $route->getCallable();
        $callable(...array_values($route->getSubstitutes()));
    } catch (Hoa\Router\Exception\NotFound $e) {
        http_response_code(404);
    }
}

Binds and route metadata. Route::setBinds() / getBinds() attach arbitrary data to a route definition (defaults, middleware hints, permission requirements). This is a lightweight extension point — read binds in your dispatcher before invoking the callable:

$router->get('admin', '/admin', $callable)->setBinds([
    'middleware' => ['auth', 'csrf'],
]);

Integration with a front controller. Keep the router at the edge and push actual work into services/controllers. A typical bootstrap: create the router, register routes from a config file or route module, dispatch, catch NotFound, and let domain exceptions bubble up. If you use PSR-7, extract method and path from the request and feed the router through its request setters (see Gotchas) or adapt $_SERVER before constructing it.

CLI routing. Hoa\Router\Cli uses the same named-route concept but matches command-line arguments. Use it when you want a single routing abstraction across web and console entry points, though in practice most projects use a dedicated console framework instead.

Testing. Because matching depends on request globals, the usual test seam is to set request parts explicitly on the router before calling route():

$router = new Hoa\Router\Http();
$router->setMethod('GET');
$router->setPath('/user/42');
$router->setQuery('');

$route = $router->route();

If your installed version lacks those setters, manipulate $_SERVER/$_REQUEST in the test bootstrap or wrap router construction in a small factory.

Gotchas and Tips

  • Archived package. The last release was 2017-01-14. There is no active maintenance, no security patch stream, and essentially no downstream ecosystem (zero dependents reported). Treat it as a stable-but-frozen dependency, pin the exact version, and vendor/audit accordingly.
  • PHP 8 compatibility is not guaranteed. The package predates PHP 7.4/8.x. Test on your target runtime; older Hoa dependencies (Consistency, Protocol) may emit deprecations or break on newer language features. If PHP 8 support is required, budget time for a fork or choose a maintained router.
  • No dispatcher, no middleware, no reverse routing. You must invoke the callable, handle 404s, run middleware, and generate URLs yourself. There is no route('name', $params) URL builder. This is the biggest day-to-day difference from Laravel/Symfony routers.
  • First match wins. Register static and specific routes before wildcard/catch-all routes. A broad any('/.*', ...) placed early will swallow everything.
  • Trailing slashes and anchoring are literal. /foo and /foo/ are different rules unless your pattern accounts for it. Keep patterns explicit and test both variants if users may type either.
  • route() throws on miss. Always wrap the match call in try/catch for Hoa\Router\Exception\NotFound (check the exact exception class in the installed version). Uncaught, it becomes a 500 instead of a 404.
  • Substitutes are attacker-controlled. Regex constraints are your primary input validation, but still cast and validate before using IDs in queries or file paths. Do not trust that (?<id>\d+) survived intact through all server/proxy layers.
  • Invocation semantics differ between PHP versions. Passing an associative substitutes array directly to call_user_func_array() behaves differently on PHP 8 (named arguments). Use ...array_values($route->getSubstitutes()) or an explicit argument mapper to keep behavior consistent.
  • Request globals are the default source. Hoa\Router\Http reads from $_SERVER/$_REQUEST. This makes it awkward behind proxies, in subdirectory deployments, or in unit tests. Normalize REQUEST_URI, SCRIPT_NAME, and method override headers before construction; use the setters for tests.
  • Hoa protocol dependency. Hoa packages often rely on the hoa:// stream protocol and Hoa\Protocol bootstrap. If you see path-resolution errors, ensure Composer's autoload/bootstrap has initialized Protocol, or avoid hoa:// paths entirely.
  • Performance is per-request regex compilation. Routes are compiled and matched sequentially each request; there is no route cache. Fine for tens of routes, noticeable for hundreds of complex patterns. Order routes by expected frequency to short-circuit matching.
  • Extension points are limited. setBinds() and subclassing Hoa\Router\Http / Hoa\Router\Router are the main hooks. If you need route groups, prefixes, middleware pipelines, or named URL generation, build them around the router rather than expecting them inside it.
  • Check composer why hoa/router before adopting. Given the archive status and zero dependents, this is likely a legacy-integration choice rather than a greenfield one. If you are starting fresh, compare against a maintained alternative; if you are maintaining an existing Hoa-based app, isolate the router behind your own interface so it can be swapped later.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle