Skip to content

PHP Router — Complete Guide to nikic FastRoute and Symfony Routing

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about PHP Router. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP routers like FastRoute and Symfony Routing component handle URL matching, parameter extraction, route grouping, and URL generation for HTTP applications.

What You'll Learn

By the end of this tutorial, you'll implement FastRoute and Symfony Routing, define route patterns, extract parameters, generate URLs from route names, and integrate routes with controllers.

Why Routers Matter

Routers are the entry point of every HTTP application. A good router handles URL patterns, method matching, parameter validation, and generates URLs for templates and redirects.

Real-World Use

A REST API uses FastRoute with 50+ route definitions grouped by prefix (/api/users, /api/orders). Routes map to controller methods and generate URLs for HATEOAS responses.

Router Path

flowchart LR
  A[Slim Middleware] --> B[PHP Router]
  B --> C[Doctrine ORM]
  B --> D[Eloquent ORM]
  C --> E[PHP-DI Container]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

FastRoute Setup

FastRoute is a fast route parser that generates regex-based matchers.

<?php
use FastRoute\RouteCollector;
require_once "vendor/autoload.php";
$dispatcher = FastRoute\simpleDispatcher(function (RouteCollector $r) {
    $r->addRoute("GET", "/users", "getUsers");
    $r->addRoute("GET", "/users/{id:\d+}", "getUserById");
    $r->addRoute("POST", "/users", "createUser");
    $r->addRoute("PUT", "/users/{id:\d+}", "updateUser");
    $r->addRoute("DELETE", "/users/{id:\d+}", "deleteUser");
});
$httpMethod = $_SERVER["REQUEST_METHOD"];
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);

Route Dispatching

Process the matched route and call the appropriate handler.

<?php
function handleRequest(array $routeInfo): void {
    switch ($routeInfo[0]) {
        case FastRoute\Dispatcher::NOT_FOUND:
            http_response_code(404);
            echo json_encode(["error" => "Not Found"]);
            break;
        case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
            http_response_code(405);
            $allowed = $routeInfo[1];
            echo json_encode(["error" => "Method Not Allowed", "allowed" => $allowed]);
            break;
        case FastRoute\Dispatcher::FOUND:
            $handler = $routeInfo[1];
            $vars = $routeInfo[2];
            echo call_user_func_array($handler, $vars);
            break;
    }
}

Route Groups

Organize related routes with common prefixes.

<?php
$dispatcher = FastRoute\simpleDispatcher(function (RouteCollector $r) {
    $r->addGroup("/api", function (RouteCollector $r) {
        $r->addGroup("/users", function (RouteCollector $r) {
            $r->addRoute("GET", "", ["UserController", "index"]);
            $r->addRoute("GET", "/{id:\d+}", ["UserController", "show"]);
            $r->addRoute("POST", "", ["UserController", "store"]);
        });
        $r->addGroup("/orders", function (RouteCollector $r) {
            $r->addRoute("GET", "", ["OrderController", "index"]);
            $r->addRoute("GET", "/{id:\d+}", ["OrderController", "show"]);
        });
    });
    $r->addGroup("/admin", function (RouteCollector $r) {
        $r->addRoute("GET", "/dashboard", ["AdminController", "dashboard"]);
    });
});

Symfony Routing Component

Symfony's Routing component provides URL matching and generation.

<?php
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\Generator\UrlGenerator;
$routes = new RouteCollection();
$routes->add("user_list", new Route("/users", ["_controller" => "UserController::index"], methods: ["GET"]));
$routes->add("user_show", new Route("/users/{id}", ["_controller" => "UserController::show"], requirements: ["id" => "\d+"], methods: ["GET"]));
$routes->add("user_create", new Route("/users", ["_controller" => "UserController::create"], methods: ["POST"]));
$context = new RequestContext();
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->match("/users/42");

URL Generation

Generate URLs from route names using Symfony Router.

<?php
use Symfony\Component\Routing\Generator\UrlGenerator;
$generator = new UrlGenerator($routes, $context);
$url = $generator->generate("user_show", ["id" => 42]);
echo $url;
$urlList = $generator->generate("user_list");
echo $urlList;

Common Mistakes

1. Not Escaping Route Parameters

User input in routes should be validated. Use regex constraints like {id:\d+} to ensure parameters match expected patterns.

2. Conflicting Route Patterns

/users and /users/{id} where {id} matches numbers only. Without constraints, /users/create might match {id} instead of a different route.

3. Ignoring HTTP Method Matching

GET /users/delete should not match DELETE /users/{id}. Use method-specific routes.

4. Performance with Too Many Routes

FastRoute compiles to regex, handling hundreds of routes efficiently. For Symfony, cache the route collection in production.

5. Not Handling Trailing Slashes

/users and /users/ are different. Normalize URIs by removing or enforcing trailing slashes.

Practice Questions

1. How does FastRoute match routes?

It compiles route definitions into regex patterns for fast matching.

2. What is a route group?

A set of routes sharing a common prefix, organized under a group callback.

3. How do you add parameter constraints in FastRoute?

Use {id:\d+} to require the parameter to match a regex pattern.

4. What does Symfony UrlGenerator do?

Generates URLs from route names and parameters, useful for templates and redirects.

5. Challenge: Implement a router with middleware support using FastRoute.

<?php
class Router {
    private $dispatcher;
    private array $middleware = [];
    public function addRoute(string $method, string $path, callable $handler): void {
        $this->dispatcher = FastRoute\simpleDispatcher(fn($r) => $r->addRoute($method, $path, $handler));
    }
    public function addMiddleware(callable $mw): void {
        $this->middleware[] = $mw;
    }
    public function dispatch(string $method, string $uri): mixed {
        $route = $this->dispatcher->dispatch($method, $uri);
        if ($route[0] !== FastRoute\Dispatcher::FOUND) return null;
        $handler = $route[1];
        foreach ($this->middleware as $mw) $handler = $mw($handler);
        return $handler($route[2] ?? []);
    }
}

FAQ

What is the difference between FastRoute and Symfony Router?

FastRoute focuses on speed. Symfony Router provides URL generation and more features for enterprise apps.

Can I use FastRoute with Slim?

Slim has its own router built on FastRoute. No additional setup needed.

How do I reverse-route (generate URLs) with FastRoute?

FastRoute does not support URL generation. Use Symfony Router or implement it manually.

What is route caching?

Compiling route definitions to PHP code for faster matching. Symfony Router supports route caching.

How do I handle optional route parameters?

Define separate routes for each combination or use a default value in the controller.

Mini Project: Simple Router Framework

Build a minimal router framework combining FastRoute with controller dispatching.

<?php
use FastRoute\RouteCollector;
use FastRoute\Dispatcher;
class AppRouter {
    private array $routes = [];
    public function get(string $path, callable $handler): void {
        $this->routes[] = ["GET", $path, $handler];
    }
    public function post(string $path, callable $handler): void {
        $this->routes[] = ["POST", $path, $handler];
    }
    public function run(): void {
        $dispatcher = FastRoute\simpleDispatcher(function (RouteCollector $r) {
            foreach ($this->routes as [$method, $path, $handler]) {
                $r->addRoute($method, $path, $handler);
            }
        });
        $route = $dispatcher->dispatch($_SERVER["REQUEST_METHOD"], parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));
        if ($route[0] === Dispatcher::FOUND) {
            echo call_user_func_array($route[1], $route[2]);
        } else {
            http_response_code(404);
            echo json_encode(["error" => "Not found"]);
        }
    }
}

What's Next

PHP Doctrine ORM PHP Eloquent ORM PHP PSR-7 HTTP Messages

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro