PHPackages                             inhere/sroute - 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. [Framework](/categories/framework)
4. /
5. inhere/sroute

ActiveLibrary[Framework](/categories/framework)

inhere/sroute
=============

a very lightweight and fasted request router. lightweight web framework

v3.1.0(1y ago)732.8k14[4 issues](https://github.com/inhere/php-sroute/issues)MITPHPPHP &gt;=8.1CI passing

Since May 26Pushed 2mo ago4 watchersCompare

[ Source](https://github.com/inhere/php-sroute)[ Packagist](https://packagist.org/packages/inhere/sroute)[ Docs](https://github.com/inhere/php-sroute)[ RSS](/packages/inhere-sroute/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (10)DependenciesVersions (46)Used By (0)

SRoute
======

[](#sroute)

[![License](https://camo.githubusercontent.com/4b09194d7d85c0d22296265013e2bb3df4f9c48696a3c56e9b8c2c072ecb4acc/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f696e686572652f73726f7574652e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/b6819470900d34d0acc0a879ff23b478f4b763294dfbf4f15abf63e0d6751d24/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f696e686572652f73726f7574652e7376673f636f6c6f72423d677265656e)](https://packagist.org/packages/inhere/sroute)[![Latest Stable Version](https://camo.githubusercontent.com/80b9ccf0261ec3683095d342bb5a7c5246a45d9fb82aaf47702fdb568ac6dbd3/687474703a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f696e686572652f73726f7574652e737667)](https://packagist.org/packages/inhere/sroute)[![Unit-tests](https://github.com/inhere/php-sroute/actions/workflows/php.yml/badge.svg)](https://github.com/inhere/php-sroute/actions)[![Coverage Status](https://camo.githubusercontent.com/5d5533b79caef474b697db666ff88402a3da239e5c47cac42240b5e40eebb9a5/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f696e686572652f7068702d73726f7574652f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/inhere/php-sroute?branch=master)

非常快速且轻量的请求匹配路由器, web 路由框架。

- 无依赖、简洁、速度快、功能完善
- 轻量级且速度快，查找速度不受路由数量的影响
- 支持路由组, 支持路由参数定义，以及丰富的自定义路由选项
- 支持给指定的路由命名，可根据名称拿到注册的路由对象
- 支持请求方法: `GET` `POST` `PUT` `DELETE` `HEAD` `OPTIONS` ...
- 支持自动匹配路由到控制器就像 Yii 一样, 请参看配置项 `autoRoute` (不推荐)
- 压测对比数据请看[路由测试](#ab-test)

**多个版本：**

> 不同的版本有稍微的区别以适应不同的场景

- `Router` 通用版本，也是后几个版本的基础类，适用于所有的情况。
- `SRouter` 静态类版本。`Router` 的简单包装，通过静态方法使用(方便小应用快速使用)
- `CachedRouter` 继承自`Router`，支持路由缓存的版本，可以 **缓存路由信息到文件**
    - 适合php-fpm 环境使用(有缓存将会省去每次的路由收集和解析消耗)
- `PreMatchRouter` 继承自`Router`，预匹配路由器。**当应用的静态路由较多时，将拥有更快的匹配速度**
    - 适合php-fpm 环境，php-fpm 情形下，实际上我们在收集路由之前，已经知道了路由path和请求动作METHOD
- `ServerRouter` 继承自`Router`，服务器路由。内置支持**动态路由临时缓存**. 适合 `swoole` 等**常驻内存应用**使用
    - 最近请求过的动态路由将会缓存为一个静态路由信息，下次相同路由将会直接匹配命中

**内置调度器：**

- 支持事件: `found` `notFound` `execStart` `execEnd` `execError`. 当触发事件时你可以做一些事情(比如记录日志等)
- 支持动态获取`action`名。支持设置方法执行器(`actionExecutor`)，通过方法执行器来自定义调用真实请求方法.
- 支持通过方法 `$router->dispatch($path, $method)` 手动调度一个路由
- 你即使不配置任何东西, 它也能很好的工作

**路由器管理**

`RouterManager` 当需要在一个项目里处理多个域名下的请求时，方便的根据不同域名配置多个路由器

**[EN README](README_en.md)**

项目地址
----

[](#项目地址)

- **github**
- **gitee**

安装
--

[](#安装)

> required PHP 8.0+

- composer 命令

```
composer require inhere/sroute
```

- composer.json

```
{
    "require": {
        "inhere/sroute": "dev-master"
    }
}
```

- 直接拉取

```
git clone https://github.com/inhere/php-sroute.git // github
```

压测
--

[](#压测)

自动生成了1000条路由，每条有9个参数位，分别测试1000次的

- 第一条路由匹配
- 最后一条路由匹配
- 不存在的路由匹配

详细的测试代码请看仓库

- 压测日期 **2018.11.19**
- An example route: `/9b37eef21e/{arg1}/{arg2}/{arg3}/{arg4}/{arg5}/{arg6}/{arg7}/{arg8}/{arg9}/bda37e9f9b`

Worst-case matching
-------------------

[](#worst-case-matching)

This benchmark matches the last route and unknown route. It generates a randomly prefixed and suffixed route in an attempt to thwart any optimization. 1,000 routes each with 9 arguments.

This benchmark consists of 14 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded.

Test NameResultsTime(ms)+ IntervalChange**inhere/sroute(Router)** - unknown route(1000 routes)9900.002031+0.00087175% slowerinhere/sroute(SRouter) - unknown route(1000 routes)9940.002895+0.001736150% slower**inhere/sroute(Router)** - last route(1000 routes)9970.005300+0.004141357% slowerinhere/sroute(SRouter) - last route(1000 routes)9970.006467+0.005308458% slowersymfony/routing(cached) - unknown route(1000 routes)9760.012777+0.0116181002% slowersymfony/routing(cached) - last route(1000 routes)9960.013608+0.0124491074% slowermindplay/timber - last route(1000 routes)9980.017211+0.0160521385% slowerFastRoute - unknown route(1000 routes)9910.039429+0.0382703302% slowerFastRoute(cached) - unknown route(1000 routes)9900.040800+0.0396413420% slowerFastRoute(cached) - last route(1000 routes)9990.045065+0.0439063788% slowerFastRoute - last route(1000 routes)9990.064694+0.0635355481% slowerPux PHP - unknown route(1000 routes)9780.316016+0.31485727163% slowersymfony/routing - unknown route(1000 routes)9920.359482+0.35832330912% slowersymfony/routing - last route(1000 routes)9990.418813+0.41765436031% slowerPux PHP - last route(1000 routes)9990.440489+0.43933037901% slowerMacaw - unknown route(1000 routes)9911.687441+1.686282145475% slowerMacaw - last route(1000 routes)9991.786542+1.785383154024% slowerFirst route matching
--------------------

[](#first-route-matching)

This benchmark tests how quickly each router can match the first route. 1,000 routes each with 9 arguments.

This benchmark consists of 7 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded.

Test NameResultsTime+ IntervalChangenikic/fast-route - first route(1000)9980.002929+0.001571116% slowercorneltek/pux(php) - first route(1000)9960.002971+0.001613119% slowerinhere/sroute(Router) - first(1000)9790.006202+0.004844357% slowerinhere/sroute(SRouter) - first(1000)9990.006627+0.005269388% slowersymfony/routing(cached) - first route(1000)9850.006858+0.005501405% slowersymfony/routing - first route(1000)9950.023105+0.0217471601% slowernikic/fast-route(cached) - first route(1000)9990.041133+0.0397752929% slowerMacaw - first route (1000 routes)9991.782017+1.780659131128% slower使用说明
----

[](#使用说明)

> 各个版本的方法名和参数都是一样的

首先, 需要导入类

```
use Inhere\Route\Router;

$router = new Router();
```

### 快速开始

[](#快速开始)

创建一个简单的 `public/index.php` 文件:

```
use Inhere\Route\Router;

// 需要先加载 autoload 文件
require dirname(__DIR__) . '/vendor/autoload.php';

$router = new Router();

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

// 开始调度运行
$router->dispatch();
```

使用php启动一个测试server： `php -S 127.0.0.1:8080 -t ./public`

好了，现在你可以访问  可以看到输出 `hello`

- 不使用 Composer

如果是直接下载的包代码，可以加载 `test/boot.php` 文件，也可以加载到 `Inhere\Route` 命名空间.

用如下的语句替换上面的 `autoload.php` 加载语句即可：

```
require dirname(__DIR__) . '/test/boot.php';
```

添加路由
----

[](#添加路由)

```
// 匹配 GET 请求. 处理器是个闭包 Closure
$router->get('/', function() {
    echo 'hello';
});

// 匹配参数 'test/john'
$router->get('/test/{name}', function($params) {
    echo $params['name']; // 'john'
}, [
      'name' => '\w+', // 添加参数匹配限制。若不添加对应的限制，将会自动设置为匹配除了'/'外的任何字符
]);

// 可选参数支持。匹配  'hello' 'hello/john'
$router->get('/hello[/{name}]', function() {
    echo $params['name'] ?? 'No input'; // 'john'
}, [
     'name' => '\w+', // 添加参数匹配限制
]);

// 匹配 POST 请求
$router->post('/user/login', function() {
    var_dump($_POST);
});

// 匹配 GET 或者 POST
$router->map(['get', 'post'], '/user/login', function() {
    var_dump($_GET, $_POST);
});

// 允许任何请求方法
$router->any('/home', function() {
    echo 'hello, you request page is /home';
});
$router->any('/404', function() {
    echo "Sorry,This page not found.";
});
```

### 使用路由组

[](#使用路由组)

```
// 路由组
$router->group('/user', function ($router) {
    $router->get('/', function () {
        echo 'hello. you access: /user/';
    });
    $router->get('/index', function () {
        echo 'hello. you access: /user/index';
    });
});
```

### 使用控制器

[](#使用控制器)

```
// 使用 控制器
$router->get('/', App\Controllers\HomeController::class);
$router->get('/index', 'App\Controllers\HomeController@index');
```

### 备用路由处理

[](#备用路由处理)

可以注册一个备用路由处理。当没匹配到时，就会使用它

```
$router->any('*', 'fallback_handler');
```

> 如果配置了 `'ignoreLastSlash' => true`, '/index' 等同于 '/index/'

#### 注意

[](#注意)

可选参数 - 只能是在路由path的最后

正确的：

```
/hello[/{name}]      // match: /hello/tom   /hello
/my[/{name}[/{age}]] // match: /my/tom/78  /my/tom
```

错误的：

```
/my[/{name}]/{age}
```

### 自动匹配路由

[](#自动匹配路由)

支持根据请求的URI自动匹配路由(就像 yii 一样), 需配置 `autoRoute`.

```
    'autoRoute' => 1, // 启用
    'controllerNamespace' => 'App\\Controllers', // 控制器类所在命名空间
    'controllerSuffix' => 'Controller', // 控制器类后缀
```

> 请参看示例 `example` 中的使用

此时请求没有配置路由的 `/demo` `/demo/test`。将会自动尝试从 `App\\Controllers` 命名空间下去查找 `DemoController`

查找逻辑是

- 只有一节的(如`/demo`)，直接定义它为控制器类名进行查找
- 大于等于两节的默认先认为最后一节是控制器类名，进行查找
- 若失败，再尝试将倒数第二节认为是控制器名，最后一节是action名

设置路由配置
------

[](#设置路由配置)

```
// set config
$router->config([
    'ignoreLastSlash' => true,
    'autoRoute' => 1,
    'controllerNamespace' => 'app\\controllers',
    'controllerSuffix' => 'Controller',
]);
```

> NOTICE: 必须在添加路由之前调用 `$router->config()`

路由匹配
----

[](#路由匹配)

```
array public function match($path, $method)
```

- `$path` string 请求的URI path
- `$method` string 请求的request method
- 返回 `array` 返回匹配结果信息

### 示例

[](#示例)

根据请求的 URI path 和 请求 METHOD 查找匹配我们定义的路由信息。

```
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];

$routeInfo = $router->match($path, $method);
```

根据返回的路由信息，我们就可以自由的决定如何调用对应的处理。

> 关于返回的数据结构，可以查看 [关键方法参考](docs/classes-api.md)

路由调度
----

[](#路由调度)

如果你不想自己实现路由调度，可以使用内置的路由调度器 `Inhere\Route\Dispatcher\Dispatcher`

```
use Inhere\Route\Dispatcher\Dispatcher;

$dispatcher = new Dispatcher([
    // default action method name
    'defaultAction' => 'index',

    'actionPrefix' => '',

    'actionSuffix' => 'Action',

    'dynamicAction' => true,
    // @see Router::$globalParams['act']
    'dynamicActionVar' => 'act',
]);
```

### 设置事件处理

[](#设置事件处理)

```
// 成功匹配路由
$dispatcher->on(Dispatcher::ON_FOUND, function ($uri, $cb) use ($app) {
    $app->logger->debug("Matched uri path: $uri, setting callback is: " . is_string($cb) ? $cb : get_class($cb));
});

// 当匹配失败, 重定向到 '/404'
$dispatcher->on('notFound', '/404');
// 或者, 当匹配失败, 输出消息...
$dispatcher->on('notFound', function ($uri) {
    echo "the page $uri not found!";
});
```

### 使用控制器方法

[](#使用控制器方法)

通过`@`符号连接控制器类和方法名可以指定执行方法。

```
$router->get('/', App\Controllers\HomeController::class);

$router->get('/index', 'App\Controllers\HomeController@index');
$router->get('/about', 'App\Controllers\HomeController@about');
```

> NOTICE: 若第二个参数仅仅是个 类，将会尝试执行通过 `defaultAction` 配置的默认方法

### 动态匹配控制器方法

[](#动态匹配控制器方法)

动态匹配控制器方法, 需配置

```
'dynamicAction' => true,  // 启用
// action 方法名匹配参数名称，符合条件的才会当做action名称
// @see Router::$globalParams['act'] 匹配 '[a-zA-Z][\w-]+'
'dynamicActionVar' => 'act',
```

> NOTICE: 使用动态匹配控制器方法, 应当使用 `any()` 添加路由. 即此时不能限定请求方法 `REQUEST_METHOD`

```
// 访问 '/home/test' 将会执行 'App\Controllers\HomeController::test()'
$router->any('/home/{act}', App\Controllers\HomeController::class);

// 可匹配 '/home', '/home/test' 等
$router->any('/home[/{act}]', App\Controllers\HomeController::class);
```

> NOTICE: 上面两个的区别是 第一个无法匹配 `/home`

### 使用方法执行器

[](#使用方法执行器)

配置 `actionExecutor` 为你需要的方法名，例如配置为 `'actionExecutor' => 'run'`，那所有的方法请求都会提交给此方法。 会将真实的 action 作为参数传入`run($action)`, 需要你在此方法中调度来执行真正的请求方法。

> NOTICE: 在你需要将路由器整合到自己的框架时很有用

示例：

```
// 访问 '/user', 将会调用 App\Controllers\UserController::run('')
$router->get('/user', 'App\Controllers\UserController');

// 访问 '/user/profile', 将会调用 App\Controllers\UserController::run('profile')
$router->get('/user/profile', 'App\Controllers\UserController');

// 同时配置 'actionExecutor' => 'run' 和 'dynamicAction' => true,
// 访问 '/user', 将会调用 App\Controllers\UserController::run('')
// 访问 '/user/profile', 将会调用 App\Controllers\UserController::run('profile')
$router->any('/user[/{name}]', 'App\Controllers\UserController');
```

开始路由匹配和调度
---------

[](#开始路由匹配和调度)

```
$router->dispatch($dispatcher);
```

运行示例
----

[](#运行示例)

示例代码在 `example` 下。

- 对象版本

你可以通过 `php -S 127.0.0.1:5670 example/object.php` 来运行一个测试服务器, 现在你可以访问

测试
--

[](#测试)

```
phpunit
```

- simple benchmark

```
php example/benchmark.php
```

License
-------

[](#license)

MIT

我的其他项目
------

[](#我的其他项目)

### `inhere/console` [github](https://github.com/inhere/php-console) [git@osc](https://git.oschina.net/inhere/php-console)

[](#inhereconsole-github-gitosc)

功能丰富的命令行应用，命令行工具库

### `inhere/php-validate` [github](https://github.com/inhere/php-validate) [git@osc](https://git.oschina.net/inhere/php-validate)

[](#inherephp-validate-github--gitosc)

一个简洁小巧且功能完善的php验证库。仅有几个文件，无依赖。

###  Health Score

54

—

FairBetter than 97% of packages

Maintenance67

Regular maintenance activity

Popularity33

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity85

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 97.5% 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 ~67 days

Recently: every ~567 days

Total

44

Last Release

430d ago

Major Versions

v0.5.3 → v1.0.12017-07-16

v1.8.1 → v2.0.02018-09-03

v2.x-dev → v3.0.02018-11-19

PHP version history (6 changes)v0.5.1PHP &gt;=5.5.0

v0.5.3PHP &gt;=5.6.0

v1.7.0PHP &gt;=7.0

v3.0.0PHP &gt;7.0

v3.0.2PHP &gt;7.1.0

v3.1.0PHP &gt;=8.1

### Community

Maintainers

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

---

Top Contributors

[![inhere](https://avatars.githubusercontent.com/u/5302062?v=4)](https://github.com/inhere "inhere (233 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (6 commits)")

---

Tags

dispatchmatchallphpphp-routerrouterouterroutingweb-routerrouterlibraryroutehttp-routerphp-routerweb framework

### Embed Badge

![Health badge](/badges/inhere-sroute/health.svg)

```
[![Health](https://phpackages.com/badges/inhere-sroute/health.svg)](https://phpackages.com/packages/inhere-sroute)
```

###  Alternatives

[pecee/simple-router

Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.

675224.9k18](/packages/pecee-simple-router)[developermarius/simple-router

Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.

112.5k](/packages/developermarius-simple-router)

PHPackages © 2026

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