eftec/routeone

PHP的路由服务类

1.33 2024-03-02 14:54 UTC

README

它读取URL路由并解析路径值,因此可以以最快的方式手动或自动解释(例如,实现MVC系统)。

Packagist Total Downloads Maintenance composer php php coverage compatible

与其他库不同,此库没有依赖项,并且包含在单个类中,因此与任何PHP项目兼容,例如WordPress、Laravel、Drupal、自定义PHP项目等。

此库基于CoC Convention over Configuration。它减少了样板代码,但具有固定功能。此库不允许使用自定义"路由",但涵盖了几乎所有情况,因此提高了性能和可用性,而牺牲了灵活性。

目录

示例

假设我们有一个下一个URL http://somedomain.dom/Customer/Update/2 此库将此URL转换为可以处理或直接调用方法的变量。

route.php

$route=new RouteOne('http://www.somedomain.dom');
$route->addPath('api/{controller}/{action}/{id}'); 
$route->addPath('{controller}/{action}/{id}/{idparent}');
$route->fetchPath();
$this->callObjectEx('cocacola\controller\{controller}Controller');

controller\CustomerController.php 类

namespace cocacola\controller\;
class CustomerController {
   public function updateAction($id=null,$idparent=null,$event=null) {
      echo "We want to update the customer $id";
   }
}
 

它做什么?

假设我们执行以下操作

用户调用以下网站 http://somedomain.com/Customer/Insert,他希望显示插入客户表单

use \eftec\routeone\RouteOne;
$route=new RouteOne('.',null,null); // Create the RouteOneClass
$route->fetch(); // fetch all the input values (from the route, get, post and such).
$route->callObject('somenamespace\\controller\\%sController'); // where it will call the  class CustomerController* 

use eftec\routeone\RouteOne;
$route=new RouteOne('.',null,null); // Create the RouteOneClass
$route->fetch(); // fetch all the input values (from the route, get, post and such).
$route->callObjectEx('somenamespace\\controller\\{controller}Controller'); // where it will call the  class CustomerController* 

此代码调用类 Customer 内的 InsertActionGet(GET)、InsertActionPost(POST)或 InsertAction(GET/POST)方法

调用的方法如下所示

class Customer {
    public function insertAction($id="",$idparent="",$event="") {
        // here we do our operation.
    }
}

$id$idparent$event是什么?

id

假设我们想要 更新 客户编号 20,那么我们可以调用以下页面

http://somedomain.com/Customer/Update/20

其中20是要编辑的客户的 "$id"(它可以是数字或字符串)

idparent

如果我们想要 更新 商业 APPL 的客户编号 20 呢?

http://somedomain.com/Customer/Update/20/APPL

其中APPL是 idparent

event

现在,假设我们点击某个按钮或执行某些操作。它可以通过字段 _event 被捕获,并通过参数 $event 读取。此变量可以通过GET或POST发送。

http://somedomain.com/Customer/Update/20/APPL?_event=click

模块

注意:如果您使用addPath()和fetchPath(),模块会自动获取,因此您不需要指定它。现在,假设我们的系统是模块化的,并且我们有多个客户(内部客户、外部客户等)

$route=new RouteOne('.',null,true); // true indicates it is modular.

$route=new RouteOne('.',null,['Internal']); // or we determine the module automatically. In this case, every url that starts with Internal

那么

$route->fetch(); 
$route->callObject('somenamespace\\%2s%\\controller\\%1sController');

http://somedomain.com/Internal/Customer/Update/20/APPL?_event=click

然后,第一个分支是模块的名称(内部),它调用类 somenamespace\Internal\controller\CustomerController

入门

使用cli(推荐)

  • 安装库

composer require eftec/routeone

  • 在根目录中执行二进制文件

Linux

vendor/bin/routeonecli -init  (if the binary does not work, then chage the permission to execution)

Windows

.\vendor\bin\routeonecli.bat -init

它将创建文件 .htaccess 和文件 route.php,并且 route.php 将具有默认配置。

  • 编辑文件 route.php 并更改以下行
const BASEURL="http://localhost"; // Base url edit this value.
const BASEWEBNS="eftec\\controller"; // Base namespace (web) edit this value
const BASEAPINS="eftec\\api"; // Base namespace (api) edit this value

稍后,您可以在此文件中添加或编辑代码。

手动安装

1) 在根目录中创建一个 .htaccess 文件(Apache)

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On
    DirectoryIndex route.php

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Send Requests To Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ route.php?req=$1 [L,QSA]
</IfModule>

如果您的网络主机不允许 FollowSymlinks 选项,请尝试将其替换为 Options +SymLinksIfOwnerMatch。

重要的一行是
RewriteRule ^(.*)$ route.php?req=$1 [L,QSA] # 调用的路由器。

或配置nginx.conf(Nginx) Linux(未测试)

server {
    listen 80;
    server_name localhost;
    root /example.com/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /router.php?req=$document_uri&$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

重要的一行是
try_files $uri $uri/ /router.php?req=$document_uri&$query_string;

或配置nginx.conf(Nginx) Windows

server {
    listen 80;
    server_name localhost;
    root c:/www;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /router.php?req=$document_uri&$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

重要的一行是
try_files $uri $uri/ /router.php?req=$document_uri&$query_string;

其中 router.php 是作为路由器工作的文件。 ?req=$1 是重要的,因为系统将从 "req" 读取路由。

// router.php
$route=new RouteOne(); // Create the RouteOneClass
$route->fetch(); // fetch all the input values (from the route, get, post and such).
$route->callObject('somenamespace\\controller\\%sController'); // where it will call the  class \somenamespace\controller\CustomerController  

注意

如果想要使用与 "req" 不同的参数,则可以使用以下代码进行更改

$route->argumentName='newargument';

使用路径

从 1.21 版本开始,可以使用自定义路径而不是预定义路径。这是推荐的方式。另一种方法仍然存在。

clearPath()

语法

clearPath()

它清除所有定义的路径。

addPath()

语法

addPath($path, $name = null,callable $middleWare=null)

它添加可以由 fetchPath() 评估的路径。
示例

$this->addPath('api/{controller}/{action}/{id:0}','apipath');  
$this->addPath('/api/{controller}/{action}/{id:0}/','apipath'); // "/" at the beginner and end are trimmed.  
$this->addPath('{controller}/{action}/{id:0}','webpath');  
$this->addPath('{controller:root}/{action}/{id:0}','webpath'); // root path using default  
$this->addPath('somepath','namepath',  
     function(callable $next,$id=null,$idparent=null,$event=null) {  
         echo "middleware\n";  
         $result=$next($id,$idparent,$event); // calling the controller  
         echo "endmiddleware\n";  
          return $result;  
      });  

注意
路径的前一部分(在 {} 之前)用于确定哪个路径将被使用。
例如 "path/{controller}" 和 "path/{controller}/{id}", 系统将认为它们是相同的路径。

  • 参数 string $path 路径,例如 "aaa/{controller}/{action:default}/{id}"
    其中 default 是可选的默认值。

    • {controller}: 要调用的控制器(类)
    • {action}: 要调用的动作(方法)
    • {verb}: 动作动词(GET/POST 等。)
    • {type}: 类型(值)
    • {module}: 模块(值)
    • {id}: id(值)
    • {idparent}: 父 id(值)
    • {category}: 类别(值)
    • {subcategory}: 子类别(值)
    • {subsubcategory}: 孙子类别(值)
  • 参数 string|null $name(可选),路径的名称

  • 参数 callable|null $middleWare 用于中间件的调用函数。
    函数的第一个参数必须是可调用的方法。
    下一个参数必须是 callObjectEx 定义的参数。
    (id,idparent,event)

  • 路径可以以静态位置开始,但其余路径必须由变量(用 {} 括起来)和 "/" 分隔定义。

  • 您还可以在变量名称后编写 ":" 以设置路径的默认值:{name:defaultvalue}

  • name 可以使用 $this->currentPath 获取。如果您添加了一个具有相同名称的名称,则它将被替换。

  • 如果没有设置名称,则使用自动编号。

  • name 也将在调用 $this->fetchPath() 时返回。

示例

$this->addPath('{controller}/{id}/{idparent}');
$this->addPath('myapi/otherfolder/{controller}/{id}/{idparent}');
$this->addPath('{controller:defcontroller}/{action:defaction}/{id:1}/{idparent:2}');

// url: /dummy/10/20 =>(controller: dummy, id=10, idparent=20)
// url: /myapi/otherfolder/dummy/10/20  =>(controller: dummy, id=10, idparent=20)

您可以定义不同的路径,但是它只使用与某些 URL 匹配的路径的第一部分。'path/somepath/{id}' 将工作,'path/{id}/other' 将不会工作。

fetchPath()

语法

fetchPath()

它获取先前通过 addPath 定义的路径,并返回路径的名称(或编号)。如果没有找到,则返回 false。

示例

$route=new RouteOne('http://www.example.dom');
$route->addPath('{controller}/{id}/{idparent}','optionalname');
// if the url is : http://www.example.dom/customer/1/200 then it will return
echo $route->fetchPath(); // optionalname
echo $route->controller; // customer
echo $route->id; // 1
echo $route->idparent; // 200

方法

__construct($base='', $forcedType=null, $isModule=false)

  • string $base 基础 URL
  • string $forcedType=['api','ws','controller','front'][$i]
    api 如果是,它期望路径为 api/controller/action/id/idparent
    ws 如果是,它期望路径为 ws/controller/action/id/idparent
    controller 如果是,它期望路径为 controller/action/id/idparent
    front 如果是,它期望路径为 /category/subcategory/subsubcategory/id
  • bool $isModule 如果为 true,则路由开始读取模块名称
    false controller/action/id/idparent
    true module/controller/action/id/idparent

    array 如果值是数组,则值由路径的第一部分是否在数组中确定。
    例如 ['modulefolder1','modulefolder2']

getQuery($key,$valueIfNotFound=null)

它获取查询值(URL)。

注意:此查询不包含“req”、“_event”和“_extra”值。

示例

// http://localhost/..../?id=hi
$id=$router->getQuery("id"); // hi
$nf=$router->getQuery("something","not found"); // not found

setQuery($key,$value)

它设置查询值

示例

$route->setQuery("id","hi");
$id=$router->getQuery("id"); // hi

fetch

语法

fetchPath()

从路由中获取值,并对值进行处理。

callObjectEx

语法

callObjectEx($classStructure, $throwOnError, $method, $methodGet, $methodPost,$arguments,$injectArguments)

它创建一个对象的新实例(例如,Controller对象)并调用该方法。
注意:这是此::callObject()的高级版本。
此方法使用{}来替换基于以下变量的值

示例

// controller example http://somedomain/Customer/Insert/23
$this->callObjectEx('cocacola\controller\{controller}Controller');
// it calls the method cocacola\controller\Customer::InsertAction(23,'','');

// front example: http://somedomain/product/coffee/nescafe/1
$this->callObjectEx('cocacola\controller\{category}Controller' // the class to call
        ,false // if error then it throw an error
        ,'{subcategory}' // the method to call (get, post or any other method)
        ,null // the method to call (method get)
        ,null // the method to call (method post)
        ,['subsubcategory','id'] // the arguments to call the method
        ,['arg1','arg2']); // arguments that will be passed to the constructor of the instance 
// it calls the method cocacola\controller\product::coffee('nescafe','1');

使用当前路由调用对象内的方法。

示例

路由器

$databaseService=new SomeDatabaseService();
$route=new RouteOne();

$route->callObjectEx('cocacola\controller\{controller}Controller' // the class to call
        ,false // if error then it throw an error
        ,'{action}Action' // the method to call (get, post or any other method)
        ,'{action}Action{verb}' // the method to call (method get)
        ,'{action}Action{verb}' // the method to call (method post)
        ,['id', 'idparent', 'event'] // the arguments to call the method
        ,[$databaseService,$route]); // (optional)arguments that will be passed to the constructor of the instance 

控制器

namespace cocacola\controller;
class CustomerController {
	protected $databaseService;
    protected $route;
    public function __construct($databaseService,$route) {
        // optional: injecting services
		$this->databaseService=$databaseService;
		$this->route=$route;        
    }
    // any action GET or POST
    public function GreenAction($id="",$idparent="",$event="") {
    }
    // GET only action (optional)
    public function BlueActionGET($id="",$idparent="",$event="") {
        // **my code goes here.**
    }    
    // POST only action (optional)
    public function YellowActionPOST($id="",$idparent="",$event="") {
        // **my code goes here.**
    }    
    // GET only action (optional)
    public function RedActionGET($id="",$idparent="",$event="") {
        // **my code goes here.**
    }      
    // any action GET or POST
    public function RedAction($id="",$idparent="",$event="") {
        // **my code goes here.**
    }      
    
}

结果

callFile($fileStructure='%s.php',$throwOnError=true)

它调用(包括)当前控制器名称的php文件。

  • $fileStructure 当前控制器的名称。"%s"是当前控制器的名称。例如:/Customer/Insert -> 调用Customer.php文件
  • throwOnError 如果为true,则抛出错误。如果为false,则只返回错误消息。

getHeader()

语法

getHeader($key, $valueIfNotFound = null)

获取当前头信息(如果有的话)。如果找不到值,则返回$valueIfNotFound。注意,$key始终转换为大写。

示例

$token=$this->getHeader('token','TOKEN NOT FOUND');

getBody()

语法

getBody($jsonDeserialize = false, $asAssociative = true)

获取请求的正文。

示例

$body=$this->getBody(); // '{"id"=>1,"name"=>john}' (as string)
$body=$this->getBody(true); // stdClass {id=>1,name=>john}
$body=$this->getBody(true,true); // ["id"=>1,"name"=>john]

getCurrentUrl($withoutFilename = true)

返回不带尾随空格、参数或查询的当前基础URL。

注意:此函数依赖于$_SERVER['SERVER_NAME'],并且可能被最终用户修改。

getCurrentServer()

返回不带尾随斜杠的当前服务器。

$route->getCurrentServer(); // http://somedomain

setCurrentServer($serverName)

设置当前服务器名称。它用于getCurrentUrl()和getCurrentServer()。
注意:如果未设置$this->setCurrentServer(),则使用$_SERVER['SERVER_NAME'],并且可能被用户修改。

$route->setCurrentServer('localhost'); 
$route->setCurrentServer('127.0.0.1'); 
$route->setCurrentServer('domain.dom'); 

getUrl($extraQuery = '',$includeQuery=false)

基于类中的信息获取(完整)URL。

$route->getUrl(); // http://somedomain/controller/action/id
$route->getUrl('id=20'); // http://somedomain/controller/action/id?id=20
$route->getUrl('id=20',true); // http://somedomain/controller/action/id?id=20&field=20&field2=40

url($module,$controller,$action,$id,$idparent)

基于自定义值构建URL。

$route->url(null,"Customer","Update",20); // Customer/Update/20

urlFront($module,$category,$subcategory,$subsubcategory,$id)

基于自定义值构建URL(前端)。

$route->url(null,"Daily","Milk",20); // Daily/Milk/20

alwaysWWW($https = false)

如果子域名为空或不同于www,则重定向到www.domain.com
注意:此功能不适用于localhost、不带TLD的域名(netbios)或IP域名。这是故意的。
注意:如果此代码需要重定向,则停止代码执行。通常,它必须在代码顶部调用。

$route->alwaysWWW();  // if the domain is somedomain.dom/url, then it redirects to www.somedomain.dom/url
$route->alwaysWWW(true);  // if the domain is http: somedomain.dom/url, then it redirects to https: www.somedomain.dom/url

alwaysHTTPS()

如果页面以http加载,则重定向到https。
注意:此功能不适用于localhost、不带TLD的域名(netbios)或IP域名。这是故意的。
注意:如果此代码需要重定向,则停止代码执行。通常,它必须在代码顶部调用。

$route->alwaysHTTPS(); // http://somedomain.com ---> https://somedomain.com
$route->alwaysHTTPS(); // http://localhost ---> // http://localhost
$route->alwaysHTTPS(); // http://127.0.0.1 ---> // http://127.0.0.1
$route->alwaysHTTPS(); // http://mypc ---> // http://mypc

alwaysNakedDomain($https = false)

如果子域名是www(例如 www.domain.dom)则重定向到裸域名domain.dom

注意:此功能不适用于localhost、不带TLD的域名(netbios)或IP域名。这是故意的。

注意:如果此代码需要重定向,则停止代码执行。通常,它必须在代码顶部调用。

$route->alwaysNakedDomain();  // if the domain is www.somedomain.dom/url, then it redirects to somedomain.dom/url
$route->alwaysNakedDomain(true);  // if the domain is http: www.somedomain.dom/url, then it redirects to https: somedomain.dom/url

字段

示例

$this->addPath('portal/web/{controller}/{action:list}');
$this->fetchPath();
var_dump($this-action); // it shows the current action or the default value "list" if none.

白名单

白名单输入。

方法白名单允许两种操作

  • 为了白名单一个输入,例如,只允许“控制器”位于列表中。
  • 它还允许定义元素的情况。

例如

// Example, value not in the whitelist: someweb.dom/customer/list
$this->setWhiteList('controller',['Product','Client']);
$this->fetch();
var_dump($this->controller); // null or the default value
var_dump($this->notAllowed); // true (whitelist error)


// Example, value in the whitelist but with the wrong case: someweb.dom/customer/list
$this->setWhiteList('controller',['Customer']);
$this->fetch();
var_dump($this->controller); // it shows "Customer" instead of "customer"
var_dump($this->notAllowed); // false (not error with the validation of the whitelist)    
    
// reset whitelist for controllers
$this->setWhiteList('controller',null);        

CLI

Routeone包含一个基本的CLI来创建和初始化配置。二进制文件routeonecli位于vendor/bin文件夹中

docs/cli1.jpg

  • 执行routeonecli,它将显示一个带有简单选项[router]的菜单。
./vendor/bin/routeonecli
  • 输入router并按Enter键。

    • 可以使用TAB键进行自动补全。
    • 可以使用上箭头键和下箭头键在可用选项中进行导航。
    • 如果输入?,将显示简单的帮助。
    • 如果输入??,将显示更技术性的帮助。

    在路由器菜单中,将显示下一个屏幕

docs/cli2.jpg

挂起表示操作正在进行中或需要配置某些内容。

  • 输入configure

docs/cli3.jpg

  • 然后输入路由器文件名,不包含扩展名。
  • 然后您的开发机器。如果您不知道,请按Enter。
  • 然后您的开发基础URL。
  • 您的生产基础URL。如果您没有或不知道,请使用默认值。

完成设置后,配置将被标记为“完成”。

现在,让我们配置路径。

  • 转到路径设置。

docs/cli4.jpg

  • 然后它会显示下一个菜单,添加、删除或编辑。
  • 输入 添加

docs/cli5.jpg

  • 然后输入路径名称,例如web。
  • 然后输入路径(检查路径以查看路径语法)
  • 最后,输入与该路径关联的命名空间
  • 完成设置后,您就有了一条路径。您可以在以后添加更多路径。
  • 按回车键返回。

docs/cli6.jpg

  • 现在,您可以生成htaccess文件、PHP路由文件、加载配置或保存在此处输入的配置。
  • 要退出,请按回车键并返回。

变更日志

  • 2024-03-02 1.33
    • 正在更新依赖项到PHP 7.4。PHP 7.2的扩展支持已于3年前结束。
    • 在代码中添加了更多的类型提示。
  • 2024-01-22 1.32.1
    • 修复了当模式没有值时的问题,例如“contact/”
  • 2024-01-22 1.32
    • 更新了单元测试。
    • 现在路由文件总是调用index.php
    • fetchPath()考虑了必须字段和可选字段。
  • 2024-01-09 1.31
  • 2023-11-13 1.30.1
    • 修复了fetch()当获取的URL为空时的问题
    • 更新了.htaccess,使其在不同情况下表现更好。
  • 2023-05-08 1.30
    • addPath()现在允许指定中间件。
  • 2023-04-02 1.29
    • [RouteOneCli]已更新
    • 添加了新的方法instance(),这样我们就可以使用RouteOne::instance();来获取单例实例。
  • 2023-03-04 1.28
    • 添加了静态路径到addPath()
    • callObjectEx()现在允许任何参数。如果参数不是定义的值,则从路由中获取。
    • callObjectEx()现在允许命名参数。
    • callObjectEx()现在允许传递实例和可调用对象而不是类的名称。
    • callObjectEx()允许按路径类型过滤。默认情况下,不进行过滤。
  • 2023-03-04 1.27.1
    • 修复了addPath()添加以"/"开头的路径时的小问题。现在,值将被截断。
  • 2023-02-15 1.27
    • 代码和文档的清理。弃用旧方法
  • 2023-02-14 1.26.4
    • 修复了一些错误
  • 2023-01-27 1.26.2
    • 编辑了composer json (bin)
  • 2023-01-27 1.26
    • callObject()被标记为已弃用,但是您仍然可以使用它。
    • 函数的参数现在使用类型提示/验证
    • addPath()现在如果路径为空或null,则抛出异常。
    • 新方法redirect()
    • 新的CLI。
  • 2023-01-26 1.25
    • 一些清理
  • 2022-03-11 1.24
    • [修复]修复了当URL为null时出现的许多问题。
  • 2022-02-01 1.23
    • [新] getRequest(), getPost(),getGet()
  • 2022-01-27 1.22
    • [新] callObjectEx允许向构造函数添加参数。
    • [新] clearPath()
    • [新] addPath()
    • [新] fetchPath()
    • [新] getHeader()
    • [新] getBody()
  • 2021-04-24 1.20
    • 构造函数现在可以在构造函数中指示可能的模块。
    • 代码进行了许多清理。
    • 添加了新的字段$moduleList及其setter和getter(默认值为null)
    • 如果$moduleList不为null,则将其用于确定URL是否为模块。
    • 添加了新的字段$moduleStrategy,并在构造函数以及setter和getter中赋值(默认值为'none')
  • 2021-02-26 1.19
    • setWhiteList()现在与controllercategory一起工作
    • setWhiteList()还用于定义元素的正确大小写。
    • 方法callObjectEx()允许定义大小写。
  • 2021-02-26 1.18
    • 新字段$verb(获取当前动词,例如GET、POST等)
    • 新的白名单元素
      • $allowedVerbs 允许的动词列表。
      • $allowedFields callObjectEx()使用的允许字段列表
      • $allowedControllers 允许的控制器列表。如果设置了此列表并且控制器不在白名单中,则控制器被设置为null
    • 方法callObjectEx()允许使用动词。动词始终是大写首字母。
      • 示例 $this->callObjectEx('cocacola\controller{controller}Controller','{action}Action{verb}');
  • 2021-02-16 1.17
    • 删除所有 @ 并替换为 isset()。由于这个库与 PHP 5.6 兼容,因此它不使用 "?? " 操作符。
    • setDefaultValues() 如果在 fetch() 之后被调用,则会触发错误。
  • 2021-02.11 1.16.1
    • 修复了 "api" 和 "ws" 的问题,它没有在正确的位置读取控制器。
  • 2021-02-11 1.16
    • 移除了 Travis。
    • 降低了要求。现在,这个库在 PHP 5.6 及更高版本上工作(而不是 PHP 7.0 及更高版本)
    • 构造函数新增了一个参数,可以调用 fetch() 获取值。
    • alwaysHTTPS() 新增了一个参数,可以返回完整的 URL(如果需要重定向)或 null。
    • alwaysWWW() 新增了一个参数,可以返回完整的 URL(如果需要重定向)或 null。
    • alwaysNakedDomain() 新增了一个参数,可以返回完整的 URL(如果需要重定向)或 null。
  • 2020-06-14 1.15
    • 在 setDefaultValues() 中添加了默认值。
    • 方法 fetch() 现在会取消设置值。
    • 修复了方法 url()。
  • 2020-06-07 1.14.2
    • 修复了一个错误:删除了一个 echo(用于调试)。
  • 2020-06-07 1.14.1
    • 解决了一个小错误。它保持了兼容性。
  • 2020-06-07 1.14
    • 添加了 defcategory、defsubcategory 和 defsubsubcategory。
    • 新增了 setIdentifyType() 方法。
  • 2020-04-23 1.13
    • 大量清理。
  • 2020-04-04 1.12
    • 添加了对 nginx 的支持。
    • 更新了 .htaccess 文档。
    • 新增了 setCurrentServer() 方法。
  • 2020-03-27 1.11
    • 添加了 alwaysNakedDomain()。
  • 2020-03-27 1.10.1
    • 对 alwaysHTTPS() 进行了小修复。
  • 2020-03-27 1.10
    • 添加了 alwaysHTTPS() 和 alwaysWWW() 方法。
  • 2020-02-15 1.9
    • 为 callObject() 添加了新参数。
    • 新增了 callObjectEx() 方法。
  • 2020-02-03 1.8
    • 新增了 getNonRouteUrl() 方法。
    • 新增了 setExtra() 方法。
    • 新增了 isPostBack() 方法。
    • 新增了 setIsPostBack() 方法。
    • 对 getUrl() 进行了一些修复。