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

漏洞挖掘
• 阅读 21972

前言

读过一篇关于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="dev" 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粉,但是我也没有想黑其他框架的意思,大家有兴趣也可以用自己熟悉的框架来实现这个小例子,写了记得@我,语言不限。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
4年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
4年前
ELK学习笔记之配置logstash消费kafka多个topic并分别生成索引
0x00 filebeat配置多个topicfilebeat.prospectors:input_type:logencoding:GB2312fields_under_root:truefields:添加字段
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
漏洞挖掘
漏洞挖掘
Lv1
明月出天山,苍茫云海间
文章
4
粉丝
0
获赞
0