PHP 应用程序 URL 路由

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/125677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:34:11  来源:igfitidea点击:

PHP Application URL Routing

phpurlroutingurl-routing

提问by Mez

So I'm writing a framework on which I want to base a few apps that I'm working on (the framework is there so I have an environment to work with, and a system that will let me, for example, use a single sign-on)

所以我正在编写一个框架,我想在该框架上建立一些我正在开发的应用程序(该框架在那里,所以我有一个可以使用的环境,以及一个系统,例如,可以让我使用单个签到)

I want to make this framework, and the apps it has use a Resource Oriented Architecture.

我想制作这个框架,它的应用程序使用面向资源的架构。

Now, I want to create a URL routing class that is expandable by APP writers (and possibly also by CMS App users, but that's WAYYYY ahead in the future) and I'm trying to figure out the best way to do it by looking at how other apps do it.

现在,我想创建一个可由 APP 编写者扩展的 URL 路由类(也可能由 CMS 应用程序用户扩展,但这是未来的 WAYYYY),我试图通过查看来找出最好的方法来做到这一点其他应用程序是如何做到的。

采纳答案by gradbot

I prefer to use reg ex over making my own format since it is common knowledge. I wrote a small class that I use which allows me to nest these reg ex routing tables. I use to use something similar that was implemented by inheritance but it didn't need inheritance so I rewrote it.

我更喜欢使用 reg ex 而不是制作我自己的格式,因为它是常识。我写了一个我使用的小类,它允许我嵌套这些 reg ex 路由表。我曾经使用过类似的东西,它是通过继承实现的,但它不需要继承,所以我重写了它。

I do a reg ex on a key and map to my own control string. Take the below example. I visit /api/related/joeand my router class creates a new object ApiControllerand calls it's method relatedDocuments(array('tags' => 'joe'));

我对一个键执行正则表达式并映射到我自己的控制字符串。以下面的例子为例。我访问/api/related/joe并且我的路由器类创建了一个新对象ApiController并调用它的方法relatedDocuments(array('tags' => 'joe'));

// the 12 strips the subdirectory my app is running in
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12)); 

Route::process($index, array(
    "#^api/related/(.*)$#Di"    => "ApiController/relatedDocuments/tags",

    "#^thread/(.*)/post$#Di"    => "ThreadController/post/title",
    "#^thread/(.*)/reply$#Di"   => "ThreadController/reply/title",
    "#^thread/(.*)$#Di"         => "ThreadController/thread/title",

    "#^ajax/tag/(.*)/(.*)$#Di"  => "TagController/add/id/tags",
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
    "#^ajax/reply/(.*)$#Di"     => "ArticleController/newReply/id",
    "#^ajax/toggle/(.*)$#Di"    => "ApiController/toggle/toggle",

    "#^$#Di"                    => "HomeController",
));

In order to keep errors down and simplicity up you can subdivide your table. This way you can put the routing table into the class that it controls. Taking the above example you can combine the three thread calls into a single one.

为了减少错误和简化,您可以细分您的表格。这样你就可以把路由表放到它控制的类中。以上面的示例为例,您可以将三个线程调用合并为一个。

Route::process($index, array(
    "#^api/related/(.*)$#Di"    => "ApiController/relatedDocuments/tags",

    "#^thread/(.*)$#Di"         => "ThreadController/route/uri",

    "#^ajax/tag/(.*)/(.*)$#Di"  => "TagController/add/id/tags",
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
    "#^ajax/reply/(.*)$#Di"     => "ArticleController/newReply/id",
    "#^ajax/toggle/(.*)$#Di"    => "ApiController/toggle/toggle",

    "#^$#Di"                    => "HomeController",
));

Then you define ThreadController::route to be like this.

然后你将 ThreadController::route 定义为这样。

function route($args) {
    Route::process($args['uri'], array(
        "#^(.*)/post$#Di"    => "ThreadController/post/title",
        "#^(.*)/reply$#Di"   => "ThreadController/reply/title",
        "#^(.*)$#Di"         => "ThreadController/thread/title",
    ));
}

Also you can define whatever defaults you want for your routing string on the right. Just don't forget to document them or you will confuse people. I'm currently calling index if you don't include a function name on the right. Hereis my current code. You may want to change it to handle errors how you like and or default actions.

您还可以为右侧的路由字符串定义所需的任何默认值。只是不要忘记记录它们,否则你会混淆人们。如果您没有在右侧包含函数名称,我目前正在调用 index 。是我当前的代码。您可能希望更改它以处理您喜欢的错误和/或默认操作。

回答by gradbot

Yet another framework? -- anyway...

又一个框架? - 反正...

The trick is with routing is to pass it all over to your routing controller.

路由的诀窍是将其全部传递给您的路由控制器。

You'd probably want to use something similar to what I've documented here:

您可能想要使用类似于我在此处记录的内容:

http://www.hm2k.com/posts/friendly-urls

http://www.hm2k.com/posts/friendly-urls

The second solution allows you to use URLs similar to Zend Framework.

第二种解决方案允许您使用类似于 Zend Framework 的 URL。

回答by Mez

Use a list of Regexs to match which object I should be using

使用正则表达式列表来匹配我应该使用的对象

For example

例如

^/users/[\w-]+/bookmarks/(.+)/$
^/users/[\w-]+/bookmarks/$
^/users/[\w-]+/$

Pros: Nice and simple, lets me define routes directly Cons: Would have to be ordered, not making it easy to add new things in (very error prone)

优点:很好很简单,让我直接定义路由缺点:必须订购,不容易添加新东西(很容易出错)

This is, afaik, how Django does it

这是,afaik,Django 是如何做到的

回答by Unlabeled Meat

I think a lot of frameworks use a combination of Apache's mod_rewrite and a front controller. With mod_rewrite, you can turn a URL like this: /people/get/3 into this: index.php?controller=people&method=get&id=3. Index.php would implement your front controller which routes the page request based on the parameters given.

我认为很多框架使用 Apache 的 mod_rewrite 和前端控制器的组合。使用 mod_rewrite,您可以将像这样的 URL:/people/get/3 变成这样:index.php?controller=people&method=get&id=3。Index.php 将实现您的前端控制器,它根据给定的参数路由页面请求。

回答by Gindi Bar Yahav

As you might expect, there are a lot of ways to do it.

正如您所料,有很多方法可以做到。

For example, in Slim Framework, an example of the routing engine may be the folllowing (based on the pattern ${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})):

例如,在Slim Framework 中,路由引擎的示例可能如下(基于模式${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})):

$app->get("/Home", function() {
    print('Welcome to the home page');
}

$app->get('/Profile/:memberName', function($memberName) {
    print( 'I\'m viewing ' . $memberName . '\'s profile.' );
}

$app->post('/ContactUs', function() {
    print( 'This action will be fired only if a POST request will occure');
}

So, the initialized instance ($app) gets a method per request method (e.g. get, post, put, delete etc.) and gets a route as the first parameter and callback as the second.

因此,初始化的实例 ( $app) 为每个请求方法(例如 get、post、put、delete 等)获取一个方法,并获取一个路由作为第一个参数和回调作为第二个参数。

The route can get tokens - which is "variable" that will change at runtime based on some data (such as member name, article id, organization location name or whatever - you know, just like in every routing controller).

路由可以获得令牌——这是“变量”,它会在运行时根据一些数据(例如成员名称、文章 ID、组织位置名称或其他任何东西——你知道,就像在每个路由控制器中一样)而改变。

Personally, I do like this way but I don't think it will be flexible enough for an advanced framework.

就个人而言,我确实喜欢这种方式,但我认为它对于高级框架来说不够灵活。

Since I'm working currently with ZF and Yii, I do have an example of a router I've created as part of a framework to a company I'm working for:

由于我目前正在与 ZF 和 Yii 合作,因此我确实有一个路由器示例,该示例是我为我工作的公司创建的框架的一部分:

The route engine is based on regex (similar to @gradbot's one) but got a two-way conversation, so if a client of yours can't run mod_rewrite (in Apache) or add rewrite rules on his or her server, he or she can still use the traditional URLs with query string.

路由引擎基于正则表达式(类似于@gradbot 的)但有双向对话,因此如果您的客户端无法运行 mod_rewrite(在 Apache 中)或在他或她的服务器上添加重写规则,他或她仍然可以使用带有查询字符串的传统 URL。

The file contains an array, each of it, each item is similar to this example:

该文件包含一个数组,每个数组,每一项都类似于这个例子:

$_FURLTEMPLATES['login']    =   array(
    'i' => array( // Input - how the router parse an incomming path into query string params
        'pattern' => '@Members/Login/?@i',
        'matches' => array( 'Application' => 'Members', 'Module' => 'Login' ),
    ),
    'o' => array( // Output - how the router parse a query string into a route
        '@Application=Members(&|&)Module=Login/?@' => 'Members/Login/'
    )
);

You can also use more complex combinations, such as:

您还可以使用更复杂的组合,例如:

$_FURLTEMPLATES['article']  =   array(
    'i' => array(
        'pattern' => '@CMS/Articles/([\d]+)/?@i',
        'matches' => array( 'Application' => "CMS",
            'Module' => 'Articles',
            'Sector' => 'showArticle',
            'ArticleID' => '' ),
    ),
    'o' => array(
     '@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/'
    )
);

The bottom line, as I think, is that the possibilities are endless, it just depend on how complex you wish your framework to be and what you wish to do with it.

我认为最重要的是,可能性是无穷无尽的,这取决于您希望框架有多复杂以及您希望用它做什么。

If it is, for example, just intended to be a web service or simple website wrapper - just go with Slim framework's style of writing - very easy and good-looking code.

例如,如果它只是打算成为一个 Web 服务或简单的网站包装器 - 只需使用 Slim 框架的编写风格 - 非常简单且美观的代码。

However, if you wish to develop complex sites using it, I think regex is the solution.

但是,如果您希望使用它开发复杂的站点,我认为正则表达式是解决方案。

Good luck! :)

祝你好运!:)

回答by c9s

You should check out Pux https://github.com/c9s/Pux

你应该看看 Pux https://github.com/c9s/Pux

Here is the synopsis

这是概要

<?php
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class.
use Pux\Executor;

class ProductController {
    public function listAction() {
        return 'product list';
    }
    public function itemAction($id) { 
        return "product $id";
    }
}
$mux = new Pux\Mux;
$mux->any('/product', ['ProductController','listAction']);
$mux->get('/product/:id', ['ProductController','itemAction'] , [
    'require' => [ 'id' => '\d+', ],
    'default' => [ 'id' => '1', ]
]);
$mux->post('/product/:id', ['ProductController','updateAction'] , [
    'require' => [ 'id' => '\d+', ],
    'default' => [ 'id' => '1', ]
]);
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [
    'require' => [ 'id' => '\d+', ],
    'default' => [ 'id' => '1', ]
]);
$route = $mux->dispatch('/product/1');
Executor::execute($route);

回答by Michael Johnson

Zend's MVC framework by default uses a structure like

Zend 的 MVC 框架默认使用类似的结构

/router/controller/action/key1/value1/key2/value2

where routeris the router file (mapped via mod_rewrite, controlleris from a controller action handler which is defined by a class that derives from Zend_Controller_Actionand actionreferences a method in the controller, named actionAction. The key/value pairs can go in any order and are available to the action method as an associative array.

router路由器文件在哪里(通过 映射mod_rewritecontroller来自控制器动作处理程序,该处理程序由一个类定义,该类派生Zend_Controller_Actionaction引用控制器中的一个方法,名为actionAction。键/值对可以按任何顺序排列并且可用于动作方法作为关联数组。

I've used something similar in the past in my own code, and so far it's worked fairly well.

过去我在自己的代码中使用过类似的东西,到目前为止效果很好。

回答by michal kralik

Try taking look at MVCpattern.
Zend Framework uses it for example, but also CakePHP, CodeIgniter, ...

尝试看看MVC模式。
例如,Zend Framework 使用它,但也使用 CakePHP、CodeIgniter、...

Me personally don't like the MVC model, but it's most of the time implemented as "View for web" component.

我个人不喜欢 MVC 模型,但它大部分时间实现为“Web 视图”组件。

The decision pretty much depends on preference...

决定在很大程度上取决于偏好......