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