michaelthedev/simple-router

Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router. (Forked from skipperbent/simple-php-router)

v0.1 2024-06-26 09:06 UTC

This package is auto-updated.

Last update: 2024-09-07 16:26:55 UTC


README

Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind.

With simple-router you can create a new project fast, without depending on a framework.

只需要几行代码即可开始使用

SimpleRouter::get('/', function() {
    return 'Hello world';
});

支持项目

If you like simple-router and wish to see the continued development and maintenance of the project, please consider showing your support by buying me a coffee. Supporters will be listed under the credits section of this documentation.

You can donate any amount of your choice by clicking here.

目录

入门

使用此命令添加simple-router项目的最新版本。

composer require pecee/simple-router

注意

本项目的目标是创建一个与Laravel文档基本100%兼容的路由器,同时尽可能地简单,易于集成和修改,不牺牲速度或复杂性。轻量级是首要任务。

我们包含了一个简单的路由器示例项目,可在以下位置找到:这里。这个项目应该能帮助你了解如何设置和使用simple-php-router项目。

请注意,示例项目仅涵盖如何在没有现有框架的项目中集成simple-php-router。如果你在项目中使用框架,实现方式可能会有所不同。

你可以在这里找到示例项目:https://github.com/skipperbent/simple-router-demo

我们不涵盖的内容

  • 如何设置满足您需求解决方案。这是一个基本的演示,以帮助您开始。
  • 对MVC的理解,包括控制器、中间件或异常处理器。
  • 如何集成到第三方框架中。

我们涵盖的内容

  • 如何快速启动 - 从零开始。
  • 如何让异常处理器、中间件和控制器正常工作。
  • 如何设置您的Web服务器。

要求

  • PHP 7.1或更高版本(版本3.x及以下支持PHP 5.5+)
  • PHP JSON扩展已启用。

特性

  • 基本路由(GETPOSTPUTPATCHUPDATEDELETE)支持自定义多个动词。
  • 正则表达式参数约束。
  • 命名路由。
  • 生成路由的URL。
  • 路由分组。
  • 中间件(拦截路由渲染前的类)。
  • 命名空间。
  • 路由前缀。
  • CSRF保护。
  • 可选参数
  • 子域名路由。
  • 自定义引导管理器以重写“更美观”的URL。
  • 输入管理器;轻松管理GETPOSTFILE值。
  • 基于IP的限制。
  • 易于扩展。

安装

  1. 在终端导航到您的项目文件夹,并运行以下命令
composer require pecee/simple-router

设置Nginx

如果您使用Nginx,请确保已启用URL重写。

您可以通过为示例项目的Nginx配置文件添加以下配置轻松启用URL重写。

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

设置Apache

Apache不需要特殊设置。我们已在public文件夹中包含了.htaccess文件。如果重写不起作用,请检查Apache配置中是否启用了mod_rewrite模块(htaccess支持)。

.htaccess示例

以下是simple-php-router使用的正在工作的.htaccess文件的一个示例。

只需在您的项目public目录中创建一个新的.htaccess文件,并将以下内容粘贴到新创建的文件中。这将重定向所有请求到您的index.php文件(见配置部分)。

RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1

设置IIS

在IIS中,您必须在public文件夹中的web.config文件中添加一些行或创建一个新的文件。如果重写不起作用,请检查您的IIS版本是否包含url rewrite模块,或者从Microsoft网站下载并安装它们。

web.config示例

以下是simple-php-router使用的正在工作的web.config文件的一个示例。

只需在您的项目public目录中创建一个新的web.config文件,并将以下内容粘贴到新创建的文件中。这将重定向所有请求到您的index.php文件(见配置部分)。如果web.config文件已经存在,请在<system.webServer>分支内部添加<rewrite>部分。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
	<rewrite>
	  <rules>
		<!-- Remove slash '/' from the en of the url -->
		<rule name="RewriteRequestsToPublic">
		  <match url="^(.*)$" />
		  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
		  </conditions>
		  <action type="Rewrite" url="/{R:0}" />
		</rule>

		<!-- When requested file or folder don't exists, will request again through index.php -->
		<rule name="Imported Rule 1" stopProcessing="true">
		  <match url="^(.*)$" ignoreCase="true" />
		  <conditions logicalGrouping="MatchAll">
			<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
			<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
		  </conditions>
		  <action type="Rewrite" url="/index.php/{R:1}" appendQueryString="true" />
		</rule>
	  </rules>
	</rewrite>
    </system.webServer>
</configuration>

故障排除

如果您在项目中没有favicon.ico文件,您可能会得到NotFoundHttpException(404 - 未找到)。

要将favicon.ico添加到IIS忽略列表,请将以下行添加到<conditions>

<add input="{REQUEST_FILENAME}" negate="true" pattern="favicon.ico" ignoreCase="true" />

您还可以为一些扩展名的文件设置一个例外

<add input="{REQUEST_FILENAME}" pattern="\.ico|\.png|\.css|\.jpg" negate="true" ignoreCase="true" />

如果您正在使用 $_SERVER['ORIG_PATH_INFO'],您将得到返回值的一部分为 \index.php\

示例

/index.php/test/mypage.php

配置

创建一个新文件,命名为 routes.php 并将其放置在您的库文件夹中。这将是一个定义您项目所有路由的文件。

警告:绝对不要将您的 routes.php 放在公共文件夹中!

在您的 index.php 中引入您新创建的 routes.php 并调用 SimpleRouter::start() 方法。这将触发并执行实际的请求路由。

这不是必须的,但您可以设置 SimpleRouter::setDefaultNamespace('\Demo\Controllers'); 以将命名空间作为前缀添加到所有路由。这将使事情稍微简单一些,因为您不需要在每条路由上指定控制器命名空间。

以下是一个基本的 index.php 文件示例

<?php
use Pecee\SimpleRouter\SimpleRouter;

/* Load external routes file */
require_once 'routes.php';

/**
 * The default namespace for route-callbacks, so we don't have to specify it each time.
 * Can be overwritten by using the namespace config option on your routes.
 */

SimpleRouter::setDefaultNamespace('\Demo\Controllers');

// Start the routing
SimpleRouter::start();

辅助函数

我们建议您将这些辅助函数添加到您的项目中。这将使您能够更轻松地访问路由器的功能。

要实现下面的函数,只需将代码复制到一个新文件中,在初始化路由器之前引入该文件,或者复制我们包含在这个库中的 helpers.php

use Pecee\SimpleRouter\SimpleRouter as Router;
use Pecee\Http\Url;
use Pecee\Http\Response;
use Pecee\Http\Request;

/**
 * Get url for a route by using either name/alias, class or method name.
 *
 * The name parameter supports the following values:
 * - Route name
 * - Controller/resource name (with or without method)
 * - Controller class name
 *
 * When searching for controller/resource by name, you can use this syntax "route.name@method".
 * You can also use the same syntax when searching for a specific controller-class "MyController@home".
 * If no arguments is specified, it will return the url for the current loaded route.
 *
 * @param string|null $name
 * @param string|array|null $parameters
 * @param array|null $getParams
 * @return \Pecee\Http\Url
 * @throws \InvalidArgumentException
 */
function url(?string $name = null, $parameters = null, ?array $getParams = null): Url
{
    return Router::getUrl($name, $parameters, $getParams);
}

/**
 * @return \Pecee\Http\Response
 */
function response(): Response
{
    return Router::response();
}

/**
 * @return \Pecee\Http\Request
 */
function request(): Request
{
    return Router::request();
}

/**
 * Get input class
 * @param string|null $index Parameter index name
 * @param string|mixed|null $defaultValue Default return value
 * @param array ...$methods Default methods
 * @return \Pecee\Http\Input\InputHandler|array|string|null
 */
function input($index = null, $defaultValue = null, ...$methods)
{
    if ($index !== null) {
        return request()->getInputHandler()->value($index, $defaultValue, ...$methods);
    }

    return request()->getInputHandler();
}

/**
 * @param string $url
 * @param int|null $code
 */
function redirect(string $url, ?int $code = null): void
{
    if ($code !== null) {
        response()->httpCode($code);
    }

    response()->redirect($url);
}

/**
 * Get current csrf-token
 * @return string|null
 */
function csrf_token(): ?string
{
    $baseVerifier = Router::router()->getCsrfVerifier();
    if ($baseVerifier !== null) {
        return $baseVerifier->getTokenProvider()->getToken();
    }

    return null;
}

路由

记得您在 index.php 中引入的 routes.php 文件?这个文件将用于放置您所有的自定义路由规则。

基本路由

以下是一个设置路由的非常基本的示例。第一个参数是路由应匹配的 URL - 下一个参数是当路由匹配时将被触发的 Closure 或回调函数。

SimpleRouter::get('/', function() {
    return 'Hello world';
});

类提示

您可以使用类提示来加载类和方法,如下所示

SimpleRouter::get('/', [MyClass::class, 'myMethod']);

可用方法

在这里,您可以看到所有可用的路由列表

SimpleRouter::get($url, $callback, $settings);
SimpleRouter::post($url, $callback, $settings);
SimpleRouter::put($url, $callback, $settings);
SimpleRouter::patch($url, $callback, $settings);
SimpleRouter::delete($url, $callback, $settings);
SimpleRouter::options($url, $callback, $settings);

多个HTTP动词

有时您可能需要创建一个接受多个 HTTP-verbs 的路由。如果您需要匹配所有 HTTP-verbs,可以使用 any 方法。

SimpleRouter::match(['get', 'post'], '/', function() {
    // ...
});

SimpleRouter::any('foo', function() {
    // ...
});

我们创建了一个简单的方法,用于匹配最常用的 GETPOST

SimpleRouter::form('foo', function() {
    // ...
});

路由参数

必需参数

现在您可能想知道如何从 URL 中解析参数。例如,您可能希望从 URL 中捕获用户的 id。您可以通过定义路由参数来实现这一点。

SimpleRouter::get('/user/{id}', function ($userId) {
    return 'User with id: ' . $userId;
});

您可以为您的路由定义任意多的路由参数

SimpleRouter::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) {
    // ...
});

注意:路由参数始终用 { } 大括号括起来,并且应仅包含字母字符。路由参数只能包含某些字符,如 A-Za-z0-9-_。如果您的路由包含其他字符,请参阅 自定义正则表达式以匹配参数

可选参数

有时您可能需要指定一个路由参数,但使该参数的存在是可选的。您可以通过在参数名称后放置一个 ? 标记来做到这一点。请确保为路由的相应变量提供一个默认值

SimpleRouter::get('/user/{name?}', function ($name = null) {
    return $name;
});

SimpleRouter::get('/user/{name?}', function ($name = 'Simon') {
    return $name;
});

在参数中包含斜杠

如果您正在使用 WebDAV 服务,URL 可能是文件和文件夹之间的区别。

例如,/path 将被视为文件 - 而且相反,/path/ 将被视为文件夹。

根据路径,路由器可以根据需要为路由的最后参数添加结尾的斜杠。因此,如果请求 /path/,参数将包含 path/ 的值,反之亦然。

为了确保与旧版本的兼容性,此功能默认是禁用的,并且必须通过设置 setSettings(['includeSlash' => true]) 或使用设置 setSlashParameterEnabled(true) 为您的路由启用。

示例

SimpleRouter::get('/path/{fileOrFolder}', function ($fileOrFolder) {
	return $fileOrFolder;
})->setSettings(['includeSlash' => true]);
  • 请求 /path/file 将返回 $fileOrFolder 值:file
  • 请求 /path/folder/ 将返回 $fileOrFolder 值:folder/

正则表达式约束

您可以使用路由实例上的 where 方法来限制路由参数的格式。where 方法接受参数名称和一个正则表达式,以定义如何限制参数

SimpleRouter::get('/user/{name}', function ($name) {
    
    // ... do stuff
    
})->where([ 'name' => '[A-Za-z]+' ]);

SimpleRouter::get('/user/{id}', function ($id) {
    
    // ... do stuff
    
})->where([ 'id' => '[0-9]+' ]);

SimpleRouter::get('/user/{id}/{name}', function ($id, $name) {
    
    // ... do stuff
    
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

正则表达式路由匹配

如果您想,还可以为整个路由定义正则表达式匹配。

如果您例如正在创建一个加载 AJAX URL 的模型框,这很有用。

以下示例使用以下正则表达式: /ajax/([\w]+)/?([0-9]+)?/?,它基本上只匹配 /ajax/ 并期望下一个参数是一个字符串 - 下一个是一个数字(但可选)。

匹配: /ajax/abc//ajax/abc/123/

不会匹配: /ajax/

正则表达式中的指定匹配组将被作为参数传递

SimpleRouter::all('/ajax/abc/123', function($param1, $param2) {
	// param1 = abc
	// param2 = 123
})->setMatch('/\/ajax\/([\w]+)\/?([0-9]+)?\/?/is');

自定义正则表达式匹配参数

默认情况下,simple-php-router 使用 [\w\-]+ 正则表达式。它将匹配 A-Za-z0-9-_ 字符。这个决定是为了速度和可靠性,因为这个匹配将匹配字母、数字和互联网上大多数使用的符号。

然而,有时可能需要添加自定义正则表达式来匹配更复杂的字符,如外文字母 æ ø å 等。

您可以通过访问网站 Regex101.com 来测试您的自定义正则表达式。

您不必为所有参数添加自定义正则表达式,只需添加一个全局正则表达式,该正则表达式将用于路由上的所有参数。

注意:如果您希望正则表达式在整个应用程序中可用,我们建议使用以下示例中所示的全局参数。

示例

此示例将确保所有参数在解析时使用 [\w\-\æ\ø\å]+ (a-zA-Z-_0-9æøå) 正则表达式。

SimpleRouter::get('/path/{parameter}', 'VideoController@home', ['defaultParameterRegex' => '[\w\-\æ\ø\å]+']);

如果您需要多个路由在解析参数时使用自定义正则表达式,您也可以将此设置应用于组。

SimpleRouter::group(['defaultParameterRegex' => '[\w\-\æ\ø\å]+'], function() {

    SimpleRouter::get('/path/{parameter}', 'VideoController@home');

});

命名路由

命名路由允许方便地生成特定路由的 URL 或重定向。您可以通过将 name 方法链接到路由定义来指定路由的名称

SimpleRouter::get('/user/profile', function () {
    // Your code here
})->name('profile');

您还可以指定控制器操作的名称

SimpleRouter::get('/user/profile', 'UserController@profile')->name('profile');

生成命名路由的URL

一旦为给定路由分配了名称,您就可以在生成 URL 或重定向时使用该路由的名称,通过全局 url 辅助函数(请参阅辅助函数部分)。

// Generating URLs...
$url = url('profile');

如果命名路由定义了参数,您可以将参数作为 url 函数的第二个参数传递。给定的参数将自动插入到 URL 的正确位置。

SimpleRouter::get('/user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = url('profile', ['id' => 1]);

有关 URL 的更多信息,请参阅URLs 部分。

路由分组

路由组允许您在大量路由之间共享路由属性,例如中间件或命名空间,而无需在每个单独的路由上定义这些属性。共享属性以数组格式指定为 SimpleRouter::group 方法的第一个参数。

中间件

要将中间件分配给组内的所有路由,您可以在组属性数组中使用中间件键。中间件按数组中列出的顺序执行。

SimpleRouter::group(['middleware' => \Demo\Middleware\Auth::class], function () {
    SimpleRouter::get('/', function ()    {
        // Uses Auth Middleware
    });

    SimpleRouter::get('/user/profile', function () {
        // Uses Auth Middleware
    });
});

命名空间

路由组的另一个常见用途是在组数组中使用 namespace 参数为控制器组分配相同的 PHP 命名空间。

注意

组命名空间将仅添加到具有相对回调的路由。例如,如果您的路由具有绝对回调如 \Demo\Controller\DefaultController@home,则不会添加路由的命名空间。要修复此问题,您可以通过从回调的开始处删除 \ 使回调相对。

SimpleRouter::group(['namespace' => 'Admin'], function () {
    // Controllers Within The "App\Http\Controllers\Admin" Namespace
});

您可以将参数添加到路由的前缀中。

在添加任何路由所需参数之后,将从前到后注入来自先前路由的参数。

SimpleRouter::group(['prefix' => '/lang/{lang}'], function ($language) {
    
    SimpleRouter::get('/about', function($language) {
    	
    	// Will match /lang/da/about
    	
    });
    
});

子域名路由

路由分组还可以用来处理子域路由。子域可以像路由URL一样分配路由参数,允许您捕获子域的一部分,用于您的路由或控制器中。可以使用组属性数组中的domain键来指定子域。

SimpleRouter::group(['domain' => '{account}.myapp.com'], function () {
    SimpleRouter::get('/user/{id}', function ($account, $id) {
        //
    });
});

路由前缀

可以使用prefix组属性将给定的URL作为前缀添加到组中的每个路由。例如,您可能希望将组内的所有路由URL都添加admin前缀。

SimpleRouter::group(['prefix' => '/admin'], function () {
    SimpleRouter::get('/users', function ()    {
        // Matches The "/admin/users" URL
    });
});

您还可以在组中使用参数。

SimpleRouter::group(['prefix' => '/lang/{language}'], function ($language) {
    SimpleRouter::get('/users', function ($language)    {
        // Matches The "/lang/da/users" URL
    });
});

部分分组

部分路由分组具有正常分组相同的优点,但与正常分组总是渲染以检索其子路由不同,部分分组只在URL匹配后才渲染。因此,部分分组更像是一个传统路由与分组优点的混合体。

这在您只想在满足特定条件或逻辑时添加特殊路由的情况下非常有用。

注意:请谨慎使用部分分组,因为其中添加的路由只有在部分分组URL匹配并渲染后才会被渲染和可用。这可能会导致在部分分组匹配并渲染之前,url()无法找到添加的路由的URL。

示例

SimpleRouter::partialGroup('/plugin/{name}', function ($plugin) {

    // Add routes from plugin

});

表单方法欺骗

HTML表单不支持PUTPATCHDELETE操作。因此,在定义从HTML表单调用的PUTPATCHDELETE路由时,您需要在表单中添加一个隐藏的_method字段。发送到_method字段的值将用作HTTP请求方法。

<input type="hidden" name="_method" value="PUT" />

访问当前路由

您可以使用以下方法访问当前加载路由的信息。

SimpleRouter::request()->getLoadedRoute();
request()->getLoadedRoute();

其他示例

您可以在下面的routes.php示例文件中找到更多示例。

<?php
use Pecee\SimpleRouter\SimpleRouter;

/* Adding custom csrfVerifier here */
SimpleRouter::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());

SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHandler' => \Demo\Handlers\CustomExceptionHandler::class], function() {


    SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show', ['where' => ['id' => '[0-9]+']]);

	/**
     * Class hinting is supported too
     */
     
     SimpleRouter::get('/answers/{id}', [ControllerAnswers::class, 'show'], ['where' => ['id' => '[0-9]+']]);

    /**
     * Restful resource (see IRestController interface for available methods)
     */

    SimpleRouter::resource('/rest', ControllerResource::class);


    /**
     * Load the entire controller (where url matches method names - getIndex(), postIndex(), putIndex()).
     * The url paths will determine which method to render.
     *
     * For example:
     *
     * GET  /animals         => getIndex()
     * GET  /animals/view    => getView()
     * POST /animals/save    => postSave()
     *
     * etc.
     */

    SimpleRouter::controller('/animals', ControllerAnimals::class);

});

SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']);

CSRF保护

任何向POSTPUTDELETE路由发送表单的表单都应该包含CSRF-token。我们强烈建议您启用站点的CSRF验证以最大程度地提高安全性。

您可以使用BaseCsrfVerifier来在所有请求上启用CSRF验证。如果您需要为特定URL禁用验证,请参阅下面的“自定义CSRF验证器”部分。

默认情况下,simple-php-router将使用CookieTokenProvider类。此提供程序将在客户端机器上使用cookie存储安全令牌。如果您想将令牌存储在其他地方,请参阅下面的“创建自定义Token提供程序”部分。

添加CSRF验证器

您已经创建CSRF验证器后,需要告诉simple-php-router它应该使用它。您可以在routes.php文件中添加以下行来实现这一点。

SimpleRouter::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());

获取CSRF令牌

向任何启用CSRF验证的URL发送请求时,您需要发送CSRF-token,否则请求将被拒绝。

您可以通过调用辅助方法来获取CSRF-token。

csrf_token();

您还可以直接获取令牌。

return SimpleRouter::router()->getCsrfVerifier()->getTokenProvider()->getToken();

输入字段的默认名称/键是csrf_token,并在BaseCsrfVerifier类的POST_KEY常量中定义。您可以通过在您自己的CSRF验证器类中覆盖常量来更改键。

示例

下面的示例将使用隐藏字段"csrf_token"向当前URL发送POST请求。

<form method="post" action="<?= url(); ?>">
    <input type="hidden" name="csrf_token" value="<?= csrf_token(); ?>">
    <!-- other input elements here -->
</form>

自定义CSRF验证器

创建一个新的类,并扩展simple-php-router库默认提供的BaseCsrfVerifier中间件类。

except属性添加到您想要排除/白名单的URL数组中,以便于路由。使用*在URL末尾将匹配整个URL。

下面是一个CSRF验证器类的示例。

namespace Demo\Middlewares;

use Pecee\Http\Middleware\BaseCsrfVerifier;

class CsrfVerifier extends BaseCsrfVerifier
{
	/**
	 * CSRF validation will be ignored on the following urls.
	 */
	protected $except = ['/api/*'];
}

自定义Token提供程序

默认情况下,BaseCsrfVerifier将使用CookieTokenProvider来在客户端机器上的cookie中存储令牌。

如果您需要将令牌存储在其他地方,您可以通过创建自己的类并实现ITokenProvider类来实现。

class SessionTokenProvider implements ITokenProvider
{

    /**
     * Refresh existing token
     */
    public function refresh(): void
    {
        // Implement your own functionality here...
    }

    /**
     * Validate valid CSRF token
     *
     * @param string $token
     * @return bool
     */
    public function validate($token): bool
    {
        // Implement your own functionality here...
    }
    
    /**
     * Get token token
     *
     * @param string|null $defaultValue
     * @return string|null
     */
    public function getToken(?string $defaultValue = null): ?string 
    {
        // Implement your own functionality here...
    }

}

接下来,您需要在路由文件中设置您的自定义ITokenProvider实现。

$verifier = new \Demo\Middlewares\CsrfVerifier();
$verifier->setTokenProvider(new SessionTokenProvider());

SimpleRouter::csrfVerifier($verifier);

中间件

中间件是加载在路由渲染之前的类。中间件可以用来验证用户是否已登录,或为当前请求/路由设置特定参数。中间件必须实现 IMiddleware 接口。

示例

namespace Demo\Middlewares;

use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;

class CustomMiddleware implements IMiddleware {

    public function handle(Request $request): void 
    {
    
        // Authenticate user, will be available using request()->user
        $request->user = User::authenticate();

        // If authentication failed, redirect request to user-login page.
        if($request->user === null) {
            $request->setRewriteUrl(url('user.login'));
        }

    }
}

异常处理器

异常处理器是处理所有异常的类。异常处理器必须实现 IExceptionHandler 接口。

处理404、403和其他错误

如果您只想捕获404(页面未找到)等错误,可以使用 SimpleRouter::error($callback) 静态辅助方法。

这将添加一个回调方法,当所有路由上发生错误时将触发。

以下基本示例在发生 NotFoundHttpException(404)时将页面重定向到 /not-found。代码应放置在包含您路由的文件中。

SimpleRouter::get('/not-found', 'PageController@notFound');
SimpleRouter::get('/forbidden', 'PageController@notFound');

SimpleRouter::error(function(Request $request, \Exception $exception) {

    switch($exception->getCode()) {
        // Page not found
        case 404:
            response()->redirect('/not-found');
        // Forbidden
        case 403:
            response()->redirect('/forbidden');
    }
    
});

上述示例将所有HTTP状态码为 404(页面未找到)和 403(禁止)的错误重定向到 /not-found/forbidden

如果您不想重定向,但想在当前URL上渲染错误页面,可以告诉路由器执行一个重写回调,如下所示

$request->setRewriteCallback('ErrorController@notFound');

如果您将设置正确的浏览器错误状态

SimpleRouter::response()->httpCode(404);

使用自定义异常处理器

这是异常处理器实现的简单示例(请参阅"轻松覆盖即将加载的路由"以了解如何更改回调的示例)。

namespace Demo\Handlers;

use Pecee\Http\Request;
use Pecee\SimpleRouter\Handlers\IExceptionHandler;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;

class CustomExceptionHandler implements IExceptionHandler
{
	public function handleError(Request $request, \Exception $error): void
	{

		/* You can use the exception handler to format errors depending on the request and type. */

		if ($request->getUrl()->contains('/api')) {

			response()->json([
				'error' => $error->getMessage(),
				'code'  => $error->getCode(),
			]);

		}

		/* The router will throw the NotFoundHttpException on 404 */
		if($error instanceof NotFoundHttpException) {

			// Render custom 404-page
			$request->setRewriteCallback('Demo\Controllers\PageController@notFound');
			return;
			
		}
		
		/* Other error */
		if($error instanceof MyCustomException) {

			$request->setRewriteRoute(
				// Add new route based on current url (minus query-string) and add custom parameters.
				(new RouteUrl(url(null, null, []), 'PageController@error'))->setParameters(['exception' => $error])
			);
			return;
			
		}

		throw $error;

	}

}

您可以通过使用 exceptionHandler 设置属性将您自定义的异常处理器类添加到您的组中。exceptionHandler 可以是类名或类名数组。

SimpleRouter::group(['exceptionHandler' => \Demo\Handlers\CustomExceptionHandler::class], function() {

    // Your routes here

});

防止父异常处理器合并

默认情况下,路由器将合并异常处理器到父组提供的任何处理器中,并将按从新到旧的顺序执行。

如果您希望您的组异常处理器独立执行,您可以添加 mergeExceptionHandlers 属性并将其设置为 false

SimpleRouter::group(['prefix' => '/', 'exceptionHandler' => \Demo\Handlers\FirstExceptionHandler::class, 'mergeExceptionHandlers' => false], function() {

	SimpleRouter::group(['prefix' => '/admin', 'exceptionHandler' => \Demo\Handlers\SecondExceptionHandler::class], function() {
	
		// Both SecondExceptionHandler and FirstExceptionHandler will trigger (in that order).
	
	});
	
	SimpleRouter::group(['prefix' => '/user', 'exceptionHandler' => \Demo\Handlers\SecondExceptionHandler::class, 'mergeExceptionHandlers' => false], function() {
	
		// Only SecondExceptionHandler will trigger.
	
	});

});

URL

默认情况下,所有控制器和资源路由将使用其URL的简化版本作为名称。

您可以通过使用 url() 短路辅助函数轻松检索路由的URL或操作当前URL。

url() 将返回一个 Url 对象,当渲染时将返回一个 string,因此它可以在模板等地方安全使用,但包含 Url 类中所有有用的辅助方法,如 containsindexOf 等。请参阅下面的 有用的URL技巧

获取当前URL

获取和/或操作当前URL从未如此容易。

以下示例显示了如何获取当前URL

# output: /current-url
url();

按名称获取(单个路由)

SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);

# output: /product-view/22/?category=shoes
url('product', ['id' => 22], ['category' => 'shoes']);

# output: /product-view/?category=shoes
url('product', null, ['category' => 'shoes']);

按名称获取(控制器路由)

SimpleRouter::controller('/images', ImagesController::class, ['as' => 'picture']);

# output: /images/view/?category=shows
url('picture@getView', null, ['category' => 'shoes']);

# output: /images/view/?category=shows
url('picture', 'getView', ['category' => 'shoes']);

# output: /images/view/
url('picture', 'view');

按类获取

SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
SimpleRouter::controller('/images', 'ImagesController');

# output: /product-view/22/?category=shoes
url('ProductsController@show', ['id' => 22], ['category' => 'shoes']);

# output: /images/image/?id=22
url('ImagesController@getImage', null, ['id' => 22]);

为控制器/资源路由上的方法使用自定义名称

SimpleRouter::controller('gadgets', GadgetsController::class, ['names' => ['getIphoneInfo' => 'iphone']]);

url('gadgets.iphone');

# output
# /gadgets/iphoneinfo/

获取REST/资源控制器URL

SimpleRouter::resource('/phones', PhonesController::class);

# output: /phones/
url('phones');

# output: /phones/
url('phones.index');

# output: /phones/create/
url('phones.create');

# output: /phones/edit/
url('phones.edit');

操作URL

您可以通过添加您的GET参数来轻松操作查询字符串。

# output: /current-url?q=cars

url(null, null, ['q' => 'cars']);

您可以通过将值设置为 null 来删除查询字符串参数。

以下示例将删除名为 q 的任何查询字符串参数,但保留所有其他查询字符串参数

$url = url()->removeParam('q');

有关更多信息,请参阅文档中的 有用的URL技巧 部分。

有用的URL技巧

调用 url 总是返回一个 Url 对象。渲染后,它将返回相对 urlstring,因此可以在模板等地方安全使用。

但是,这允许我们使用 Url 对象上的有用方法,如 indexOfcontains 或检索URL的特定部分,如路径、查询字符串参数、主机等。您还可以操作URL,如删除或添加参数、更改主机等。

在以下示例中,我们检查当前URL是否包含 /api 部分。

if(url()->contains('/api')) {
    // ... do stuff
}

如前所述,您还可以使用 Url 对象来显示URL的特定部分或控制您想要的URL部分。

# Grab the query-string parameter id from the current-url.
$id = url()->getParam('id');

# Get the absolute url for the current url.
$absoluteUrl = url()->getAbsoluteUrl();

有关更多方法,请参阅 Pecee\Http\Url 类。

输入与参数

simple-router 提供了库和辅助工具,可以轻松管理和操作输入参数,如 $_POST$_GET$_FILE

使用Input类来管理参数

您可以使用InputHandler类轻松访问和管理请求中的参数。该InputHandler类提供了扩展功能,例如在对象上直接复制/移动上传的文件,获取文件扩展名、MIME类型等。

获取单个参数值

input($index, $defaultValue, ...$methods);

要快速从参数获取值,您可以使用input辅助函数。

这将自动删除值的首尾空格并确保其不为空。如果为空,则返回$defaultValue

注意:此函数返回一个string,除非参数分组在一起,在这种情况下它将返回一个包含值的array

示例

此示例匹配POST和GET请求方法,如果名称为空,则返回默认值"Guest"。

$name = input('name', 'Guest', 'post', 'get');

获取参数对象

处理文件上传时,检索原始参数对象可能很有用。

在多个或特定请求方法中搜索具有默认值的对象

以下示例如果找到参数,将返回一个InputItem对象或返回$defaultValue。如果参数分组,它将返回一个包含InputItem对象的数组。

$object = input()->find($index, $defaultValue = null, ...$methods);

获取特定的$_GET参数作为InputItem对象

以下示例如果找到参数,将返回一个InputItem对象或返回$defaultValue。如果参数分组,它将返回一个包含InputItem对象的数组。

$object = input()->get($index, $defaultValue = null);

获取特定的$_POST参数作为InputItem对象

以下示例如果找到参数,将返回一个InputItem对象或返回$defaultValue。如果参数分组,它将返回一个包含InputItem对象的数组。

$object = input()->post($index, $defaultValue = null);

获取特定的$_FILE参数作为InputFile对象

以下示例如果找到参数,将返回一个InputFile对象或返回$defaultValue。如果参数分组,它将返回一个包含InputFile对象的数组。

$object = input()->file($index, $defaultValue = null);

管理文件

/**
 * Loop through a collection of files uploaded from a form on the page like this
 * <input type="file" name="images[]" />
 */

/* @var $image \Pecee\Http\Input\InputFile */
foreach(input()->file('images', []) as $image)
{
    if($image->getMime() === 'image/jpeg') 
    {
        $destinationFilname = sprintf('%s.%s', uniqid(), $image->getExtension());
        $image->move(sprintf('/uploads/%s', $destinationFilename));
    }
}

获取所有参数

# Get all
$values = input()->all();

# Only match specific keys
$values = input()->all([
    'company_name',
    'user_id'
]);

所有对象都实现了IInputItem接口,并且始终包含以下方法

  • getIndex() - 返回输入的索引/键。
  • setIndex() - 设置输入的索引/键。
  • getName() - 返回输入的友好名称(company_name将变成公司名称等)。
  • setName() - 设置输入的友好名称(company_name将变成公司名称等)。
  • getValue() - 返回输入的值。
  • setValue() - 设置输入的值。

InputFile具有上述相同的方法,以及一些其他特定的文件方法,如

  • getFilename - 获取文件名。
  • getTmpName() - 获取文件临时名称。
  • getSize() - 获取文件大小。
  • move($destination) - 将文件移动到目的地。
  • getContents() - 获取文件内容。
  • getType() - 获取文件的MIME类型。
  • getError() - 获取文件上传错误。
  • hasError() - 返回bool,表示上传过程中是否发生错误(如果getError不是0)。
  • toArray() - 返回原始数组

检查参数是否存在

您可以使用exists方法轻松检查是否存在多个项目。它与value类似,可以用于过滤请求方法,并支持stringarray作为参数值。

示例

if(input()->exists(['name', 'lastname'])) {
	// Do stuff
}

/* Similar to code above */
if(input()->exists('name') && input()->exists('lastname')) {
	// Do stuff
}

事件

本节将帮助您了解如何将自定义回调注册到路由器的事件中。它还将涵盖事件处理程序的基本知识;如何使用路由器提供的事件处理程序以及如何创建您自己的自定义事件处理程序。

可用事件

本节包含可以使用EventHandler注册的所有可用事件。

所有事件回调都将检索一个EventArgument对象作为参数。此对象包含对事件名称、路由器和请求实例以及任何与特定事件相关的特殊事件参数的轻松访问。您可以在下面的列表中查看每个事件返回的特殊事件参数。

注册新事件

要注册新事件,您需要创建一个新的EventHandler对象实例。在此对象上,您可以通过调用registerEvent方法添加任意数量的回调。

注册事件后,请确保通过调用SimpleRouter::addEventHandler()将其添加到路由器中。我们建议您在routes.php中添加您的事件处理程序。

示例

use Pecee\SimpleRouter\Handlers\EventHandler;
use Pecee\SimpleRouter\Event\EventArgument;

// --- your routes goes here ---

$eventHandler = new EventHandler();

// Add event that fires when a route is rendered
$eventHandler->register(EventHandler::EVENT_RENDER_ROUTE, function(EventArgument $argument) {
   
   // Get the route by using the special argument for this event.
   $route = $argument->route;
   
   // DO STUFF...
    
});

SimpleRouter::addEventHandler($eventHandler);

自定义事件处理器

EventHandler 是一个管理事件的类,必须继承自 IEventHandler 接口。处理器知道如何处理特定处理器类型的事件。

大多数情况下,基本的 \Pecee\SimpleRouter\Handler\EventHandler 类对大多数人来说已经足够了,因为您只需注册一个在触发时执行的事件。

让我们来看看如何创建您自己的事件处理器类。

下面是一个自定义事件处理器 DatabaseDebugHandler 的基本示例。下面的示例想法是在触发时将所有事件记录到数据库中。希望这足以让您了解事件处理器的工作原理。

namespace Demo\Handlers;

use Pecee\SimpleRouter\Event\EventArgument;
use Pecee\SimpleRouter\Router;

class DatabaseDebugHandler implements IEventHandler
{

    /**
     * Debug callback
     * @var \Closure
     */
    protected $callback;

    public function __construct()
    {
        $this->callback = function (EventArgument $argument) {
            // todo: store log in database
        };
    }

    /**
     * Get events.
     *
     * @param string|null $name Filter events by name.
     * @return array
     */
    public function getEvents(?string $name): array
    {
        return [
            $name => [
                $this->callback,
            ],
        ];
    }

    /**
     * Fires any events registered with given event-name
     *
     * @param Router $router Router instance
     * @param string $name Event name
     * @param array ...$eventArgs Event arguments
     */
    public function fireEvents(Router $router, string $name, ...$eventArgs): void
    {
        $callback = $this->callback;
        $callback(new EventArgument($router, $eventArgs));
    }

    /**
     * Set debug callback
     *
     * @param \Closure $event
     */
    public function setCallback(\Closure $event): void
    {
        $this->callback = $event;
    }

}

高级

多路由渲染

如果您需要多个路由在同一个URL上执行,您可以通过在您的 routes.php 文件中设置 SimpleRouter::enableMultiRouteRendering(true) 来启用此功能。

这通常用于高级情况,例如在CMS系统中,需要渲染多个路由。

限制IP访问

您可以使用内置的 IpRestrictAccess 中间件来白名单和/或黑名单IP的访问。

创建您自己的自定义中间件并扩展 IpRestrictAccess 类。

IpRestrictAccess 类包含两个属性 ipBlacklistipWhitelist,可以将它们添加到您的中间件中,以更改哪些IP可以访问您的路由。

您可以使用 * 来限制对IP范围的访问。

use \Pecee\Http\Middleware\IpRestrictAccess;

class IpBlockerMiddleware extends IpRestrictAccess 
{

    protected $ipBlacklist = [
        '5.5.5.5',
        '8.8.*',
    ];

    protected $ipWhitelist = [
        '8.8.2.2',
    ];

}

您可以通过将中间件添加到组来将中间件添加到多个路由。

设置自定义基本路径

有时添加一个自定义基本路径到所有添加的路由可能会有用。

这可以很容易地利用项目的 事件处理器 支持来实现。

$basePath = '/basepath';

$eventHandler = new EventHandler();
$eventHandler->register(EventHandler::EVENT_ADD_ROUTE, function(EventArgument $event) use($basePath) {

	$route = $event->route;

	// Skip routes added by group as these will inherit the url
	if(!$event->isSubRoute) {
		return;
	}
	
	switch (true) {
		case $route instanceof ILoadableRoute:
			$route->prependUrl($basePath);
			break;
		case $route instanceof IGroupRoute:
			$route->prependPrefix($basePath);
			break;

	}
	
});

SimpleRouter::addEventHandler($eventHandler);

在上面的示例中,我们创建了一个新的 EVENT_ADD_ROUTE 事件,当添加新路由时触发。我们跳过所有子路由,因为这些将继承其父路由的URL。然后,如果路由是组,我们更改前缀
否则我们更改URL。

URL重写

更改当前路由

有时操纵即将加载的路由可能会有用。simple-php-router 允许您轻松地操纵和更改即将渲染的路由。关于当前路由的所有信息都存储在 \Pecee\SimpleRouter\Router 实例的 loadedRoute 属性中。

为了方便访问,您可以使用快捷助手函数 request() 而不是直接调用类 \Pecee\SimpleRouter\SimpleRouter::router()

request()->setRewriteCallback('Example\MyCustomClass@hello');

// -- or you can rewrite by url --

request()->setRewriteUrl('/my-rewrite-url');

Bootmanager:动态加载路由

有时可能有必要将URL存储在数据库、文件或类似的地方。在这个例子中,我们希望URL /my-cat-is-beatiful 加载路由 /article/view/1,这是路由器知道的,因为它在 routes.php 文件中定义。

为了干扰路由器,我们创建了一个实现 IRouterBootManager 接口的类。此类将在 routes.php 中的任何其他规则之前加载,并允许我们在满足我们的某些条件(如来自URL /my-cat-is-beatiful)时“更改”当前路由。

use Pecee\Http\Request;
use Pecee\SimpleRouter\IRouterBootManager;
use Pecee\SimpleRouter\Router;

class CustomRouterRules implement IRouterBootManager 
{

    /**
     * Called when router is booting and before the routes is loaded.
     *
     * @param \Pecee\SimpleRouter\Router $router
     * @param \Pecee\Http\Request $request
     */
    public function boot(\Pecee\SimpleRouter\Router $router, \Pecee\Http\Request $request): void
    {

        $rewriteRules = [
            '/my-cat-is-beatiful' => '/article/view/1',
            '/horses-are-great'   => '/article/view/2',
        ];

        foreach($rewriteRules as $url => $rule) {

            // If the current url matches the rewrite url, we use our custom route

            if($request->getUrl()->contains($url)) {
                $request->setRewriteUrl($rule);
            }
        }

    }

}

上面的内容应该相当直观,并且可以轻松地修改为循环遍历存储在数据库、文件或缓存中的URL。

发生的情况是,如果当前路由与我们的 $rewriteRules 数组的索引中定义的路由匹配,我们将路由设置为数组值。

通过这种方式,路由现在将加载URL /article/view/1 而不是 /my-cat-is-beatiful

最后我们需要做的是,将我们的自定义引导管理器添加到 routes.php 文件中。您可以根据需要创建尽可能多的引导管理器,并轻松地将它们添加到您的 routes.php 文件中。

SimpleRouter::addBootManager(new CustomRouterRules());

手动添加路由

前一个示例中提到的 SimpleRouter 类,仅仅是一个简单的辅助类,它知道如何与 Router 类进行通信。如果你喜欢挑战,想要完全控制,或者只是想创建自己的 Router 辅助类,这个示例就是为你准备的。

use \Pecee\SimpleRouter\Router;
use \Pecee\SimpleRouter\Route\RouteUrl;

/* Create new Router instance */
$router = new Router();

$route = new RouteUrl('/answer/1', function() {

    die('this callback will match /answer/1');

});

$route->addMiddleware(\Demo\Middlewares\AuthMiddleware::class);
$route->setNamespace('\Demo\Controllers');
$route->setPrefix('v1');

/* Add the route to the router */
$router->addRoute($route);

自定义类加载器

你可以通过利用添加自定义类加载器的功能,轻松扩展 simple-router 以支持自定义注入框架,如 php-di。

类加载器必须继承 IClassLoader 接口。

示例

class MyCustomClassLoader implements IClassLoader
{
    /**
     * Load class
     *
     * @param string $class
     * @return object
     * @throws NotFoundHttpException
     */
    public function loadClass(string $class)
    {
        if (\class_exists($class) === false) {
            throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $class), 404);
        }

        return new $class();
    }
    
    /**
     * Called when loading class method
     * @param object $class
     * @param string $method
     * @param array $parameters
     * @return object
     */
    public function loadClassMethod($class, string $method, array $parameters)
    {
        return call_user_func_array([$class, $method], array_values($parameters));
    }

    /**
     * Load closure
     *
     * @param Callable $closure
     * @param array $parameters
     * @return mixed
     */
    public function loadClosure(Callable $closure, array $parameters)
    {
        return \call_user_func_array($closure, array_values($parameters));
    }

}

接下来,我们需要配置我们的 routes.php 文件,以便路由器使用我们的 MyCustomClassLoader 类来加载类。这可以通过在 routes.php 文件中添加以下行来完成。

SimpleRouter::setCustomClassLoader(new MyCustomClassLoader());

与php-di集成

从版本 4.3 开始,php-di 的支持已被弃用,但是你可以通过创建自己的类加载器(如下面的示例所示)轻松重新添加它。

use Pecee\SimpleRouter\ClassLoader\IClassLoader;
use Pecee\SimpleRouter\Exceptions\ClassNotFoundHttpException;

class MyCustomClassLoader implements IClassLoader
{

    protected $container;

    public function __construct()
    {
        // Create our new php-di container
        $this->container = (new \DI\ContainerBuilder())
                    ->useAutowiring(true)
                    ->build();
    }

    /**
     * Load class
     *
     * @param string $class
     * @return object
     * @throws ClassNotFoundHttpException
     */
    public function loadClass(string $class)
    {
        if ($this->container->has($class) === false) {
            throw new ClassNotFoundHttpException($class, null, sprintf('Class "%s" does not exist', $class), 404, null);
        }
        return $this->container->get($class);
    }
    
    /**
     * Called when loading class method
     * @param object $class
     * @param string $method
     * @param array $parameters
     * @return string
     */
    public function loadClassMethod($class, string $method, array $parameters)
    {
        return (string)$this->container->call([$class, $method], $parameters);
    }

    /**
     * Load closure
     *
     * @param Callable $closure
     * @param array $parameters
     * @return string
     */
    public function loadClosure(callable $closure, array $parameters)
    {
        return (string)$this->container->call($closure, $parameters);
    }
}

参数

本节包含关于扩展参数使用的进阶技巧和技巧。

扩展

这是一个将简单示例集成到框架中的示例。

该框架有自己的 Router 类,该类继承自 SimpleRouter 类。这允许框架添加自定义功能,如加载自定义的 routes.php 文件或添加调试信息等。

namespace Demo;

use Pecee\SimpleRouter\SimpleRouter;

class Router extends SimpleRouter {

    public static function start() {

        // change this to whatever makes sense in your project
        require_once 'routes.php';

        // change default namespace for all routes
        parent::setDefaultNamespace('\Demo\Controllers');

        // Do initial stuff
        parent::start();

    }

}

帮助与支持

本节将详细介绍如何调试路由器并回答一些常见的问题和问题。

常见问题和解决方案

本节将介绍常见的问题以及如何解决它们。

参数不匹配或路由与特殊字符不工作

当参数中包含一个或多个特殊字符时,人们经常会遇到这个问题。路由器使用一个稀疏的正则表达式来匹配字母 a-z 以及数字,以匹配参数,以提高性能。

所有其他字符都必须通过你的路由上的 defaultParameterRegex 选项来定义。

有关如何为匹配参数添加自己的自定义正则表达式的更多信息,请点击此处

多路由匹配?哪个有优先级?

路由器将按添加的顺序匹配路由,并在匹配的情况下渲染多个路由。

如果你想让路由器在匹配到路由时停止,你只需在回调中返回一个值,或手动停止执行(使用 response()->json() 等),或者简单地返回一个结果。

任何实现了 __toString() 魔术方法的返回对象也会阻止渲染其他路由。

如果你想让路由器在每个请求中只执行一个路由,你可以禁用多个路由渲染

在子路径上使用路由器

请参阅文档的设置自定义基本路径部分。

调试

本节将向您展示如何为路由器编写单元测试,查看有用的调试信息,并回答一些常见问题。

它还将介绍如何报告您可能遇到的问题。

创建单元测试

调试路由器中任何问题的最快、最简单的方法是创建一个代表您所遇到问题的单元测试。

单元测试使用特殊的 TestRouter 类,该类模拟浏览器的请求方法和请求 URL。

TestRouter 类可以直接返回输出或静默渲染路由。

public function testUnicodeCharacters()
{
    // Add route containing two optional paramters with special spanish characters like "í".
    TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']);
    
    // Start the routing and simulate the url "/cursos/listado/especialidad/cirugía local".
    TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'GET');
    
    // Verify that the url for the loaded route matches the expected route.
    $this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
    
    // Start the routing and simulate the url "/test/Dermatología" using "GET" as request-method.
    TestRouter::debugNoReset('/test/Dermatología', 'GET');

    // Another route containing one parameter with special spanish characters like "í".
    TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']);

    // Get all parameters parsed by the loaded route.
    $parameters = TestRouter::request()->getLoadedRoute()->getParameters();

    // Check that the parameter named "param" matches the exspected value.
    $this->assertEquals('Dermatología', $parameters['param']);

    // Add route testing danish special characters like "ø".
    TestRouter::get('/category/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']);
    
    // Start the routing and simulate the url "/kategory/økse" using "GET" as request-method.
    TestRouter::debugNoReset('/category/økse', 'GET');
    
    // Validate that the URL of the loaded-route matches the expected url.
    $this->assertEquals('/category/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());

    // Reset the router, so other tests wont inherit settings or the routes we've added.
    TestRouter::router()->reset();
}

使用 TestRouter 辅助类

根据您的测试,您可以在单元测试中渲染路由时使用以下方法。

调试信息

库可以输出调试信息,其中包含加载的路由、解析的请求 URL 等信息。它还包含在报告新问题时重要的信息,如 PHP 版本、库版本、服务器变量、路由调试日志等。

您可以通过调用替代的启动方法来激活调试信息。

下面的示例将启动路由并返回包含调试信息的数组。

示例

$debugInfo = SimpleRouter::startDebug();
echo sprintf('<pre>%s</pre>', var_export($debugInfo));
exit;

上面的示例将提供包含以下内容的输出

基准测试和日志记录

您可以通过在 Router 实例上调用 setDebugEnabled 方法来激活基准测试调试/日志记录。

您必须在开始路由之前启用调试。

示例

SimpleRouter::router()->setDebugEnabled(true);
SimpleRouter::start();

当路由完成时,您可以通过在 Router 实例上调用 getDebugLog() 来获取调试日志。这将返回一个包含执行时间、跟踪信息和调试信息的 array

示例

$messages = SimpleRouter::router()->getDebugLog();

报告新问题

在报告您的问题之前,请确保您遇到的问题在常见错误部分或GitHub上的已关闭问题页面上已有解答。

为了避免混淆并尽快帮助您解决问题,您应该详细描述您遇到的问题。

报告新问题的流程

  1. 前往此页面创建新的问题。
  2. 尽可能用最少的词语添加描述您问题的标题。
  3. 将下面的模板复制并粘贴到您问题描述中,并将每个步骤替换为您自己的信息。如果步骤与您的问题不相关,您可以删除它。

问题模板

将下面的模板复制并粘贴到您新问题的描述中,并用您自己的信息替换。

您可以通过查看调试信息部分来了解如何生成调试信息。

### Description

The library fails to render the route `/user/æsel` which contains one parameter using a custom regular expression for matching special foreign characters. Routes without special characters like `/user/tom` renders correctly.

### Steps to reproduce the error

1. Add the following route:

```php
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
```

2. Navigate to `/user/æsel` in browser.

3. `NotFoundHttpException` is thrown by library.

### Route and/or callback for failing route

*Route:*

```php
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
```

*Callback:*

```php
public function show($username) {
    return sprintf('Username is: %s', $username);
}
```

### Debug info

```php

[PASTE YOUR DEBUG-INFO HERE]

```

请记住,更详细的问题描述和调试信息可能很麻烦编写,但它将帮助其他人理解并解决您的问题,而无需询问信息。

注意:在创建新问题时,请尽可能详细地描述。这将帮助其他人更容易理解和解决您的问题。在描述中提供必要的步骤以重现错误,添加有用的调试信息等,将帮助其他人快速解决您报告的问题。

反馈与开发

如果您在项目中缺少库的功能,或者如果您有反馈,我们很乐意听取您的意见。请通过创建新问题给我们留下反馈。

遇到问题了吗?

在报告新问题之前,请先参阅文档中的帮助和支持部分。

贡献开发指南

  • 请尝试遵循PSR-2代码风格指南。

  • 请将您的pull请求创建到与您想要更改的版本号匹配的开发基础库中。例如,当向版本3推送更改时,pull request应使用v3-development基础库/分支。

  • 为您的提交创建详细的描述,因为这些将在新版本的更改日志中使用。

  • 在更改现有功能时,请确保单元测试正在运行。

  • 在添加新内容时,请记住为该功能添加新的单元测试。

鸣谢

网站

以下是一些在生产中使用simple-router项目的网站。

许可证

MIT许可(MIT)

版权所有(c)2016 Simon Sessingø / simple-php-router

特此授予任何人免费获得本软件及其相关文档文件(“软件”)副本的权利,无论出于何种目的,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件副本,并允许向软件提供者提供软件的人这样做,前提是必须遵守以下条件

上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。

本软件按“原样”提供,不提供任何形式的保证,无论是明示的还是隐含的,包括但不限于适销性、特定用途适用性和非侵权性。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论基于合同行为、侵权或其他原因,以及与该软件、使用或其他方式与该软件有关的情况。