PHPackages                             alclab/subway-router - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. alclab/subway-router

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

alclab/subway-router
====================

php router

1.3.1(1y ago)023proprietaryPHPPHP ^8.3

Since Mar 18Pushed 1y ago1 watchersCompare

[ Source](https://github.com/Alcohol120/SubwayPHP)[ Packagist](https://packagist.org/packages/alclab/subway-router)[ RSS](/packages/alclab-subway-router/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (5)Dependencies (1)Versions (6)Used By (0)

SubwayPHP - php router

Usage
-----

[](#usage)

#### Basic example

[](#basic-example)

```
use Subway\Map;

$map = new Map(); // create router
$map->route('GET', '/', function ($request, $response, $route) {
    // home page
    return $response->body('home page');
});
$map->group('/news', function ($group) {
    $group->route('GET', '/', function ($request, $response, $route) {
        // news page
        return $response->body('news');
    });
    $group->route('GET', '/{id:i}', function ($request, $response, $route) {
        $postId = (int)$request->segment(2);
        // article page
        if($postId < 1) {
            return $response->status(404)->body('Not found');
        } else return $response->body("Article: {$pageId}");
    });
});
$map->build(); // routes built and ready to use
$map->dispatch(); // route

```

#### Install

[](#install)

```
composer require alclab/subway-router

```

Routes
------

[](#routes)

#### Create routes map

[](#create-routes-map)

```
use Subway\Map;

$map = new Map();

```

#### Static path

[](#static-path)

```
$map->route('GET', '/foo/bar', function($request, $route) {});

```

#### Variables in url

[](#variables-in-url)

```
$map->route('GET', '/foo/{bar}') // {bar} - any string
$map->route('GET', '/foo/{bar:i}') // {bar:i} - integers only
$map->route('GET', '/foo/{bar:a}') // {bar:a} - allowed alpha (a-zA-Z), underscores and dashes
$map->route('GET', '/foo/{bar:[a-z]{2}\d+}') - pattern matching (case insensitive)

```

#### Optional segments

[](#optional-segments)

```
$map->route('GET', '/foo?/{bar}') // first segment is optional

```

#### Groups

[](#groups)

```
$map->group('/foo', function ($group) {
    $group->route('GET', '/', function ($req) {}); // GET /foo
    $group->route('GET', '/bar', function ($req) {}); // GET /foo/bar
});

```

Preparing and dispatching routes
--------------------------------

[](#preparing-and-dispatching-routes)

#### Build

[](#build)

Your router has to be compiled before dispatching.

```
$map->build(); // now router is ready to use

```

You should re-build your router with any changes in your routes setup

#### Dispatch

[](#dispatch)

```
// using current request data from $_SERVER, $_POST, $_COOKIES etc...
$map->dispatch();

// using custom Request instance
$req = new Request(
    $_SERVER['REQUEST_METHOD'],
    $_SERVER['REQUEST_URI'],
    $_POST,
    getallheaders(),
    $_COOKIE,
    @file_get_contents('php://input'),
    $_FILES,
);
$map->displatch($req);

```

Routes estimation and priorities
--------------------------------

[](#routes-estimation-and-priorities)

Static segments has the highest priority.
Pattern segments has the medium priority.
Variable (any) segments has the lowest priority.

```
$map->route('GET', '/foo/{bar}')->name('any')
$map->route('GET', '/foo/{bar:i}')->name('integer')
$map->route('GET', '/foo/bar')->name('static')

$map->dispatch(new Request('GET', '/foo/bar')) // route named 'static' will be loaded
$map->dispatch(new Request('GET', '/foo/foo')) // route named 'any' will be loaded

```

Optional segments has less priority than mandatory ones

```
$map->route('GET', '/foo?/bar')->name('first')
$map->route('GET', '/foo/bar')->name('second')

$map->dispatch(new Request('GET', '/foo/bar')) // route named 'second' will be loaded
// Technically both routes are fits such request,
// but 'first' route will get less estimation due to an optional segment.
// In this example 'first' route will never be loaded

```

Middleware hooks
----------------

[](#middleware-hooks)

```
use Subway\Map;
use Subway\Route;
use Subway\Request;
use Subway\Response;
use Subway\Middleware;

class CustomMiddleware extends Middleware {

    // execute after route estimated
    public function onEstimated(int $rate, Request $request, Route $route) : int {
        // this hook should return integer
        // return -1 to skip current route
        return $rate;
    }

    // execute before route loaded
    public function onResolving(callable $onLoad, Request $request, Response $response, Route $route) : callable {
        // this should return function. You can return $onLoad callback to keep default behavior
        return $onLoad;
    }

    // execute after route loaded
    public function onResolved(Request $request, Response $response, Route $route) : void {
        // this hook don't return anything
        // you can still manipulate with $response
        if($response->body !== 'ok') $response->status(404);
    }

}

$map = new Map();

$map->route('GET', '/foo/bar', function ($request, $response, $route) {
    return $response->body('not ok');
})->middleware(new CustomMiddleware());

$map->group('/', function ($group) {
    $group->route('GET', '/foo/bar', function ($request, $response, $route) {
        return $response->body('ok');
    });
})->middleware(new CustomMiddleware());

$map->build();
$map->dispatch();

```

#### Multiple middleware

[](#multiple-middleware)

```
class Logger extends Middleware {

    public function onResolved($request, $response, $route) {
        print_r([ $request->path, $request->query, $route->name ]);
    }

}

class Auth extends Middleware {

    public function onResolving($onLoad, $request, $response) {
        // check if user logged in
        if(isset($_SESSION['userId'])) {
            return $onLoad; // return default route loader
        } else {
            return function () {
                // return 401 code or redirect to login page
            };
        }
    }

}

$map->group('/', function ($group) {
    ...
})->middleware(new Logger(), new Auth());

```

Object reference
----------------

[](#object-reference)

#### Request

[](#request)

```
new Subway\Request(string $method, string $url, array $post=[], array $headers=[], array $cookies=[], string | null $body=null, array $files=[])
    ->method : string // returns HTTP method
    ->url : string // returns full request URL
    ->origin : string // returns request origin (https://example.com)
    ->path : string // returns URI path (foo/bar)
    ->query : string // returns query string (foo=1&bar=2)
    ->params : array // returns POST params ([ 'foo' => 1, 'bar' => 2 ])
    ->param(string $paramName) : string // returns POST param by name
    ->segments : array // returns URI segments array ([ 'foo', 'bar' ])
    ->segment(int $segmentNumber) : string // returns segment by number (starts from 1)
    ->keys : array // return GET params ([ 'foo' => '1', 'bar' => '2' ])
    ->key(string $keyName) : string // returns GET property by name
    ->headers : array // returns HTTP headers array
    ->header(string $headerName) : string // returns HTTP headers by name
    ->cookies : array // returns cookies array
    ->cookie(string $cookieName) : string // returns cookie by cookie name
    ->body : string // returns request body string
    ->json(assoc=true) : array // returns array of decoded request body
    ->files : array // returns $_FILES array
    // static methods
    ::getCurrent() : Request // returns Request instance witch represents current request

```

#### Response

[](#response)

```
new Subway\Response()
    ->status : int // returns status code
    ->headers : array // returns headers array
    ->body : ?string // returns body or null
    ->json : ?array // returns content array or null
    ->status(int $statusCode) : $this // set status code
    ->header(string $name, string $value) : $this // set header
    ->body(string $data) : $this // set body content
    ->json(array $data) : $this // set body content as array
    ->redirect(string $url, int $statusCode=302) : void // redirects to given URL with given status code (default 302)

```

#### Route

[](#route)

```
Subway\Route
    ->method : string // http method
    ->name : string // name of route or empty string if not defined
    ->groups : array // array with names of groups which contain this route
    ->inGroup(string $name) : bool // check if this route in group with specific name
    ->estimate(Subway\Request $request) : int // estimate route (onEstimated() middleware hooks will be executed if exists)
    ->getUrl(array $props) : string // get URL of this route. Keys from 'props' parameter will be used to replace variable segments. Example: $map->route('/foo/{bar}')->getUrl([ 'bar' => 'something' ]) will return '/foo/something'
    ->resolve(Subway\Request $request) // load route (onResolving() and onResolved() middleware hooks will be executed if exists)

```

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance42

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~77 days

Total

5

Last Release

474d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d6502e436547d285a3651dfa885a0179d379ad84dde733e318a4480954308ad8?d=identicon)[Alcohol120](/maintainers/Alcohol120)

---

Top Contributors

[![Alcohol120](https://avatars.githubusercontent.com/u/21302441?v=4)](https://github.com/Alcohol120 "Alcohol120 (7 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/alclab-subway-router/health.svg)

```
[![Health](https://phpackages.com/badges/alclab-subway-router/health.svg)](https://phpackages.com/packages/alclab-subway-router)
```

###  Alternatives

[snicco/better-wp-hooks

1118.6k2](/packages/snicco-better-wp-hooks)[thalidzhokov/exchange-rates-cbrf

ExchangeRatesCBRF Class to get exchange rates of the Central Bank of Russia

102.7k](/packages/thalidzhokov-exchange-rates-cbrf)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
