作为入门的新人,PHP框架用的不多。听大牛说,框架何其多,用不完,知道它的基本用法,比如怎么接收路由参数,怎么操作数据库,知道它自带了哪些常用方法或者函数,就差不多了——PHP本来就没太多东西(其实发展到现在已经加了不少了~-~,都用上最新的最先进的,也不简单)。为了理解这个过程更充分一些,尝试着自己也写一个最最最基本的框架,因为要写一个足够好的框架,需要考虑的东西比较多,尤其是可扩展性以及灵活性这两个方面,没有足够经验都不知道这两个“性”是什么东西,还有就是整个框架的设计,没些积累真写不出什么东西来。常用框架里边CI被认为是最简单的(其实也是最古老的框架之一),基本流程是:入口—》路由—》(《缓存》)—》控制器(跟一堆辅助函数以及模型打交道)—》视图(—》缓存)—》浏览器。这确实是最基本最简单的了,而我自己写的这个,也基本是这个流程,并且借鉴了一些Yii Framework和ThinkPHP的做法。
1、框架启动
启动部分,最开始是在入口引入框架启动文件,定义一些路径,读取应配置文件,启动。接着注册自己的自动加载函数,注册错误处理方法以及日志记录等。
2、路由解析
这部分就是难点了,到底要怎样的路由呢?传统的index.php?module=m&controller=c&action=a是不是有点土?使用正则匹配来美化一翻?抑或其他?其实传统路由是最简单效率最高的,正则匹配其实很耗时,尤其是规则多的时候,但为了所谓的美观,还是学一下。至于其他,我目前是不太了解了。因此,我这里有传统路由和正则匹配两种。正则匹配规则如下,左边是浏览器看到的,右边是实际对应的模块控制器方法以及传递的变量。
'rules'=>array(
'index.html'=>'index',
'login'=>'user/login',
'torrent/list//'=>'torrent/list',
'user/name/<.>/age/'=>'user/filter',
'user/sex/<.>'=>'user/filter',
'user/year/'=>array('user/filter', 'urlSuffix'=>'.shtml'),
'thread/'=>array('thread/show', 'urlSuffix'=>'.html'),
),
3、创建URL
这跟路由解析是相反的过程,又一耗时大户,感觉真有点得不尝失。需要根据规则创建能够被解析的URL,而且要传递一定的参数。要传递的参数如果规则中有就得写到规则中的位置,否则就是传统的方法?param1=value1¶m2=value2接到规则后边。不知道我的这个处理如何,感觉好麻烦。
4、数据库操作
这一块当然得用上PDO,再用mysql系列估计要被鄙视到火星了。学习了一下基本用法,封装一个Db类,内容倒不是很多。执行查询的方法一个以及执行增删改的方法一个,就差不多了,当然还有个事务操作。
5、模型基类
这是重点之一。很多操作数据库的基础方法都在里边定义,拼接sql、Active Record活跃记录、缓存、自动验证等都在里面定义或是调用的入口。这里面好像刚好达到1000行,对于自己写的东西来说,算比较多的了。
6、活跃记录
英文是Active Record,现在基本的框架都提供这功能,操作数据库就像操作一个对象一样,对于操作单条记录比较方便好用,多条就不行了,每条记录一个对象内存占用高很多的拼接,不如直接拼sql语句来得效率高。一开始不知道这个活跃对象要怎么设计,摸索了很久大体如下,有表名、主键、主键的值、拼sql的各种方法、更改了的数据、查询了的数据、验证场景、查询缓存等等。
object(TestModel)#7 (24) {
["_tableName":"Model":private]=>
string(10) "`students`"
["_fields":"Model":private]=>
array(4) {
[0]=>
string(6) "stu_id"
[1]=>
string(6) "gender"
[2]=>
string(4) "name"
[3]=>
string(8) "class_id"
}
["_pk":"Model":private]=>
string(6) "stu_id"
["_pv":"Model":private]=>
int(3)
["_noPk":"Model":private]=>
bool(false)
["distinct":"Model":private]=>
NULL
["field":"Model":private]=>
NULL
["table":"Model":private]=>
NULL
["join":"Model":private]=>
NULL
["where":"Model":private]=>
NULL
["bindParam":"Model":private]=>
array(0) {
}
["group":"Model":private]=>
NULL
["having":"Model":private]=>
NULL
["order":"Model":private]=>
NULL
["limit":"Model":private]=>
NULL
["active":"Model":private]=>
bool(false)
["chained":"Model":private]=>
bool(false)
["changeProperties":"Model":private]=>
array(0) {
}
["data":"Model":private]=>
array(4) {
["stu_id"]=>
int(3)
["gender"]=>
string(4) "male"
["name"]=>
string(6) "xiaoai"
["class_id"]=>
string(2) "99"
}
["isNew":"Model":private]=>
bool(false)
["scene":"Model":private]=>
NULL
["errors":"Model":private]=>
array(0) {
}
["cacheOptions":"Model":private]=>
array(0) {
}
["doCache":"Model":private]=>
bool(false)
}
7、自动验证
这个简单的写了非空验证、长度验证、数值验证、唯一验证、自定义函数验证、等于某值、不等于某值等简单的验证,其实对这一个的必要性有点疑虑,在控制器之类的自己写也不是很麻烦,但如果需要验证的地方多,也还是有一定用处。对于错误信息的显示还不是很清楚,Yii有个活跃表单之说,把整个模型传递到视图,字段的值与错误信息都通过模型取,打算也这么干。
8、缓存
缓存写了最基本的文件缓存,可以缓存整个页面和sql查询结果,标识分别是url和sql语句。页面缓存可以指定一些url,或者像路由解析一样的符合由module,controller,action,params参数组合起来的规则的页面。
9、视图渲染
这一块Yii写得很复杂,因为它还有Widget挂件这一说法,这个目前不知道怎么实现,也就没搞这么复杂了,直接ob_start之类的就完事了。当然,布局还是有的。
10、日志记录
在调试模式下,系统会记录引入的文件的名称、时间、内存占用、文件路径,一些错误信息,执行了的sql语句以及整个脚本运行的时间。发生错误时用了debug_backtrace追溯调用过程,日志的输出是register_shutdown_function。
目前完成的就是以上这些了,很简陋,其中大部分只是精粗略试了一下能实现而已,实际用的话肯定BUG无数。后面拿它来做个具体的东西再慢慢完善吧。代码push到了github,看官不怕丑可以前往一看: