导言:ThinkPHP框架是国内知名度很高应用很广泛的php框架,我们从一些简单的开发示例中来深入了解一下这个框架给我们带来的开发便捷性,以及游刃有余的扩展设计。同时也从源码分析的角度看看框架的一些不足,尽量做全面客观的评价。这里假设大家已经使用过ThinkPHP框架,基本使用方法请参考官方文档。
一、框架分层及url路由
框架的安装非常简单,下载后放入web服务器的目录即可,但是建议大家不要用默认的入口文件位置,而是放入单独的目录,便于保护代码和数据。例如我的入口文件和web服务器配置目录在web目录(外层框架里的index.php没有删除但是没有使用):
[1.png]
同大多数MVC框架一样,我们只需要按框架的目录结构,扩展自己的Controller和View,一些页面就开发完成了。ThinkPHP提供Module、Controller、Action三层结构来组织自己的url(3.1版本叫分组、Action和method,3.2更加国际范),目录结构如下:
[2.png]
这里强烈建议大家:
1、业务单独分层,不用放在Controller和Model里,例如我这里通过扩展函数库Application/Common/Common/function.php强制定义业务层名称为Service:
1
2
3
4
functionservice($name)
{
return D($name, 'Service');
}
好处是复用性好,假如将来要开发wap页面,写了不同的Controller,就可以复用service,假如以后的数据存储变了,比如把数据库从mysql迁移到mongodb之类,那修改Model就可以,service还是不需要任何修改。
2、基础模块和业务模块分开,不要相互引用。基础模块(例如用户基本信息)只提供数据接口没有Controller和View。
三层目录已经可以应对一般的web应用,更加复杂的web应用我们可以定义不同的入口文件加载不同的Application来解决。更更复杂的应用?门户和超大规模网站么,那就不是一个php框架能解决所有问题的了,需要自己的中间件和定制框架。
ThinkPHP的支持4种url访问模式,分别是:
1、 普通模式,传统url模式,所有参数分开,例如
http://localhost/tp/index.php?m=Ucai&c=User&a=index¶=xxx
路由参数:m参数表示模块,c表示控制器,a表示访问方法
2、 兼容模式
http://localhost/tp/index.php?s=/Ucai/User/index/para/xxx
路由参数通过s参数组装,当然数据参数也可以不必放在s参数里;
3、pathinfo模式
http://localhost/tp/index.php/Ucai/User/index/para/xxx
这种模式把入口文件和真实脚本放在一起,含义明确,也便于SEO;
4、rewrite模式
http://localhost/tp/Ucai/User/index/para/xxx
这种模式通过web服务器的rewrite配置隐藏入口文件,显得更加友好;其中pathinfo和rewrite模式需要web服务器支持。ThinkPHP有个配置需要设置为哪种模式,其实是用在U方法里生成url链接的时候用到的,访问的时候只web服务器支持用哪种方式都可以。也建议ThinkPHP其实不需要配置,而是记住用户访问的方式,只要第一个访问用的是哪种模式,以后生成的url都用这种方式生成,因为用户都已经访问到了就不存在支不支持的问题了。如果正常的url不能达到我们的要求,还可以通过配置路由进一步优化url,例如我们想把url配置的更加简单http://localhost/tp/Ucai/login/xxx
我们只需要在模块配置文件中添加如下的路由配置即可,如果用正则表达式则可以更加简化
1
2
3
4
'URL_ROUTE_RULES' => array(
'login/:para' =>'Ucai/User/index',
'login' => 'Ucai/User/index',
),
到这里我们可以看到,ThinkPHP框架支持的层次结构和url配置非常丰富,能满足各种不同的需求。当然我们建议大家不要滥用路由配置,适当少量的配置能带来更好的seo效果,但是大量的配置会给项目的维护和修改带来困难。
二、ThinkPHP扩展
ThinkPHP本身含有丰富的组件和驱动,我们以数据库驱动扩展和行为扩展为例来了解一下ThinkPHP的扩展设计。
1、数据库驱动扩展
虽然ThinkPHP提供了众多的数据库驱动,但是也并不能满足所有的需求。例如我们的数据很可能不是通过直接访问数据库去实现,而是通过一些中间件(例如C程序)进行转发,从而获得更好的性能,这时就需要扩展数据库驱动来支持。扩展非常简单,在DB/Driver目录下新建自己的驱动,例如Custom.php,然后实现request和execute方法扩展就算完成了,然后再配置文件里配置DB_TYPE=’custom’,就可以使用了。这里的request表示查询,execute表示更改数据,所有其他操作都会在Model里进行解析,包装成sql语句调用这两个方法执行。
例如我所实现的最简单的query方式,通过shell命令调用sqlite执行sql语句:
1
2
3
4
publicfunction query($str) {
$cmd = sprintf('sqlite3 %s"%s"', $this->config['params']['dbfile'], $str);
exec($cmd, $arr);
}
当然这个只是示例,ThinkPHP本身就支持sqlite3,通过pdo的方式去连接就可以。实际的应用环境可能是通过连接4层协议访问中间层端口获取数据。
2、Behavior行为扩展
Behavior行为设计是ThinkPHP框架的核心,通过行为配置和扩展,为系统的伸缩性和定制性提供了最大的支持。
假如我们要加入登录验证的功能,按照常规我们会设计自己的父类Controller,然后所有其他的Controller都从这里继承。但有了Behavior会变得更加简单和灵活,我们只需要在tags.php(没有的话在配置目录新建)添加一个Behavior就可以了:
1
2
3
4
return array(
'action_begin' => array('UcaiBehaviorAuthBehavior'),
'view_begin' => array('UcaiBehaviorOutputBehavior'),
);
程序在执行到action_begin流程时就会调用这个Behavior,我们可以根据状态进行跳转或终止执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace UcaiBehavior;
class AuthBehavior {
// 行为扩展的执行入口必须是run
public function run(&$return) {
//不需要验证的action设置为true
if (!$return['AUTH_PUBLIC']) {
if (service('User')->checkLogin())
{
$return = true;
}
else
{
header('Content-Type: text/html; charset=utf-8');
redirect(U('User/index', array('url' => $_SERVER['HTTP_REFERER'])), 5, '需要登录,5秒后跳转。。。');
}
}
}
}
对于不需要登录的页面我们可以在Controller里添加配置,所有不配置的都会要求登录验证。
1
public $config = array('AUTH_PUBLIC' => true);
这里大家对继承和Behavior实现登录验证做一个对比,可能觉得区别不大。但是在一个复杂的项目里,这种功能会非常多,如果每个功能都放到父类里,就会非常庞大,并且部分子类可能又不需要,这时候用Behavior去定制流程就会显得游刃有余。
在上面的配置中我们还发现了一个配置OutputBehavior更能说明问题,大家有没有猜到,这个Behavior我是用来在view里输出一些共有变量,例如jscss的域名和路径等。在没有Behavior之前,大家是不是需要一个公共方法,然后每个页面都去调用一次,或者改写View的类代码?有了Behavior就显得方便许多。
1
2
3
4
5
6
7
namespace UcaiBehavior;
class OutputBehavior {
public function run(&$return) {
$view = ThinkThink::instance('ThinkView');
$view->assign('STATIC_URL', 'http://p3.ucai.cn/static');
}
}
扩展总结:通过Behavior扩展和数据库驱动扩展大家可以看到,ThinkPHP提供了很灵活的扩展和增强机制,能满足众多需求。其他存储、缓存、日志、模板引擎等如果需要也能很方便的扩展。
三、源码分析与不足
首先我们来分析一下框架执行的大致流程:
index.php(入口、调试模式、应用路径)
-->ThinkPHP.php(定义路径与访问模式)
-->ThinkThink(类加载器、异常处理、读取共有配置)
-->ThinkApp(请求url调度解析、执行调度解析结果)
--> exec 执行用户定义的Controller的Action方法
-->ThinkDispatcher(根据url模式解析M、C、A和参数,加载模块配置)
-->ThinkController(调用视图、包装和重定向)
可以看到,框架的内部流程其实比较简单,还有2个很重要的类:
ThinkHook: 监听App、Action、View的各个阶段,执行Behavior
ThinkBehavior: 可配置(配置文件)可增删(代码)
在分析源代码的过程中,我们也看到了一些不足:
1、宏定义过多,难于维护和修改
建议:只在个别文件定义极少数几个宏,其余用类常量包装
2、面向过程代码过多,封装不清晰
建议:用面向对象思想包装
例如:url的解析和包装,现在是在Dispatcher里生成/index.php?app=blog宏,然后在U方法里读取宏并生成最终url。其实完全可以定义一个类来包装例如UrlHelper,而类的二个方法parse和generate分别负责解析和生成url,这样代码结构会清晰很多。
3、有的函数和类代码封装过多,复用和改进不方便
建议:用组合来封装独立功能内容
例如:Model的校验功能,完全可以独立成类,也可以用于非Model对象调用。而现在的校验接口是Model的保护性方法,只能在Model的create函数调用,外面必须通过create方法才能校验。
4、代码规范和风格问题
希望代码风格能更加规范和标准,例如DB类作为模板方法的父类,应该用抽象方法或抛出异常形式定义所有Model需用到的方法。事实上有些方法子类是不需要的,而Db类却没有实现。
四、总结
ThinkPHP作为国内热门的php框架,确实给我们的开发带来了便利。框架开发者对web流程理解的很透彻,对php的函数应用炉火纯青。框架定义了灵活的配置和扩展适应各种需求,提供了丰富的组件和模块来加速开发。最后说一点,ThinkPHP的文档和社区支持非常完善,这也是框架流行不可缺少的重要一环。我们也希望ThinkPHP以后能更加完善自身的结构,打造成最优秀的php框架。
- PHP技术文章
- PHP中session和cookie的区别
- php设计模式(一):简介及创建型模式
- php设计模式结构型模式
- Php设计模式(三):行为型模式
- 十款最出色的 PHP 安全开发库中文详细介绍
- 12个提问频率最高的PHP面试题
- PHP 语言需要避免的 10 大误区
- PHP 死锁问题分析
- 致PHP路上的“年轻人”
- PHP网站常见安全漏洞,及相应防范措施总结
- 各开源框架使用与设计总结(一)
- 数据库的本质、概念及其应用实践(二)
- PHP导出MySQL数据到Excel文件(fputcsv)
- PHP中14种排序算法评测
- 深入理解PHP原理之--echo的实现
- PHP性能分析相关的函数
- PHP 性能分析10则
- 10 位顶级 PHP 大师的开发原则
- 30条爆笑的程序员梗 PHP是最好的语言
- PHP底层的运行机制与原理
- PHP 性能分析与实验——性能的宏观分析
- PHP7 性能翻倍关键大揭露
- 鸟哥:写在PHP7发布之际一些话
- PHP与MySQL通讯那点事
- Php session内部执行流程的再次剖析
- 关于 PHP 中的 Class 的几点个人看法
- PHP Socket 编程过程详解
- PHP过往及现在及变革
- PHP吉祥物大象的由来
- PHP生成静态页面的方法
- 吊炸天的 PHP 7 ,你值得拥有!
- PHP开发中文件操作疑难问答
- MongoDB PHP Driver的连接处理解析
- PHP 杂谈《重构-改善既有代码的设计》之二 对象
- 在php中判断一个请求是ajax请求还是普通请求的方法
- 使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重构-改善既有代码的设计
- PHP场景中getshell防御思路分享
- 移动互联时代,你看看除了PHP你还会些什么
- 安卓系统上搭建本地php服务器环境
- PHP中常见的缓存技术!
- PHP里10个鲜为人知但却非常有用的函数
- 成为一名PHP专家其实并不难
- PHP 命令行?是的,您可以!
- PHP开发提高效率技巧
- PHP八大安全函数解析
- PHP实现四种基本排序算法
- PHP开发中的中文编码问题
- php.get.post
- php发送get、post请求的6种方法简明总结
- 中高级PHP开发者应该掌握哪些技术?
- 前端开发
- web前端知识体系大全
- 前端工程与性能优化(下)
- 前端工程与性能优化(上)
- 2016 年技术发展方向
- Web应用检查清单
- 如何成为一名优秀的web前端工程师
- 前端组件化开发实践
- 移动端H5页面高清多屏适配方案
- 2015前端框架何去何从
- 从前端看“百度迁徙”的技术实现(一)
- 从前端看“百度迁徙”的技术实现(二)
- 前端路上的旅行
- 大公司里怎样开发和部署前端代码?
- 5个经典的前端面试问题
- 前端工程师新手必读
- 手机淘宝前端的图片相关工作流程梳理
- 一个自动化的前端项目实现(附源码)
- 前端代码异常日志收集与监控
- 15年双11手淘前端技术总结 - H5性能最佳实践
- 深入理解javascript原型和闭包系列
- 一切都是对象
- 函数和对象的关系
- prototype原型
- 隐式原型
- instanceof
- 继承
- 原型的灵活性
- 简述【执行上下文】上
- 简述【执行上下文】下
- this
- 执行上下文栈
- 简介【作用域】
- 【作用域】和【上下文环境】
- 从【自由变量】到【作用域链】
- 闭包
- 完结
- 补充:上下文环境和作用域的关系
- Linux私房菜