转载

PHP框架路由大比拼:ThinkPHP vs ZF2 vs Yaf vs Laravel

前言

读过一篇关于Zend Framework2的技术文章 《ZF2多级树形路由Route配置实例》 ,是介绍路由配置的。我觉得很有意思,这是的需求:

  • /user 对应用户列表页面
  • /user/:user_id 对应用户的个人主页,比如 /user/AlloVince 就对应AlloVince用户的个人主页
  • /user/:user_id/blog/ 对应用户的博客列表页面,比如 /user/AlloVince/blog 就会列出AlloVince写过的Blog
  • /user/:user_id/blog/:blog_id 对应用户的一篇博客文章

方案引用自原文:

php
'router' => array( 'routes' => array( 'user' => array( 'type' => 'Segment', 'options' => array( 'route' => '/user[/]', 'defaults' => array( 'controller' => 'UserController', 'action' => 'index', ), ), 'may_terminate' => true, 'child_routes' => array( 'profile' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:id][/]', 'constraints' => array( 'id' => '[a-zA-Z0-9_-]+' ), 'defaults' => array( 'action' => 'get' ), ), 'may_terminate' => true, 'child_routes' => array( 'blog' => array( 'type' => 'Segment', 'options' => array( 'route' => 'blog[/]', 'constraints' => array( ), 'defaults' => array( 'action' => 'blog' ) ), 'may_terminate' => true, 'child_routes' => array( 'post' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:post_id][/]', 'constraints' => array( 'post_id' => '[a-zA-Z0-9_-]+' ), 'defaults' => array( 'action' => 'post' ) ), 'may_terminate' => true, ), ), ), ), //profile child_routes end ), //profile end ), //user child_routes end ), //user end ), ),

看了这篇文章后,我打算使用我用过的PHP框架来实现这个路由需求。

ThinkPHP

新建一个ThinkPHP项目:

composer create-project topthink/thinkphp tp --prefer-dist

命令行显示我安装的是3.2.2

Installing topthink/thinkphp (3.2.2)

我看 ThinkPHP官网 最新稳定版本是3.2.3。

我特意去 packagist官网 查了一下,库中稳定版确实是3.2.2。

我得使用3.2.3。为什么我特别纠结这一点哩?因为:

3.2的路由功能是针对模块设置的,所以URL中的模块名不能被路由,路由定义也通常是放在模块配置文件中。 3.2.3版本开始增加全局路由定义支持,可以在项目的公共配置文件中定义路由。

也就是说,路由重写的部分是Controller和Action部分,Moudle还是存在。

我希望的是 /user ,而不是 home/user 。(ThinkPHP中默认Module是Home, 'DEFAULT_MODULE' => 'Home' ,可以修改)

当然,这个问题也可以修改.htaccess文件的解决。但是,我还是决定安装3.2.3。

在 ThinkPHP官网 下载最新的包,解压。

使用浏览器访问一下项目的入口文件,让ThinkPHP自动生成了一个默认的应用模块Home。

修改公共配置文件 tp/Application/Common/Conf/config.php

php<?php return array(  // 开启路由  'URL_ROUTER_ON' => true,  // URL访问模式,可选参数0、1、2、3,代表以下四种模式:  // 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE  模式); 3 (兼容模式)  默认为PATHINFO 模式  'URL_MODEL' => 2,  // URL伪静态后缀设置,为空表示可以支持所有的静态后缀  // 使用U函数生成URL时会不带后缀  'URL_HTML_SUFFIX' => '',  // URL变量绑定到Action方法参数,默认为true  'URL_PARAMS_BIND' => true,  // URL变量绑定的类型 0 按变量名绑定 1 按变量顺序绑定,默认为0  'URL_PARAMS_BIND_TYPE' => 0,  // 路由配置  'URL_ROUTE_RULES' => array(   '/^url$/' => 'Home/User/url',   '/^user$/' => 'Home/User/index',   '/^user//([a-zA-Z0-9_-]+)$/' => 'Home/User/show?name=:1',   '/^user//([a-zA-Z0-9_-]+)//blog$/' => 'Home/Blog/index?name=:1',   '/^user//([a-zA-Z0-9_-]+)//blog//([0-9]+)$/' => 'Home/Blog/show?name=:1&blog_id=:2',  ), ); ?>  

创建文件 tp/Application/Home/Controller/UserController.class.php

php<?php namespace Home/Controller; use Think/Controller; class UserController extends Controller {  public function url() {   $name = 'jing';   $blogId = 1;   $urls = array(    U('/user'),    U("/user/{$name}"),    U("/user/{$name}/blog"),    U("/user/{$name}/blog/{$blogId}"),   );   foreach ($urls as $url) {    echo "<a href=/"{$url}/">{$url}<a/><br />/n";   }  }  public function index() {   echo '我是用户列表^_^';  }  public function show($name) {   echo "欢迎你,{$name}";  } } ?>  

创建文件 tp/Application/Home/Controller/BlogController.class.php

php<?php namespace Home/Controller; use Think/Controller; class BlogController extends Controller {  public function index($name) {   echo "这是{$name}的博客列表";  }  public function show($blog_id, $name) {   echo "{$name}的这篇博客的id为{$blog_id}";  } } ?>  

访问: http://127.0.0.1/tp/url

输出:

html<a href="/tp/user">/tp/user<a/><br /> <a href="/tp/user/jing">/tp/user/jing<a/><br /> <a href="/tp/user/jing/blog">/tp/user/jing/blog<a/><br /> <a href="/tp/user/jing/blog/1">/tp/user/jing/blog/1<a/><br /> 

访问上面4个链接,依次返回:

html我是用户列表^_^ 欢迎你,jing 这是jing的博客列表 jing的这篇博客的id为1 

下面其他框架,也同样输出以上内容。

Zend Framework 2

使用ZF2骨架程序创建一个ZF2项目:

composer create-project --stability=&quot;dev&quot; zendframework/skeleton-application zf2

修改默认模块Application的配置文件 zf2/module/Application/config/module.config.php

php<?php /**  * Zend Framework (http://framework.zend.com/)  *  * @link   http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository  * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)  * @license   http://framework.zend.com/license/new-bsd New BSD License  */ return array(  'router' => array(   'routes' => array(    'home' => array(     'type' => 'Zend/Mvc/Router/Http/Literal',     'options' => array(      'route' => '/url',      'defaults' => array(       'controller' => 'Application/Controller/User',       'action' => 'url',      ),     ),    ),    // The following is a route to simplify getting started creating    // new controllers and actions without needing to create a new    // module. Simply drop new controllers in, and you can access them    // using the path /application/:controller/:action    'application' => array(     'type' => 'Literal',     'options' => array(      'route' => '/application',      'defaults' => array(       '__NAMESPACE__' => 'Application/Controller',       'controller' => 'Index',       'action' => 'index',      ),     ),     'may_terminate' => true,     'child_routes' => array(      'default' => array(       'type' => 'Segment',       'options' => array(        'route' => '/[:controller[/:action]]',        'constraints' => array(         'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',         'action' => '[a-zA-Z][a-zA-Z0-9_-]*',        ),        'defaults' => array(        ),       ),      ),     ),    ),    'user_list' => array(     'type' => 'Segment',     'options' => array(      'route' => '/user[/]',      'defaults' => array(       '__NAMESPACE__' => 'Application/Controller',       'controller' => 'User',       'action' => 'index',      ),     ),     'may_terminate' => true,     'child_routes' => array(      'user' => array(       'type' => 'Segment',       'options' => array(        'route' => '[:name][/]',        'constraints' => array(         'name' => '[a-zA-Z0-9_-]+',        ),        'defaults' => array(         'action' => 'show',        ),       ),       'may_terminate' => true,       'child_routes' => array(        'blog_list' => array(         'type' => 'Segment',         'options' => array(          'route' => 'blog[/]',          'constraints' => array(          ),          'defaults' => array(           'controller' => 'Blog',           'action' => 'index',          )         ),         'may_terminate' => true,         'child_routes' => array(          'blog' => array(           'type' => 'Segment',           'options' => array(            'route' => '[:blog_id]',            'constraints' => array(             'blog_id' => '[0-9]+',            ),            'defaults' => array(             'action' => 'show',            )           ),           'may_terminate' => true,          ),         ),        ),       ),      ),     ),    ),   ),  ),  'service_manager' => array(   'abstract_factories' => array(    'Zend/Cache/Service/StorageCacheAbstractServiceFactory',    'Zend/Log/LoggerAbstractServiceFactory',   ),   'aliases' => array(    'translator' => 'MvcTranslator',   ),  ),  'translator' => array(   'locale' => 'en_US',   'translation_file_patterns' => array(    array(     'type' => 'gettext',     'base_dir' => __DIR__ . '/../language',     'pattern' => '%s.mo',    ),   ),  ),  'controllers' => array(   'invokables' => array(    'Application/Controller/Index' => 'Application/Controller/IndexController',    'Application/Controller/User' => 'Application/Controller/UserController',    'Application/Controller/Blog' => 'Application/Controller/BlogController',   ),  ),  'view_manager' => array(   'display_not_found_reason' => true,   'display_exceptions' => true,   'doctype' => 'HTML5',   'not_found_template' => 'error/404',   'exception_template' => 'error/index',   'template_map' => array(    'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',    'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',    'error/404' => __DIR__ . '/../view/error/404.phtml',    'error/index' => __DIR__ . '/../view/error/index.phtml',   ),   'template_path_stack' => array(    __DIR__ . '/../view',   ),  ),  // Placeholder for console routes  'console' => array(   'router' => array(    'routes' => array(    ),   ),  ), ); ?>  

这个文件是骨架程序中自带的,我只是修改了 router 部分和 controllers 部分。要我写这么长的文件,那就太为难我了。这也是 ZF官方 发布了一个骨架程序的原因。

创建文件 zf2/module/Application/src/Application/Controller/UserController.php

php<?php namespace Application/Controller; use Zend/Mvc/Controller/AbstractActionController; use Zend/View/Model/ViewModel; class UserController extends AbstractActionController {     public function urlAction() {  $name = 'jing';  $blogId = 1;  $urls = array(      $this->url()->fromRoute('user_list'),      $this->url()->fromRoute('user_list/user', array('name' => $name)),      $this->url()->fromRoute('user_list/user/blog_list', array('name' => $name)),      $this->url()->fromRoute('user_list/user/blog_list/blog', array('name' => $name, 'blog_id' => $blogId)),  );  $view = new ViewModel(compact('urls'));  $view->setTerminal(true);  return $view;     }     public function indexAction() {  $view = new ViewModel();  // 禁用布局模板  $view->setTerminal(true);  return $view;     }     public function showAction() {  $username = $this->params()->fromRoute('name');  $view = new ViewModel(compact('username'));  $view->setTerminal(true);  return $view;     } } ?>  

创建文件 zf2/module/Application/src/Application/Controller/BlogController.php

php<?php namespace Application/Controller; use Zend/Mvc/Controller/AbstractActionController; use Zend/View/Model/ViewModel; class BlogController extends AbstractActionController {     public function indexAction() {  $username = $this->params()->fromRoute('name');  $view = new ViewModel(compact('username'));  $view->setTerminal(true);  return $view;     }     public function showAction() {  $username = $this->params()->fromRoute('name');  $blogId = $this->params()->fromRoute('blog_id');  $view = new ViewModel(compact('username', 'blogId'));  $view->setTerminal(true);  return $view;     } } ?>  

zf2不支持Action参数绑定,ThinkPHP不仅支持绑定,还支持2种绑定方式:按变量名绑定和按变量顺序绑定。

zf2中Action必须得返回视图,除非 exit() 。如果你知道可以禁用视图的办法,请告诉我。

创建文件 zf2/module/Application/view/application/user/url.phtml

php<?php foreach ($urls as $url): ?> <a href="<?php echo $url;?>"><?php echo $url;?><a/><br /> <?php endforeach; ?> 

创建文件 zf2/module/Application/view/application/user/index.phtml

php我是用户列表^_^ 

创建文件 zf2/module/Application/view/application/user/show.phtml

php欢迎你,<?php echo $username; ?> 

创建文件 zf2/module/Application/view/application/blog/index.phtml

php这是<?php echo $username; ?>的博客列表 

创建文件 zf2/module/Application/view/application/blog/show.phtml

php<?php echo $username; ?>的这篇博客的id为<?php echo $blogId; ?> 

Yaf

安装Yaf

使用代码生成工具创建Yaf项目

修改启动文件 yaf/application/Bootstrap.php ,修改其中的 _initRoute 方法:

php  $router = Yaf_Dispatcher::getInstance()->getRouter();   $route0 = new Yaf_Route_Rewrite('url', array(    'controller' => 'User',    'action' => 'url',     ), array()   );   $route1 = new Yaf_Route_Rewrite('user', array(    'controller' => 'User',    'action' => 'index',     ), array()   );   $route2 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)#', array(    'controller' => 'User',    'action' => 'show',     ), array(1 => 'name',)   );   $route3 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)/blog#', array(    'controller' => 'Blog',    'action' => 'index',     ), array(1 => 'name',)   );   $route4 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)/blog/([0-9]+)#', array(    'controller' => 'Blog',    'action' => 'show',     ), array(1 => 'name', 2 => 'blogId',)   );   $router->addRoute('url', $route0);   $router->addRoute('user_list', $route1);   $router->addRoute('user', $route2);   $router->addRoute("blog_list", $route3);   $router->addRoute("blog", $route4);  

Yaf有路由功能,但是没有根据路由名生成URL的方法。所以我定义了一个项目名,用于拼接URL。

在配置文件中添加配置项 yaf/conf/application.ini

iniproject.name = 'yaf' 

创建文件 yaf/application/controllers/User.php

php<?php class UserController extends Yaf_Controller_Abstract {  public function urlAction() {   $name = 'jing';   $blogId = 1;   $app = Yaf_Application::app();   $projectName = $app->getConfig()->project->name;   $urls = array(    "/{$projectName}/user",    "/{$projectName}/user/{$name}",    "/{$projectName}/user/{$name}/blog",    "/{$projectName}/user/{$name}/blog/{$blogId}",   );   foreach ($urls as $url) {    echo "<a href=/"{$url}/">{$url}<a/><br />/n";   }   return false;  }  public function indexAction() {   echo '我是用户列表^_^';   // 禁用视图模板   return false;  }  public function showAction($name) {   echo "欢迎你,{$name}";   return false;  } }  

创建文件 yaf/application/controllers/Blog.php

php<?php class BlogController extends Yaf_Controller_Abstract {  public function indexAction($name) {   echo "这是{$name}的博客列表";   return false;  }  public function showAction($blogId, $name) {   echo "{$name}的这篇博客的id为{$blogId}";   return false;  } }  

Yaf的Action支持参数绑定,是按变量名绑定的。$name、$blogId要和路由中配置的名称一样,而和参数顺序无关。

Laravel

新建Laravel项目:

composer create-project laravel/laravel --prefer-dist

清除合并文件。在目录 laravel/vendor/ 下有个文件 compiled.php ,这个文件是为了减少IO提高框架性能,将很多类文件合并到一个文件中而生存的。在开发环境下,应该删除该文件,否则修改了一些文件发现没有效果,其实是因为文件已经合并缓存了。

清除命令:

php artisan clear-compiled

在生产环境中应该开启,以提升性能:

php artisan optimize --force

修改路由文件 laravel/app/Http/routes.php

php<?php Route::get('/url', array('uses' => 'UserController@getUrl')); Route::get('/user', array('uses' => 'UserController@getIndex')); Route::get('/user/{username}', array('uses' => 'UserController@getShow')); Route::get('/user/{username}/blog', array(  'as' => 'blog_list',  'uses' => 'BlogController@getIndex', )); Route::get('/user/{username}/blog/{blogId}', array(  'as' => 'blog',  'uses' => 'BlogController@getShow', ))->where(array('blogId' => '[0-9]+'));  

查看路由定义情况:

php artisan route:list

输出:

+--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ | Domain | Method   | URI                           | Name      | Action                                       | Middleware | +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ |        | GET|HEAD | url                           |           | App/Http/Controllers/UserController@getUrl   |            | |        | GET|HEAD | user                          |           | App/Http/Controllers/UserController@getIndex |            | |        | GET|HEAD | user/{username}               |           | App/Http/Controllers/UserController@getShow  |            | |        | GET|HEAD | user/{username}/blog          | blog_list | App/Http/Controllers/BlogController@getIndex |            | |        | GET|HEAD | user/{username}/blog/{blogId} | blog      | App/Http/Controllers/BlogController@getShow  |            | +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ 

定义路由变量全局模式,修改文件 laravel/app/Providers/RouteServiceProvider.php 中的 boot 方法:

php    public function boot(Router $router) {         $router->pattern('username', '[a-zA-Z0-9_-]+');          parent::boot($router);     } 

创建UserController控制器:

php artisan make:controller UserController

Laravel帮我们在 laravel/app/Http/Controllers 目录下创建了文件 UserController.php ,文件中已经为我们写好一部分骨架代码。修改文件 laravel/app/Http/Controllers/UserController.php

php<?php namespace App/Http/Controllers; use App/Http/Controllers/Controller; class UserController extends Controller {  public function getUrl() {   $name = 'jing';   $blogId = 1;   $urls = array(    url('/user'),    action('UserController@getShow', array($name)),    route('blog_list', array($name)),    route('blog', array($name, $blogId)),   );   foreach ($urls as $url) {    echo "<a href=/"{$url}/">{$url}<a/><br />/n";   }  }  public function getIndex() {   echo '我是用户列表^_^';  }  public function getShow($name) {   echo "欢迎你,{$name}";  } }  

创建BlogController控制器:

php artisan make:controller BlogController

修改文件 laravel/app/Http/Controllers/BlogController.php

php<?php namespace App/Http/Controllers; use App/Http/Controllers/Controller; class BlogController extends Controller {  public function getIndex($name) {   echo "这是{$name}的博客列表";  }  public function getShow($name, $blogId) {   echo "{$name}的这篇博客的id为{$blogId}";  } }  

Laravel的Action也支持参数绑定,是按变量顺序绑定的,和变量名无关。

后语

我是Laravel粉,但是我也没有想黑其他框架的意思,大家有兴趣也可以用自己熟悉的框架来实现这个小例子,写了记得@我,语言不限。

正文到此结束
Loading...