每次我面试应届生时都会问他使用过什么框架,并谈谈对这些框架的理解。 当面试有经验的程序员时,会让他本身写一个框架出来。 其实也不是让他编码,只要有思路就 OK 了。 我以为,若是一个有一年经验的程序员连一个 Framework v0.0.1 都开发不出来的话,确定是没有深刻理解一个框架。 javascript
前几天 @phoenixg 说要本身写个 MVC 框架。 并且他也确实不只仅是说说而已,短短一个周末,这个框架雏形就神奇的出如今了 github 上。 php
这篇博文的名字是『本身动手设计 PHP MVC框架』, 因此本文不会涉及太多的编码,文中出现的任何代码片断都是我直接在 vim 里面敲的, 没作任何测试,若是想使用文中代码需自行测试。 html
跟随本教程,将从零开始设计一个属于本身的 MVC 框架。 java
我使用过 ZendFramwork、CodeIgniter,每一个框架都有本身的优势和不足。 在写本文以前,我又看了 Symfony、cakephp、MooPHP、doitphp 等的核心源码, 下面说说我将把个人框架设计成什么样子,这一章主要讨论 URL 的设计。 node
在这个 REST 横行的时代,若是一个框架不支持 REST,确定被前卫程序员所瞧不起,因此本框架也要支持 REST。 git
第一个设计准则: 全部东西都是资源,资源有多种表现形式。 程序员
无论实际上存在的,仍是抽象上的, 全部资源都会有一个不变的标识(ID),对资源的任何 API 操做都不该该改变资源的标识。 github
事实上,上面的这些完彻底全是按照互联网的特性提出来的。 面试
好比 json
GET http://justjavac.com/users // 全部用户 GET http://justjavac.com/users/phper // 标识为phper的用户
在此我不讨论扩展名和文件类型之间的关系,以及“扩展名只是约定,而文件类型记录在文件头”。
我一般把扩展名理解为“约定”,而不是文件类型。 当咱们请求一个 news.html 时,咱们并不能确信它就是一个存在于服务器上的news.html文件, 它也多是php文件,也多是jsp文件,在nodejs流行的今天,它也多是一个js文件。 但无论页面是如何生成的,有一点是明确的——最终咱们获得了一个html文档。
虽然rest不要求使用扩展名,但有人告诉我,若是在一个女生名字后面加一个.rmvb 的扩展名,将变得很是……所以本框架将支持扩展名,可是扩展名并是资源的一部分。
什么意思呢?
仍是前面的例子,全部用户这个资源该如何表示呢? 用 url http://justjavac.com/users
就能够惟一标识, 而 扩展名能够用来标识资源的不一样表现形式。
a、当咱们请求 http://justjavac.com/users
时,框架将返回一个html文档, 数据可能在表格中,也可能在 form 中,也可能在 div 中(以下图)。
b、当咱们请求 http://justjavac.com/user.json
时,将返回 json 格式数据。
c、当咱们请求 http://justjavac.com/user.xml
时, 将返回 xml 格式的数据,xml 文档可由 DTD 或者 XSD 定义。
d、若是咱们想把全部用户的列表发给管理员,或者打印出来呢?
能够直接访问 http://justjavac.com/user.xls
,框架将会返回 Excel 电子表格。 当咱们高高兴兴把文件下载下来,却发现电脑没有安装 Excel,怎么办? 不要紧,咱们还能够访问http://justjavac.com/user.jpg
,毕竟看图工具咱们仍是有的。
总之,无论用了什么扩展名,将返回同一个资源,只是表现形式不一样罢了。 这也就是常常所说的 数据 + 模板 = 输出。
若是没有扩展名呢?返回 HTML 文档?
别忘了 http 请求的 Accept。 设置请求头的 Accept: application/x-excel
咱们依然能够获得一个电子表格。
甚至当咱们访问某个用户时, http://justjavac.com/user/justjavac
,咱们可使用 Accept: text/x-vcard
,若是不知道嘛意思,本身Google去。
下面说说设计模式,在这个功能上,能够用一个适配器模式,根据不一样的扩展名选择不一样的适配器,执行不一样的功能,最后提供相同的接口,具体实现就很少说了。
@TODO 多语言支持的 url 结构设计
和请求有关的错误和其余重要的状态信息怎么办呢?
简单,使用 HTTP 的状态码! 经过使用 HTTP 状态码,你不须要为你的接口想出 error/success 规则,它已经为你作好。
好比:假如一个消费者提交数据(POST)到 /api/users
,
幸运的是,你已经知道了这些,假如你想要了解更多关于状态码的资料,能够在维基百科上查找。
HTTP 支持客户端缓存,在HTTP响应里利用 Cache-Control,Expires,Last-Modified 三个头字段, 咱们可让浏览器缓存资源一段时间。
REST 也能够利用这些头,告诉客户端在必定时间内不须要再次请求资源。 这对提升性能有很大好处。Expires、Last-Modified 以及 ETag 能够经过资源的属性提供,这个在有关 Model 层的设计中再详细介绍。
PHP 的灵活使得自动化测试或者 TDD 变得困难,至少和 Java 比就差了好大一截。 在框架中,将很自由的开启调试,好比个人设计是经过添加 url 参数:
http://justjavac.com/user/justjavac?DEBUG=2
经过添加 DEBUG 参数告诉框架开启调试模式,后面的参数值是调试的级别 level。 相似的,你也能够加入 LOG 参数来启动日志。
这样设计还有一个好处就是,不须要修改配置文件,并且还能够 针对某一个页面来开启或者关闭。 当我用 CI 时,每次我发现程序中的问题,都在配置文件中将 log 级别设置为 all, 再从新打开页面,当我再看 log 文件时,竟然已经几百行了,由于我访问的每一个页面都被记录到了日志里面。
测试和 url 好像没有多大关系,测试放在单独的章节讨论。 我为测试约定的 url 是添加 test,好比为控制器 justjavac.controller.php 写的测试用例(Test Case)能够经过http://justjavac.com/test/user/justjavac
访问。
但我仍是比较喜欢在命令行测试,毕竟当你手动点击浏览器,并手动输入 url, 手动敲回车键时,已经违背了自动化测试。
@TODO 应用于单页 Ajax 的 url 结构设计