使用Aura.Router在PHP中进行Web路由

Everyone is interested in SEO-friendly, REST-style URLs. Apache can do URL routing via mod_rewrite rules, but it’s hard and error prone. Why not use PHP itself to handle routing instead?

每个人都对SEO友好的REST风格的URL感兴趣。 Apache可以通过mod_rewrite规则进行URL路由,但这很难并且容易出错。 为什么不使用PHP本身来处理路由呢?

Aura is a independent collection of libraries for PHP 5.4 brought to you by Paul M Jones. Here we are going to introduce you Aura.Router. Aura.Router is a simple and easy web routing library for PHP. In this article you will learn how to create routes which are SEO-friendly, REST-style URLs with the help of PHP.

Aura是Paul M Jones带给您PHP 5.4库的独立集合。 在这里,我们将向您介绍Aura.Router。 Aura.Router是一个用于PHP的简单易用的网络路由库。 在本文中,您将学习如何在PHP的帮助下创建SEO友好的REST风格的URL。

要求和安装 (Requirements and Installation)

We are covering Aura.Router version 1 which requires PHP 5.4+ (the latest version 2 is using PHP 5.3). You can install it in many ways:

我们将介绍Aura.Router版本1,该版本需要PHP 5.4+(最新版本2使用的是PHP 5.3)。 您可以通过多种方式安装它:

  1. Download as a tar ball or zip from GitHub.

    从GitHub下载为tar球zip文件

  2. If you are using git, download it via the command line with:

    如果您使用的是git,请通过以下命令行通过命令行下载:
git clone https://github.com/auraphp/Aura.Router.git

Once you have downloaded Aura.Router, you’ll see a directory with the file structure below:

下载Aura.Router后,您将看到一个目录,其文件结构如下:

alt

All the source files lie in the src directory, and from there follows the PSR-0 Standard for autoloaders. All unit tests reside in the tests directory; you can run the tests by invoking phpunit inside the tests directory (just be sure you have PHPUnit installed).

所有源文件都位于src目录中,并从那里遵循用于自动加载器的PSR-0标准 。 所有单元测试都位于tests目录中; 您可以通过调用tests目录内的phpunit来运行测试(只需确保已安装PHPUnit)。

使用Aura.Router (Working with Aura.Router)

A minimal set of mod_rewrite rules need to be written in .htaccess to point incoming requests to a single entry point.

需要在.htaccess编写最少的mod_rewrite规则集,以将传入请求指向单个入口点。

RewriteEngine On
RewriteRule ^$ index.php [QSA]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,L]

The above rules check for existing files and directories and point all other requests to index.php.

上面的规则检查现有文件和目录,并将所有其他请求指向index.php

Or, if you are using php’s built in server, you can run something like the following:

或者,如果您使用的是php的内置服务器,则可以运行以下内容:

php -S localhost:8080 index.php

The Aura.Router contains four files in the src/Aura/Router folder: Map.php, Route.php, RouteFactory.php and Exception.php. RouteFactory is a factory class to create new Route objects. The RouteFactory contains a newInstance() method, which accepts an associative array. The values are passed to the constructor of Route class.

Aura.Router在src/Aura/Router文件夹中包含四个文件: Map.phpRoute.phpRouteFactory.phpException.phpRouteFactory是一个工厂类,用于创建新的Route对象。 RouteFactory包含一个newInstance()方法,该方法接受一个关联数组。 这些值将传递给Route类的构造函数。

The Route object represents an individual route with a name, path, params, values, etc. You should never need to instantiate a Route directly; you should use RouteFactory or Map instead.

Route对象表示具有名称,路径,参数,值等的单个路由。您永远不需要直接实例化Route ; 您应该改用RouteFactoryMap

The Route and RouteFactory accepts an associative array with the following keys:

RouteRouteFactory接受具有以下键的关联数组:

  • name – a string which is the name for the Route.

    name –一个字符串,它是路由的名称。

  • path – a string which is the path for this Route with param token placeholders.

    path –一个字符串,它是带有参数令牌占位符的该Route的路径。

  • params – an array of params which map tokens to regex subpatterns.

    params –将令牌映射到regex子模式的一组参数。

  • values – an array of default values for params if none are found.

    values –找不到参数的默认值的数组。

  • method – a string or array of HTTP methods; the server REQUEST_METHOD must match one of these values.

    method – HTTP方法的字符串或数组; 服务器REQUEST_METHOD必须与以下值之一匹配。

  • secure – whether the server must use an HTTPS request.

    secure –服务器是否必须使用HTTPS请求。

  • routable – if true, this route can be matched; if not, it can be used only to generate a path.

    routable –如果为true,则可以匹配此路由; 如果不是,则只能用于生成路径。

  • is_match – a callable function to evaluate the route.

    is_match –用于评估路线的可调用函数。

  • generate – a callable function to generate a path.

    generate –生成路径的可调用函数。

  • name_prefix – a string prefix for the name.

    name_prefix –名称的字符串前缀。

  • path_prefix – a string prefix for the path.

    path_prefix –路径的字符串前缀。

Don’t worry too much about the method parameters. I’ll soon show you some examples.

不必太担心方法参数。 我将很快向您展示一些示例。

The Map class is a collection point of URI routes. Map’s constructor accepts a RouteFactory and your routes in a single array of attachment groups. This allows you to separate the configuration and construction of routes.

Map类是URI路由的集合点。 Map的构造函数在单个附件组数组中接受RouteFactory和您的路线。 这使您可以分开路由的配置和构造。

You can add individual routes to a Map object via its add() method, or attach a series of routes using the attach() method. Which ever way you add a route, all route specifications are stored in the Map object’s definitions property which is an array.

您可以通过其add()方法将单个路由添加到Map对象,或使用attach()方法附加一系列路由。 无论采用哪种方式添加路线,所有路线规格都存储在Map对象的definitions属性中,该属性是一个数组。

Route objects are only created when you call the generate() and match() methods. The order in which the objects are created is the order in which they are defined. It will not create all of the Route objects for all of the routes defined. If the route name is same it will generate the route with the generate() method of the Route object. The match() method also applies the same way. The match() method internally calls the Route object’s isMatch() method to know whether the routes are same. If there are previous routes created it will traverse them first in that order. If it’s not found in the created routes, it will create the Route objects for the rest of the routes and look. It’s simple to understand the Map class as it has only 400 lines of commented code. Feel free to take a look at it for more information.

仅当调用generate()match()方法时才创建Route对象。 创建对象的顺序就是定义它们的顺序。 它不会为定义的所有Route创建所有Route对象。 如果路由名称相同,它将使用Route对象的generate()方法generate() Routematch()方法也采用相同的方法。 match()方法在内部调用Route对象的isMatch()方法以了解路由是否相同。 如果创建了以前的路线,它将首先以该顺序遍历。 如果在创建的路由中找不到,它将为其余路由创建Route对象并进行外观。 理解Map类很简单,因为它只有400行注释的代码。 随时查看它以获取更多信息。

基本用法 (Basic Usage)

The easiest way to create an instance of a Map object is to require the file instance.php located in the scripts directory.

创建Map对象实例的最简单方法是要求位于scripts目录中的instance.php文件。

<?php
$map = require "/path/to/Aura.Router/scripts/instance.php";

Alternatively, you can create the object manually, which is just what the instance.php script does anyway.

另外,您也可以手动创建对象,这正是instance.php脚本所做的。

<?php
use AuraRouterMap;
use AuraRouterDefinitionFactory;
use AuraRouterRouteFactory;

$map = new Map(new DefinitionFactory(), new RouteFactory());

Next, you want to add routes to the object using its add() method.

接下来,您要使用对象的add()方法添加路由。

<?php
// add a simple named route without params
$map->add("home", "/");

// add a simple unnamed route with params
$map->add(null, "/{:controller}/{:action}/{:id:(d+)}");

// add a complex named route
$map->add("read", "/blog/read/{:id}{:format}", [
    "params" => [
        "id" => "(d+)",
        "format" => "(..+)?"],
        "values" => [
            "controller" => "blog",
            "action" => "read",
            "format" => "html"
        ]
    ]);

The add() method accepts a route name, path, and associative array. As I mentioned earlier the values contains the default values of the params array. So in the example for the route “read”, you can see the default format is always “html” if none is specified.

add()方法接受路由名称,路径和关联数组。 如前所述,这些值包含params数组的默认值。 因此,在路由“读取”的示例中,如果未指定,则默认格式始终为“ html”。

Are you wondering why we need a default format? For a REST-like application, the controller and the action will be the same. The rendering of the data differs on the accept format. In this way we don’t need to repeat the same code from one action to another. For example, consider the URIs:

您是否想知道为什么我们需要默认格式? 对于类似REST的应用程序,控制器和操作将相同。 数据的呈现方式在接受格式上有所不同。 这样,我们无需从一个动作到另一个动作重复相同的代码。 例如,考虑以下URI:

example.com/blog/read/42.html 
example.com/blog/read/42.json
example.com/blog/read/42.atom

The data we need to output is same for each, but in different formats like json, html, and atom. So if none of the formats appear, for example:

我们每个人需要输出的数据都是相同的,但是格式不同,例如json,html和atom。 因此,如果没有一种格式出现,例如:

example.com/blog/read/42

Then it will assume the request is for HTML.

然后,它将假定请求是针对HTML的。

For a real REST API, file extensions should not be used to indicate the desired format. Clients should be encouraged to utilize HTTP’s Accept request header. To learn more about REST, you can read the REST – Can You do More than Spell It? series here at SitePoint, or the book REST API Design Rulebook Mark Massé.

对于真正的REST API,不应将文件扩展名用于指示所需的格式。 应鼓励客户端使用HTTP的Accept请求标头。 要了解有关REST的更多信息,您可以阅读REST –您可以做更多的事情吗? 可以在SitePoint上找到该系列,或者在REST API设计规则书MarkMassé中找到这本书。

匹配路线 (Matching a Route)

Once the routes have been added, you’ll want to recognize which route has been requested by a user. This is possible with the help the match() method of the Map object. Internally the Map object is calling the isMatch() method of the Route object. For the match method, you need to pass the path and the $_SERVER value as shown below:

添加路线后,您将要识别用户请求的路线。 这可以通过Map对象的match()方法来实现。 在内部, Map对象正在调用Route对象的isMatch()方法。 对于match方法,您需要传递路径和$_SERVER值,如下所示:

<?php
// get the route
$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$route = $map->match($path, $_SERVER);

You may have wondered why we need to pass the path and also the server values. Why can’t Aura.Router get the path itself from the $_SERVER array you have passed? It’s for some flexibility.

您可能想知道为什么我们需要传递路径以及服务器值。 为什么Aura.Router无法从您传递的$_SERVER数组中获取路径本身? 这是为了提高灵活性。

  1. The match() method does not parse the URI or use $_SERVER internally. This is because different systems may have different ways of representing that information (e.g., through a URI object or a context object). As long as you can pass the string path and a server array, you can use Aura.Router in your application foundation or framework.

    match()方法不解析URI或在内部使用$_SERVER 。 这是因为不同的系统可能具有表示该信息的不同方式(例如,通过URI对象或上下文对象)。 只要您可以传递字符串路径和服务器数组,就可以在应用程序基础或框架中使用Aura.Router。

  2. Sometimes the URI may be like http://example.com/base/path/controller/action/param, and here we have a basepath. So before we look for the match method, we need to remove the basepath.

    有时URI可能类似于http://example.com/base/path/controller/action/param ,这里我们有一个基本路径。 因此,在寻找match方法之前,我们需要删除基本路径。

  3. In some cases you may be getting the PATH_INFO from REDIRECT_PATH_INFO when your server can understand only /index.php/home/index, and not by /home/index.

    在某些情况下,当服务器只能理解/index.php/home/index而不是/home/index时,您可能会从REDIRECT_PATH_INFO获取PATH_INFO

If a match is found, the method returns an instance of a Route object, otherwise it returns false. Once you have obtained the object you can access its values as such:

如果找到匹配项,则该方法返回Route对象的实例,否则返回false。 获得对象后,可以按以下方式访问其值:

<?php
$route->values["controller"];
$route->values["action"];
$route->values["id"];

It’s from the $route->values we know what sort of rendering, what sort of class, what sort of method we want to call for dispatching.

通过$route->values我们知道要调用哪种类型的渲染,哪种类,哪种方法。

调度路线 (Dispatching a Route)

If a route is found, it’s easy for you to create the controller object and an appropriate method. This simple example is adapted from the Aura docs:

如果找到路由,则可以轻松创建控制器对象和适当的方法。 这个简单的例子改编自Aura文档:

<?php
if (!$route) {
    // no route object was returned, You can also set error controller depending on your logic
echo "No application route was found for that URI path.";
exit();
}

// does the route indicate a controller?
if (isset($route->values["controller"])) {
    // take the controller class directly from the route
    $controller = $route->values["controller"];
}
else {
    // use a default controller
    $controller = "Index";
}

// does the route indicate an action?
if (isset($route->values["action"])) {
// take the action method directly from the route
    $action = $route->values["action"];
}
else {
// use a default action
    $action = "index";
}

// instantiate the controller class
$page = new $controller();

// invoke the action method with the route values
echo $page->$action($route->values);

微框架路由 (Micro-Framework Routing)

Sometimes you may wish to use Aura as a micro-framework. It’s also possible to assigning anonymous function to controller:

有时您可能希望将Aura用作微框架。 也可以为控制器分配匿名函数:

<?php
$map->add("read", "/blog/read/{:id}{:format}", [
	"params" => [
		"id" => "(d+)",
		"format" => "(..+)?",
	],
	"values" => [
		"controller" => function ($args) {
			$id = (int) $args["id"];
			return "Reading blog ID {$id}";
		},
		"format" => ".html",
	],
));

When you are using Aura.Router as a micro-framework, the dispatcher will look something similar to the one below:

当您将Aura.Router用作微框架时,调度程序将看起来类似于以下内容:

<?php
$params = $route->values;
$controller = $params["controller"];
unset($params["controller"]);
echo $controller($params);

生成路径 (Generating A Route Path)

You may want to generate the route in your view. This is possible with the help of the map’s generate() method. The map generate() method internally calls the generate() method of Route object.

您可能需要在视图中生成路线。 这可以借助地图的generate()方法实现。 映射的generate()方法在内部调用Route对象的generate()方法。

<?php
// $path => "/blog/read/42.atom"
$path = $map->generate("read", [
    "id" => 42,
    "format" => ".atom"
]);

$href = htmlspecialchars($path, ENT_QUOTES, "UTF-8");
echo '<a href="' . $href . '">Atom feed for this blog entry</a>';

Of course typing “blog/read/42.atom” is shorter, but hardcoded paths are less flexible and makes changing the route more difficult. Consider the following example:

当然,键入“ blog / read / 42.atom”的时间较短,但是硬编码路径的灵活性较差,并且更改路由也更加困难。 考虑以下示例:

<?php
$map->add("read", "/blog/read/{:id}{:format}", [
    "params" => [
        "id" => "(d+)",
        "format" => "(..+)?"],
        "values" => [
            "controller" => "blog",
            "action" => "read",
           "format" => "html"
        ]
    ]);

What will happen when you want to change the /blog/read/42.atom to /article/view/42.atom? Or, what will happen when your client mentions they want to move to a multilingual website? If you have hard-coded paths, you’ll probably have to change them in many places. The generate() method always comes handy.

当你想改变,会发生什么/blog/read/42.atom/article/view/42.atom ? 或者,当您的客户提到他们想转到多语言网站时会发生什么? 如果您具有硬编码的路径,则可能必须在许多地方进行更改。 generate()方法总是很方便。

The add() method’s second argument is an associate array. You can pass callable functions with is_match keys which can either return true or false. And depending on the value, it will return the routes when the match() method is called. For example:

add()方法的第二个参数是关联数组。 您可以使用is_match键传递可调用的函数,这些键可以返回true或false。 并且根据值,在调用match()方法时它将返回路由。 例如:

<?php
$map->add("read", "/blog/read/{:id}{:format}", [
    "params" => [
        "id" => "(d+)",
        "format" => "(..+)?",
    ],
    "values" => [
        "controller" => "blog",
        "action" => "read",
        "id" => 1,
        "format" => ".html",
    ],
    "secure" => false,
    "method" => ["GET"],
    "routable" => true,
    "is_match" => function(array $server, ArrayObject $matches) {
        // disallow matching if referred from example.com
        if ($server["HTTP_REFERER"] == "http://example.com") {
            return false;
        }

        // add the referer from $server to the match values
        $matches["referer"] = $server["HTTP_REFERER"];
        return true;
    },
    "generate" => function(AuraRouterRoute $route, array $data) {
        $data["foo"] = "bar";
        return $data;
    }
]);

Here if the HTTP_REFERER is example.com we will fail to load the content. You can pass your own callable functions like above. This makes Aura.Router more flexible.

如果HTTP_REFERERexample.com我们将无法加载内容。 您可以像上面那样传递自己的可调用函数。 这使Aura.Router更加灵活。

结论 (Conclusion)

In this article we have covered some of the basic and advanced features of using Aura.Router for web routing. Why not give a try to Aura.Router? I’m sure it will make your life with web routing easier than before. Have a look at http://auraphp.github.com/Aura.Router as it has more to say than we have in the current article.

在本文中,我们介绍了使用Aura.Router进行Web路由的一些基本和高级功能。 为什么不尝试Aura.Router? 我敢肯定,这将使您的网络路由工作比以往更加轻松。 看看http://auraphp.github.com/Aura.Router,因为它比我们在当前文章中所说的更多。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/web-routing-in-php-with-aura-router/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值