tbolner/flex-php-router

适用于Web和CLI应用的路由器(PHP)。

1.0.3 2020-08-26 14:47 UTC

This package is auto-updated.

Last update: 2024-09-26 23:53:03 UTC


README

简单灵活的Web和命令行应用路由库。

Packagist页面: https://packagist.org.cn/packages/tbolner/flex-php-router

推荐IDE: PhpStorm

安装

将库包含到项目的 composer.json 文件中

{
    "require": {
        "tbolner/flex-php-router": "dev-master"
    }
}

然后执行

composer update

此库需要PHP 7.1或更高版本。

Web应用的用法示例

在您的项目中创建一个文件夹,用于存放控制器。例如

project1/src/Controller/

然后创建控制器。文件名必须与URI的第一部分匹配

如你所见,当查找相应的PHP文件时,URI的第一部分所有特殊字符都被移除。(出于安全考虑。)更准确地说:从第一部分移除了所有不是:a-z、A-Z、0-9、-(破折号)、_(下划线)的字符。(URL的其他部分不受此机制的影响。)

控制器将通过模式匹配来决定执行什么。这些匹配的部分被称为“动作”。

project1/src/Controller/items.php

<?php declare(strict_types=1);

namespace MyProject\Controller;

use FlexPhpRouter\Router;

Router::get(
    "items/{id:int}",
    function (int $id) {
        /* ... your code ... */
        /* you can also throw exceptions to
            catch them at your router start-up
            code */
    }
);

Router::post(
    "items/{id:int}/list",
    function (int $id) {
        /* ... your code ... */
    }
);

Router::put(
    "items/{id:int}/details{other:string}",
    function (int $id, string $other) {
        /* ... your code ... */
    }
);

注意

  • 在路径参数中,模式有4种类型
    • 字符串整数浮点数布尔值
    • 要添加额外的类型,只需使用“字符串”并在动作中进行类型转换。
  • 末尾的斜线(/)被忽略。无论是否有结束斜线,模式都将匹配。
  • 要匹配站点根目录,只需在默认控制器中匹配“/”即可。(有关如何指定默认控制器,请参阅下一点。)
  • 请注意,在第三个动作中,参数跟随“/details”文本而没有斜线分隔它们,这是一个可接受的解决方案。
  • 支持的方法有
    • get,post,put,delete,any

最后将路由启动代码添加到您的应用中

project1/web/index.php

<?php declare(strict_types=1);

namespace Web;

use FlexPhpRouter\Router;
use MyProject\Exceptions\MySpecialException;

require_once (dirname(__FILE__)."/../vendor/autoload.php");

try {
    Router::route(dirname(__FILE__)."/../src/Controller", 'default');
} catch (MySpecialException $ex) {
    /* ... */
} catch (\Exception $ex) {
    /* ... */
}

注意

  • Router::route()的第一个参数定义了控制器目录的路径。使用类似上面的方式,以相对方式定义路径。
  • Router::route()命令的第二个参数期望的是默认控制器的名称,当未找到控制器时会调用它。您可以在那里处理站点根目录等。名称“default”将导致“default.php”控制器文件。
  • 处理控制器或从控制器中调用的代码中抛出的所有异常。

路径相关认证等

您可以将一个可选的回调作为Router::route()的第四个参数传递

class MyAuthClass {
    public static function authenticate(bool $isWeb, string $path) {
        ...
    }
}

...

    Router::route(dirname(__FILE__)."/../src/Controller", 'default',
        '',     // The third parameter is the API prefix
        [MyAuthClass::class, 'authenticate']
    );

...

它将在任何控制器代码之前执行,并了解所选控制器路径。回调接收2个参数。第一个参数'$isWeb'当执行Web控制器时为true,当执行CLI时为false。第二个参数告诉选择了哪个路径。

此功能允许您为API条目集合实现认证。建议在认证失败的情况下,抛出您创建的异常,例如:MyAuthException,并在主入口点处理它,通常所有异常都在这里被捕获。

特性

API路径前缀

Router::route(...) 的第三个参数指定了一个API前缀,在路由过程中将始终被忽略,就像它从URI的开头被截断一样。

示例

Router::route(dirname(__FILE__)."/../src/Controller", 'default', 'api');

注意

  • 上一个示例也适用于以下前缀:'/api'、'api/'、'/api/'。

默认页面 / 404 页面

默认控制器(前面已描述)在两种情况下会被调用

  • 如果没有找到控制器,根据URI的第一部分
  • 如果加载的控制器中没有动作匹配的模式

因此,我们可以在默认动作的末尾始终放置一个动作,当没有执行任何操作时会被调用。

Controller/default.php

<?php declare(strict_types=1);

namespace Controller;

use FlexPhpIO\Response;
use FlexPhpRouter\Router;

Router::any(
    "/",
    function () {
        Response::printJson([
            "message" => "This is the site root."
        ]);
    }
);

Router::any(
    "*",
    function () {
        Response::printHtmlFile(dirname(__FILE__).'/../web/notFound.html', 404);
    }
);

注意

  • 注意最后一个动作中的 "*" 字符。它表示“任意”。
  • 第一个动作——处理站点根请求——也可以使用一个空的模式:""。

CLI应用程序的用法示例

在您的项目中创建一个文件夹,用于存放CLI控制器。例如

  • project1/src/CLI

然后创建控制器。文件名必须匹配第一个参数中传递的路径的第一部分

示例控制器

project1/src/CLI/test.php

<?php declare(strict_types=1);

namespace MyProject/CLI;

use FlexPhpRouter\Router;
use FlexPhpRouter\CliParameter;

Router::cli("test/cleanup")
    ->matchRoute(function () {
        /* ... your code ... */
        /* you can also throw exceptions to
            catch them at your router start-up
            code */
    });

Router::cli("test/run")
    ->addParameter("param1", CliParameter::TYPE_INT, true, "Description of first parameter.")
    ->addParameter("param2", CliParameter::TYPE_STRING, false, "Description of second parameter.")
    ->matchRoute(function (int $param1, string $param2 = null) {
        /* ... your code ... */
    });

在这种情况下,路由启动代码必须放入一个非Web相关的PHP文件中(因此它应该在Web文件夹之外)。例如

project1/console.php

#!/usr/bin/php
<?php declare(strict_types=1);

namespace CLI;

use FlexPhpRouter\Router;
use MyProject\Exceptions\MySpecialException;

require_once (dirname(__FILE__)."/vendor/autoload.php");

try {
    Router::route(dirname(__FILE__)."/src/CLI", 'default');
} catch (MySpecialException $ex) {
    /* ... */
} catch (\Exception $ex) {
    /* ... */
}

注意

  • 您不需要告诉 Router::route() 方法它需要期望控制台参数,因为它将自动检测脚本是在控制台模式下运行的。

设计考虑

  • 用法与Laravel路由类似。
  • 没有库依赖。
  • 没有解析和预构建缓存。请参见下一点,它解释了如何在不需要使用代码预生成的情况下实现高性能。
  • 动作的位置不是完全自定义的。它们的控制器由URL的第一部分决定。这迫使开发者保持动作代码整洁,同时也提高了应用程序的性能,因为可以通过明确的方式来找到控制器的PHP文件。(有关更多信息,请参见“Web应用程序的用法示例”)
  • 类型安全。所有参数都转换为指定的类型。
  • 它不处理输出。在大多数路由库中,动作使用 return 命令将数据返回以进行输出(例如,作为JSON的数组)。我发现这比解决的问题更多。更好的做法是将I/O功能放入一个单独的库中,并从您的控制器中调用其方法。请参见示例: FlexPhpIO
  • 它只支持一个“任意”方法指定器,但不支持每个动作的多个(特定)方法(如POST+GET),因为这些方法很少需要,所以不值得为每个动作(如Laravel)在API中添加一个额外的参数。当需要时,请将您的动作代码放入静态方法中,并从单独的POST、GET等动作中调用它们。