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

Simple Router Laravel Package

pecee/simple-router

A fast, lightweight PHP router with a simple, expressive API. Supports route groups, middleware, custom regex patterns, named routes and reverse routing, plus optional parameter binding and subdomain routing. Works with or without a framework.

View on GitHub
Deep Wiki
Context7

Getting Started

pecee/simple-router is a standalone, Laravel-flavored router. It gives you Route::get(), groups, middleware, named routes, and controller actions — but without the framework, the container, or Eloquent. Think of it as the Illuminate\Routing component extracted and made usable in any PHP project.

Install and wire it in your front controller:

// composer require pecee/simple-router
require __DIR__ . '/vendor/autoload.php';

use Pecee\SimpleRouter\SimpleRouter as Router;

// Optional: let route strings omit FQCNs
Router::setDefaultNamespace('App\\Controllers');

Router::get('/', fn () => 'Hello')->name('home');

Router::get('/users/{id}', function ($id) {
    return "User {$id}";
});

Router::get('/health', [HealthController::class, 'index']);

Router::start(); // must be the last call

Where to look first:

  1. Pecee\SimpleRouter\SimpleRouter — the static facade (get, post, group, start, request, response). This is your primary API surface and maps almost 1:1 to the Laravel Route facade.
  2. Pecee\SimpleRouter\Route\Route — the fluent chain returned by each verb: ->name(), ->where(), ->middleware(), ->domain().
  3. Pecee\Http\Request / Pecee\Http\Response — no Laravel Illuminate\Http\Request; these are the objects your middleware and handlers receive.
  4. Pecee\Http\Middleware\BaseCsrfVerifier — if you need CSRF, this is the starting point (there is no @csrf directive; you render the token yourself).

First real use case: replace a legacy switch ($_SERVER['PHP_SELF']) block. Point your web server's rewrite to index.php, define three or four routes, call start(), and you have typed parameters, 404 handling, and a place to hang middleware.

Implementation Patterns

Grouping with prefix + middleware — same shape as Laravel:

Router::group(['prefix' => '/admin', 'middleware' => AuthMiddleware::class], function () {
    Router::get('/dashboard', [DashboardController::class, 'index'])->name('admin.dashboard');
    Router::post('/users',    [UserController::class, 'store']);
});

Controller action syntax. Two accepted forms: 'App\Controllers\UserController@show' or [UserController::class, 'show']. Unlike Laravel, the container does not instantiate controllers — the router does new $controllerClass and invokes the method. If a controller needs dependencies, either resolve them inside the method via your own container, or use a boot manager:

class ContainerBoot implements IRouterBootManager
{
    public function boot(Route $route) { /* set up container, session, etc. */ }
}
Router::setBootManager(new ContainerBoot());

Route parameters and constraints. Match Laravel's {id} and ->where():

Router::get('/posts/{id}', [PostController::class, 'show'])->where(['id' => '[0-9]+']);
Router::get('/posts/{slug?}', [PostController::class, 'bySlug']); // optional

Inline regex — '/posts/{id:[0-9]+}' — is also supported and useful for quick constraints without chaining.

Middleware is not aliased by default. Unlike Laravel's web/auth groups, you register classes by FQCN or wrap them in a group. Implement Pecee\Http\Middleware\IMiddleware::handle(Request $request). Return null to continue, or a Response to short-circuit — this differs from Laravel, where a middleware returns the request to proceed.

class AuthMiddleware implements IMiddleware
{
    public function handle(Request $request)
    {
        if (!$_SESSION['user'] ?? null) {
            return (new Response())->redirect('/login', 302);
        }
        return null; // continue
    }
}

Responses. Controllers and closures may return a string (auto-wrapped), an array (serialized as JSON), or an explicit Response/JsonResponse/RedirectResponse object. Use the object form when you need status codes or headers — return new JsonResponse($data, 201) rather than setting headers globally.

Named routes for URL generation. ->name('user.show') then Router::getUrl('user.show', ['id' => 5]) in views or redirects. Name the root route (->name('home')) if you generate links back to / — generating URLs for home/root routes was broken before 5.4.1.7, so pin at least that patch if getUrl() on your index route matters. This requires request context (scheme/host), so it behaves differently in CLI — pass an absolute URL context or accept the relative fallback.

CSRF. Call Router::csrfVerifier(new BaseCsrfVerifier()) during boot. The verifier checks a token in the request body or a header; you must expose the token yourself in templates (read it from the request/session — there's no Blade directive). Exempt endpoints by overriding the verifier's excluded path patterns.

Custom exception handling. The default handler renders a debug page; in production you want your own:

Router::setExceptionHandler(new class implements IExceptionHandler {
    public function handleException(RouterException $e, ?IRoute $route = null) { /* render 404/500 */ }
});

Route file organization. For anything non-trivial, split routes by concern and require them from a central file. There is no php artisan route:list equivalent; a small dev-only endpoint that reflects Router::getRoutes() is a common substitute.

Gotchas and Tips

  • Route order is first-match-wins. Unlike Laravel's more sophisticated compilation, wildcards registered early will swallow later specific routes. Define /users/{id} after /users/create.
  • Router::start() must be the absolute last call. Any output before it (whitespace, BOM, echo) blocks headers and breaks redirects/responses. This is a common "works locally, fails on server" bug.
  • No automatic dependency injection. Type-hinting Request $request in a controller works; a service class or repository does not. Plan for manual construction or a boot manager — a persistent friction point if you're used to Laravel's container.
  • Middleware semantics differ. Returning null continues; returning a Response stops. Returning the request object does nothing useful. This trips up developers porting Laravel middleware.
  • No @csrf / no Blade. Tokens must be injected in your template engine manually. Check BaseCsrfVerifier for the storage key it reads from.
  • Default namespace is opt-in. If you use string controllers without Router::setDefaultNamespace(), they'll resolve to the global namespace and fail with a cryptic "controller not found."
  • Trailing slashes and URL decoding can produce mismatches. Register both '/foo' and '/foo/', or normalise REQUEST_URI in your bootstrap. Encoded parameters (%20) are decoded by the router — apply where constraints if the value shape matters.
  • getUrl() needs request context. It reads the current host/scheme, so calling it during a CLI script or queue worker silently yields an incomplete URL. Prefer absolute URLs built from a configured base in background code. Home/root URLs (getUrl('home') where the route path is /) were fixed in 5.4.1.7 — if you're on an earlier 5.x patch, upgrade rather than working around it with a hardcoded /.
  • Debugging missing routes: inspect Router::getRequest()->getMethod() and getUri() first — the request target is often the culprit (subdirectory installs, reverse-proxy X-Forwarded-* not trusted). Second, inspect Router::getRoutes() to verify registration order and namespaces.
  • No route cache / no compiled container. Cold performance is fine for a few hundred routes; it will not match Laravel's cached route table. If you're at scale, cache at the HTTP layer instead of expecting a built-in optimization.
  • Version and PHP constraints. The 5.x line is current (5.4.1.7 is the latest patch at time of writing); v4 targeted PHP 7.2+ and is stable on 8.x, and older v3 tags are for legacy projects. Pin the major and check the CHANGELOG before upgrading — the middleware and exception interfaces have shifted between majors, and 5.x patches are bug-fix releases you should generally take.
  • Extension points worth knowing: IRouterBootManager (per-route boot), IExceptionHandler (error rendering), RouteController (subclass to control controller instantiation — useful for wiring a container), and custom BaseCsrfVerifier subclasses (excluded paths, token header name).
  • Pair with a real $_SERVER-based SAPI. Under PHP's built-in server, REQUEST_URI handles rewrites; under nginx/Apache you must forward all unmatched paths to index.php, or the router only ever sees /.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource