alclab / subway-router
php 路由器
1.3.0
2024-04-09 16:56 UTC
Requires
- php: ^8.3
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2024-09-09 18:01:51 UTC
README
SubwayPHP - php 路由器
用法
基本示例
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
安装
composer require alclab/subway-router
路由
创建路由映射
use Subway\Map;
$map = new Map();
静态路径
$map->route('GET', '/foo/bar', function($request, $route) {});
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)
可选部分
$map->route('GET', '/foo?/{bar}') // first segment is optional
分组
$map->group('/foo', function ($group) {
$group->route('GET', '/', function ($req) {}); // GET /foo
$group->route('GET', '/bar', function ($req) {}); // GET /foo/bar
});
准备和分发路由
构建
在分发之前,您必须编译路由器。
$map->build(); // now router is ready to use
当您的路由设置有任何更改时,您应该重新构建路由器。
分发
// 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);
路由估计和优先级
静态部分具有最高优先级。
模式部分具有中等优先级。
变量(任何)部分具有最低优先级。
$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
可选部分比必需部分的优先级低
$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
中间件钩子
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();
多个中间件
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());
对象引用
请求
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 ])
->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() : array // returns array of decoded request body
->files : array // returns $_FILES array
// static methods
::getCurrent() : Request // returns Request instance witch represents current request
响应
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)
路由
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 (onResolving() 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)