PHP应用程序URL路由

时间:2008-09-24 06:20:59

标签: php url routing url-routing

所以我正在编写一个框架,我想在其上建立一些我正在研究的应用程序(框架就在那里,所以我有一个可以使用的环境,以及一个让我可以使用的系统,例如,使用单点登录)

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

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

8 个答案:

答案 0 :(得分:13)

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

我在一个键上做一个注册表并映射到我自己的控制字符串。以下面的例子为例。我访问/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",
));

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

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

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

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

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

答案 1 :(得分:2)

又一个框架? - 无论如何......

诀窍在于路由是将它全部传递给路由控制器。

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

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

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

答案 2 :(得分:1)

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

例如

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

优点:简单明了,让我直接定义路线 缺点:必须订购,不要轻易添加新内容(非常容易出错)

afaik,Django是如何做到的

答案 3 :(得分:0)

我认为很多框架都使用了Apache的mod_rewrite和前端控制器的组合。使用mod_rewrite,您可以将以下URL:/ people / get / 3转换为: ?的index.php控制器=人及放大器;包含method = get和ID = 3。 Index.php将实现您的前端控制器,它根据给定的参数路由页面请求。

答案 4 :(得分:0)

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

例如,在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');
}

因此,初始化实例($app)获取每个请求方法的方法(例如,获取,发布,放置,删除等),并获取路由作为第一个参数,并将回调作为第二个参数。

路由可以获得令牌 - 这是“变量”,它将在运行时根据某些数据(例如成员名称,文章ID,组织位置名称或其他任何内容而更改 - 您知道,就像在每个路由控制器中一样)。 / p>

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

由于我目前正在使用ZF和Yii,我确实有一个路由器的例子,我已经为我正在为之工作的公司创建了框架的一部分:

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

该文件包含一个数组,每个数组,每个项目与此示例类似:

$_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/'
    )
);

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

$_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'
    )
);

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

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

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

祝你好运! :)

答案 5 :(得分:0)

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

这是概要

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

答案 6 :(得分:-1)

Zend的MVC框架默认使用类似

的结构
/router/controller/action/key1/value1/key2/value2

其中router是路由器文件(通过mod_rewrite映射,controller来自控制器操作处理程序,该处理程序由派生自Zend_Controller_Action和{{的类定义1}}引用控制器中一个名为action的方法。键/值对可以按任何顺序排列,并作为关联数组用于action方法。

我在我自己的代码中使用了类似的东西,到目前为止,它的效果相当不错。

答案 7 :(得分:-4)

尝试查看MVC模式 Zend Framework使用它,例如CakePHP,CodeIgniter,......

我个人不喜欢MVC模型,但它大部分时间是作为“View for web”组件实现的。

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