每次我面試應屆生時都會問他使用過什麼框架,並談談對這些框架的理解。 當面試有經驗的程序員時,會讓他本身寫一個框架出來。 其實也不是讓他編碼,只要有思路就 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 結構設計