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


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

    • 8 推荐

    前言

    读过一篇关于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对应用户的一篇博客文章

    方案引用自原文:

    '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。

    修改公共配置文件tpApplicationCommonConfconfig.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',
        ),
    
    );
    ?>
    

    创建文件tpApplicationHomeControllerUserController.class.php

    <?php
    
    namespace HomeController;
    
    use ThinkController;
    
    
    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 />
    ";
            }
        }
    
    
        public function index() {
            echo '我是用户列表^_^';
        }
    
    
        public function show($name) {
            echo "欢迎你,{$name}";
        }
    
    }
    ?>
    

    创建文件tpApplicationHomeControllerBlogController.class.php

    <?php
    
    namespace HomeController;
    
    use ThinkController;
    
    
    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

    输出:

    <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个链接,依次返回:

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

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


    Zend Framework 2

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

    composer create-project --stability="dev" zendframework/skeleton-application zf2

    修改默认模块Application的配置文件zf2moduleApplicationconfigmodule.config.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' => 'ZendMvcRouterHttpLiteral',
                    'options' => array(
                        'route' => '/url',
                        'defaults' => array(
                            'controller' => 'ApplicationControllerUser',
                            '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__' => 'ApplicationController',
                            '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__' => 'ApplicationController',
                            '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(
                'ZendCacheServiceStorageCacheAbstractServiceFactory',
                'ZendLogLoggerAbstractServiceFactory',
            ),
            '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(
                'ApplicationControllerIndex' => 'ApplicationControllerIndexController',
                'ApplicationControllerUser' => 'ApplicationControllerUserController',
                'ApplicationControllerBlog' => 'ApplicationControllerBlogController',
            ),
        ),
        '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官方发布了一个骨架程序的原因。

    创建文件zf2moduleApplicationsrcApplicationControllerUserController.php

    <?php
    
    namespace ApplicationController;
    
    use ZendMvcControllerAbstractActionController;
    
    use ZendViewModelViewModel;
    
    
    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;
        }
    
    }
    ?>
    

    创建文件zf2moduleApplicationsrcApplicationControllerBlogController.php

    <?php
    
    namespace ApplicationController;
    
    use ZendMvcControllerAbstractActionController;
    
    use ZendViewModelViewModel;
    
    
    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()。如果你知道可以禁用视图的办法,请告诉

    创建文件zf2moduleApplicationviewapplicationuserurl.phtml

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

    创建文件zf2moduleApplicationviewapplicationuserindex.phtml

    我是用户列表^_^
    

    创建文件zf2moduleApplicationviewapplicationusershow.phtml

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

    创建文件zf2moduleApplicationviewapplicationlogindex.phtml

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

    创建文件zf2moduleApplicationviewapplicationlogshow.phtml

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

    Yaf

    安装Yaf

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

    修改启动文件yafapplicationBootstrap.php,修改其中的_initRoute方法:

            $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。

    在配置文件中添加配置项yafconfapplication.ini

    project.name = 'yaf'
    

    创建文件yafapplicationcontrollersUser.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 />
    ";
            }
            return false;
        }
    
    
        public function indexAction() {
            echo '我是用户列表^_^';
    
            // 禁用视图模板
            return false;
        }
    
    
        public function showAction($name) {
            echo "欢迎你,{$name}";
            return false;
        }
    
    }
    
    

    创建文件yafapplicationcontrollersBlog.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

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

    在生产环境中应该开启,以提升性能:
    php artisan optimize --force

    修改路由文件laravelappHttp outes.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                           |           | AppHttpControllersUserController@getUrl   |            |
    |        | GET|HEAD | user                          |           | AppHttpControllersUserController@getIndex |            |
    |        | GET|HEAD | user/{username}               |           | AppHttpControllersUserController@getShow  |            |
    |        | GET|HEAD | user/{username}/blog          | blog_list | AppHttpControllersBlogController@getIndex |            |
    |        | GET|HEAD | user/{username}/blog/{blogId} | blog      | AppHttpControllersBlogController@getShow  |            |
    +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+
    

    定义路由变量全局模式,修改文件laravelappProvidersRouteServiceProvider.php中的boot方法:

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

    创建UserController控制器:
    php artisan make:controller UserController

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

    <?php
    
    namespace AppHttpControllers;
    
    use AppHttpControllersController;
    
    
    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 />
    ";
            }
        }
    
    
        public function getIndex() {
            echo '我是用户列表^_^';
        }
    
    
        public function getShow($name) {
            echo "欢迎你,{$name}";
        }
    
    }
    
    

    创建BlogController控制器:
    php artisan make:controller BlogController
    修改文件laravelappHttpControllersBlogController.php

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

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


    后语

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

  • 相关阅读:
    图灵访谈之二十二——Brian W. Kernighan与CS的半个世纪 (翻译)
    GIT 初探
    关于SQL的分组汇总统计(关键字 Grouping)
    根据表名生成该表的插入语句
    利用sys.dm_db_index_physical_stats查看索引碎片等数据
    SQL SERVER CURSOR
    Configuring a readonly replicated folder on Windows Server 2008 R2.
    securestring
    Sql Server查询性能优化
    七大排序速查版
  • 原文地址:https://www.cnblogs.com/shsgl/p/5884226.html
Copyright © 2020-2023  润新知