nezamy/route

Route - 为 PHP 提供快速灵活的路由功能,使您能够快速轻松地构建 RESTful 网络应用程序。

v2.0-beta 2020-09-17 09:22 UTC

This package is auto-updated.

Last update: 2024-09-19 14:41:19 UTC


README

Route - 为 PHP 提供快速灵活的路由功能,使您能够快速轻松地构建 RESTful 网络应用程序。

安装

$ composer require nezamy/route

或者,如果您正在寻找使用此路由的现成模板,请访问 https://github.com/nezamy/just

Route 需要 PHP 7.4.0 或更高版本。

变更列表

  • 基于 php 7.4 重写路由
  • 支持 Swoole 扩展
  • 支持地区以构建多语言网站
  • 添加了 Auth、Basic、Digest
  • 可自定义路由解析器和处理器
  • 智能依赖注入和服务容器

用法

仅当使用 composer 时,在根目录中创建 index.php。

创建一个包含以下内容的 index.php 文件

<?php declare(strict_types=1);

define('DS', DIRECTORY_SEPARATOR);
define('BASE_PATH', __DIR__ . DS);
//Show errors
//===================================
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
//===================================
require BASE_PATH.'vendor/autoload.php';
$request = new Just\Http\GlobalRequest;
$response = new Just\Http\Response;
$route = new Just\Routing\Router($request, $response);
// let store them to container, to use them as a singleton
container()->set(Just\Http\Request::class, $request);
container()->set(Just\Http\Response::class, $response);
container()->set(Just\Routing\Router::class, $route);

try {
    include 'app/routes.php';
    $output = $route->run();

    foreach ($output->headers->all() as $k => $v) {
        header("$k: $v");
    }
    http_response_code($output->statusCode());
    if ($output->hasRedirect()) {
        list($url, $code) = $output->getRedirect();
        header("Location: $url", true, $code);
    }

} catch (\Error $e) {
    pre($e, 'Error', 6);
} catch (\Exception $e) {
    pre($e, 'Exception', 6);
}

echo response()->body();

app/routes.php

<?php
use Just\Route;

Route::get('/', function (){
    return 'Welcome to the home page';
});

// Maybe you want to customize 404 page
Route::setNotfound(function (){
    return 'Page Not found';
});

Swoole 一起使用

<?php
declare(strict_types=1);

use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;

use Just\Routing\Router;

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

require __DIR__ . '/vendor/autoload.php';

$http = new Server("0.0.0.0", 9501);
$http->set([
    'document_root' => '/var/www/public',
    'enable_static_handler' => true,
]);
$http->on("request", function (Request $request, Response $response) {

    $request = new Just\Http\Request(
       $request->header ?? [],
       $request->server ?? [],
       $request->cookie ?? [],
       $request->get ?? [],
       $request->post ?? [],
       $request->files ?? [],
       $request->tmpfiles ?? []
    );
    $response = new Just\Http\Response;
    $route = new Just\Routing\Router($request, $response);
	container()->set(Just\Http\Request::class, $request);
	container()->set(Just\Http\Response::class, $response);
	container()->set(Router::class, $route);
    try {
        include __DIR__ .'/app/routes.php';
        $output = $route->run();
        foreach ($output->headers->all() as $k => $v) {
            $response->header($k, $v);
        }

        $response->setStatusCode($output->statusCode());

        if ($output->hasRedirect()) {
            list($url, $code) = $output->getRedirect();
            $response->redirect($url, $code);
        }
    } catch (\Error $e) {
        pre($e, 'Error', 6);
    } catch (\Exception $e) {
        pre($e, 'Exception', 6);
    }
    $response->end(response()->body(true));
});
$http->start();

工作原理

通过将 URL 模式与回调函数匹配来执行路由。

app/routes.php

Route::any('/', function() {
    return 'Hello World';
});

Route::post('/contact-us', function(\Just\Http\Request $req) {
    pre($req->body, 'Request');
});

回调可以是任何可调用的对象。因此,您可以使用常规函数

function pages() {
    return 'Page Content';
}
Route::get('/', 'pages');

或类方法

class home
{
    public function pages() {
        return 'Home page Content';
    }
}
Route::get('/', [home::class, 'pages']);
// OR
Route::get('/', 'home@pages');

方法路由

Route::any('/', function() {});
Route::get('/', function() {});
Route::post('/', function() {});
Route::put('/', function() {});
Route::patch('/', function() {});
Route::option('/', function() {});
Route::delete('/', function() {});

参数

// This example will match any page name
Route::get('/{page}', function($page) {
    return "you are in $page";
});

Route::get('/post/{id}', function($id) {
    // Will match anything like post/hello or post/5 ...
    // But not match /post/5/title
    return "post id $id";
});

// more than parameters
Route::get('/post/{id}/{title}', function($id, $title) {
    return "post id $id and title $title";
});

// you can get parameter in any order
Route::get('/post/{id}/{title}', function($title, $id) {
    return "post id $id and title $title";
});

对于“无限”可选参数,您可以这样做

// This example will match anything after blog/ - unlimited arguments
Route::get('/blog/{any}:*', function($any) {
    pre($any);
});

正则表达式

您可以使用正则表达式验证参数。

// Validate args by regular expressions uses :(your pattern here)
Route::get('/{username}:([0-9a-z_.-]+)/post/{id}:([0-9]+)',
function($username, $id) {
    return "author $username post id $id";
});

// You can add named regex pattern in routes
Route::addPlaceholders([
    'username' => '([0-9a-z_.-]+)',
    'id' => '([0-9]+)'
]);

// Now you can use named regex
Route::get('/{username}:username/post/{id}:id', function($username, $id) {
    return "author $username post id $id";
});
//if the parameter name match the placeholder name just ignore placeholder and route will deduct that
Route::get('/{username}/post/{id}', function($username, $id) {
    return "author $username post id $id";
});

一些已注册在路由中的命名正则表达式模式

[
    'int'               => '/([0-9]+)',
    'multiInt'          => '/([0-9,]+)',
    'title'             => '/([a-z_-]+)',
    'key'               => '/([a-z0-9_]+)',
    'multiKey'          => '/([a-z0-9_,]+)',
    'isoCode2'          => '/([a-z]{2})',
    'isoCode3'          => '/([a-z]{3})',
    'multiIsoCode2'     => '/([a-z,]{2,})',
    'multiIsoCode3'     => '/([a-z,]{3,})'
];

可选参数

您可以通过添加 (?) 来指定可选的命名参数以进行匹配

Route::get('/post/{title}?:title/{date}?',
function($title, $date) {
    $content = '';
    if ($title) {
        $content = "<h1>$title</h1>";
    }else{
        $content =  "<h1>Posts List</h1>";
    }

    if ($date) {
        $content .= "<small>Published $date</small>";
    }
    return $content;

});

分组

Route::group('/admin', function()
{
    // /admin/
    Route::get('/', function() {});
    // /admin/settings
    Route::get('/settings', function() {});
    // nested group
    Route::group('/users', function()
    {
        // /admin/users
        Route::get('/', function() {});
        // /admin/users/add
        Route::get('/add', function() {});
    });
    // Anything else
    Route::any('/{any}:*', function($any) {
        pre("Page ( $any ) Not Found", 6);
    });
});

带参数的分组

Route::group('/{module}', function($lang)
{
    Route::post('/create', function() {});
    Route::put('/update', function() {});
});

地区

// the first language is the default i.e. ar
// when you hit the site http://localhost on the first time will redirect to  http://localhost/ar
Route::locale(['ar','en'], function(){
    // will be /ar/
    Route::get('/', function($locale){
        //get current language
        pre($locale);
    });
    // /ar/contact
    Route::get('/contact', function() {});

    Route::group('/blog', function() {
        // /ar/blog/
        Route::get('/', function() {});
    });
});
// Also you can write locales like that or whatever you want
Route::locale(['ar-eg','en-us'], function(){
    // will be /ar/
    Route::get('/', function($locale){
        //get current language
        list($lang, $country) = explode('-', $locale, 2);
        pre("Lang is $lang, Country is $country");
    });
});

认证

基本认证

$auth = new \Just\Http\Auth\Basic(['users' => [
    'user1' => '123456',
    'user2' => '987654'
]]);
Route::auth($auth, function (){
    Route::get('/secret', function(\Just\Http\Request $req){
        pre("Hello {$req->user()->get('username')}, this is a secret page");
    });
});

摘要认证

$auth = new \Just\Http\Auth\Digest(['users' => [
    'user1' => '123456',
    'user2' => '987654'
]]);
Route::auth($auth, function (){
    Route::get('/secret', function(\Just\Http\Request $req){
        pre("Hello {$req->user()->get('username')}, this is a secret page");
    });
});

中间件

全局

Route::use(function (\Just\Http\Request $req, $next){
    //validate something the call next to continue or return whatever if you want break 
    if($req->isMobile()){
        return 'Please open from a desktop';
    }
    
    return $next();
}, function ($next){
    // another middleware
    $next();
});

// After 
Route::use(function ($next){
    $response =  $next();
    // make some action
    return $response;
});

分组上的中间件

// if open from mobile device
Route::middleware(fn(\Just\Http\Request $req, $next) => !$req->isMobile() ? '' : $next())
    ->group('/mobile-only', function (){
        Route::get('/', function(\Just\Http\Request $req){
            pre($req->browser());
        });
    });

如果您将中间件作为类实现,您可以使用命名空间传递该类。该类应具有 handle 方法。

class MobileOnly{
    public function handle(\Just\Http\Request $req, $next){
        return !$req->isMobile() ? '' : $next();
    }
}
Route::middleware(MobileOnly::class)
    ->group('/',function (){
        Route::get('/', function(\Just\Http\Request $req){
            pre($req->browser());
        });
    });

路由上的中间件

Route::get('/', function(\Just\Http\Request $req){
    pre($req->browser());
})->middleware(MobileOnly::class);

依赖注入

有关依赖注入和服务容器的信息,请访问此 链接

处理和解析器自定义

CustomRouteHandler 示例

class CustomRouteHandler implements Just\Routing\RouteHandlerInterface
{
    public function call(callable $handler, array $args = [])
    {
        return call_user_func_array($handler, $args);
    }

    public function parse($handler): callable
    {
        if (is_string($handler) && ! function_exists($handler)) {
            $handler = explode('@', $handler, 2);
        }
        return $handler;
    }
}
\Just\Route::setHandler(new CustomRouteHandler);
class CustomRouteParser implements RouteParserInterface
{
    public function parse(string $uri): array
    {
        $matchedParameter = [];
        $matchedPattern = [];
        $result = [];
        // parse uri here and return array of 3 elements
        // /{page}
        // /{page}?

        return ['parameters' => $matchedParameter, 'patterns' => $matchedPattern, 'result' => $result];
    }
}
\Just\Route::setParser(new CustomRouteParser);