本文詳細講述了搭建自己的PHP MVC框架的方法。分享給大家供大家參考,具體如下:
前言
說到寫PHP的MVC框架,大家想到的第一個詞--“造輪子”,是的,一個還沒有深厚功力的程序員,寫出的PHP框架肯定不如那些出自大神們之手、經過時間和各種項目考驗的框架。但我還是準備并且這么做了,主要是因為:
認為有關PHP的方方面面都了解了,但自己學習PHP的時間還短,基礎并不扎實,很多常用函數的參數還偶爾要查手冊,而且對于PHP的一些較新的特性如命名空間、反射等只是簡單的看過,并沒有能實際應用過。
PHP的知識多且雜,一個普通的項目往住是業務邏輯代碼為主,而框架是一個能把這些知識點能融匯在一起的項目。
在自己寫一個框架的時候,也會參考一些我使用過的框架如TP/CI/YII等的源碼,在自己看源碼時也能幫助自己理解框架,更容易接受以后要使用的框架。
所以說,這次造輪子的目的不是為了造輪子而是為了在造輪子的過程中熟悉其工藝,總結輪子特點,更好的使用輪子。
如果說寫一個完整的PHP框架,那需要掌握的PHP知識點非常多,像設計模式、迭代器、事件與鉤子等等,還有許多基礎知識的靈活應用。我自認為這些還無法完全掌控,所以我的步驟是先自己搭建一個骨架,然后參考借鑒不同的PHP框架的特點,將其慢慢完善。因為工作原因,而且晚上還要補算法、網絡等編程基礎,PHP框架部分可能只有周末有時間更新,我會在進行框架功能更新之后,總結使用的知識點,更新博文。
首先放上框架的目前源碼:GITHUB/zhenbianshu
或者點擊此處本站下載。
框架整體
首先自己總結一下PHP的MVC框架的工作流程:
簡單來說,它以一個入口文件來接受請求,選擇路由,處理請求,返回結果。
當然,幾句話總結完的東西實際上要做的工作很多,PHP框架會在每次接受請求時,定義常量,加載配置文件、基礎類,根據訪問的URL進行邏輯判斷,選擇對應的(模塊)控制器和方法,并且自動加載對應類,處理完請求后,框架會選擇并渲染對應的模板文件,以html頁面的形式返回響應。在處理邏輯的時候,還要考慮到錯誤和異常的處理。
1、作為MVC框架,一定要有一個唯一的入口文件來統領全局,所有的訪問請求都會首先進入這個入口文件,如我框架根目錄的index.php,在里面,我定義了基本文件夾路徑,當前環境,并根據當前環境定義錯誤報告的級別。
2、PHP中加載另外的文件,使用require和include,它們都是將目標文件內容加載到當前文件內,替換掉require或include語句,require是加載進來就執行,而include是加載進來在需要的時候執行,而它們的_once結構都是表示在寫多次的時候只執行一次。
3、框架內的配置變量等使用專用的配置文件來保存,這里我仿照了TP里的數組返回法,用了一個compileConf()
函數來解析數組,將數組的鍵定義為常量,值為數組的值。
if (!function_exists('compile_conf')) { function compileConf($conf) { foreach ($conf as $key => $val) { if(is_array($val)){ compileConf($val); }else{ define($key, $val); } } } } compileConf(require_once CONF_PATH.'config.php');
命名空間和自動加載
為什么把命名空間和自動加載放到一塊說呢?
在一個PHP項目中,類特別多的時候,如果類名重復的話就會造成混亂,而且相同文件夾內也不能存在同名的文件,所以這時候命名空間和文件夾就搭檔出場了。文件夾就是一個一個的盒子,命名空間在我理解就像是一個標簽,盒子對應標簽。我們定義類時,把各種類用不同的盒子分別裝好,并貼上對應的標簽。而在自動加載類時,我們根據標簽(命名空間)可以很輕易找到對應的盒子(文件夾)然后找到對應的類文件。
而類的自動加載,我們知道的__autoload()魔術函數,它會在你實例化一個當前路徑找不到的對象時自動調用,根據傳入的類名,在函數體內加載對應的類文件。
現在我們多用spl_autoload_register()函數,它可以注冊多個函數來代替__autoload函數的功能,我們傳入一個函數名為參數,spl_autoload_register會將這個函數壓入棧中,在實例化一個當前路徑內找不到的類時,系統將會將函數出棧依次調用,直到實例化成功。
spl_autoload_register('Sqier\Loader::autoLoad'); class Loader { public static function autoLoad($class) { //如果有的話,去除類最左側的\ $class = ltrim($class, '\\'); //獲取類的路徑全名 $class_path = str_replace('\\', '/', $class) . EXT; if (file_exists(SYS_PATH . $class_path)) { include SYS_PATH . $class_path; return; } if (file_exists(APP_PATH . $class_path)) { include APP_PATH . $class_path; return; } }
現在Loader類還是一個簡單的類,待以后慢慢完善。
路由選擇