简体   繁体   English

PHP应用程序URL路由

[英]PHP Application URL Routing

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 App用户可扩展的,但是未来的WAYYYY也是如此),我试图找出最好的方法来实现它其他应用如何做到这一点。

I prefer to use reg ex over making my own format since it is common knowledge. 我更喜欢使用reg ex来制作我自己的格式,因为它是常识。 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. 我使用类似于继承实现的东西,但它不需要继承,所以我重写了它。

I do a reg ex on a key and map to my own control string. 我在一个键上做一个注册表并映射到我自己的控制字符串。 Take the below example. 以下面的例子为例。 I visit /api/related/joe and my router class creates a new object ApiController and 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. 如果你没有在右边包含一个函数名,我正在调用index。 Here is my current code. 是我目前的代码。 You may want to change it to handle errors how you like and or default actions. 您可能希望更改它以处理您喜欢的错误和/或默认操作。

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。

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 这就是Django如何做到的

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 (eg get, post, put, delete etc.) and gets a route as the first parameter and callback as the second. 因此,初始化实例( $app )获取每个请求方法的方法(例如,获取,发布,放置,删除等)并获取路由作为第一个参数,并将回调作为第二个参数。

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' => '$1' ),
    ),
    'o' => array(
     '@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/$4'
    )
);

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! 祝好运! :) :)

I think a lot of frameworks use a combination of Apache's mod_rewrite and a front controller. 我认为很多框架都使用了Apache的mod_rewrite和前端控制器的组合。 With mod_rewrite, you can turn a URL like this: /people/get/3 into this: index.php?controller=people&method=get&id=3. 使用mod_rewrite,您可以将这样的URL:/ people / get / 3转换为: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. Index.php将实现您的前端控制器,它根据给定的参数路由页面请求。

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);

Zend's MVC framework by default uses a structure like Zend的MVC框架默认使用类似的结构

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

where router is the router file (mapped via mod_rewrite , controller is from a controller action handler which is defined by a class that derives from Zend_Controller_Action and action references 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_rewrite映射, controller来自一个控制器动作处理程序,它由一个派生自Zend_Controller_Action的类定义, action引用控制器中的一个方法,名为actionAction 。键/值对可以按任何顺序排列并且作为关联数组可用于action方法。

I've used something similar in the past in my own code, and so far it's worked fairly well. 我在我自己的代码中使用了类似的东西,到目前为止它的工作得相当好。

Try taking look at MVC pattern. 试着看一下MVC模式。
Zend Framework uses it for example, but also CakePHP, CodeIgniter, ... 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模型,但它大部分时间都是作为“View for web”组件实现的。

The decision pretty much depends on preference... 这个决定很大程度上取决于偏好......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM