PHP的MVC模式實(shí)現(xiàn)原理分析(一相簡單的MVC框架范例)
他們的工作原理大家應(yīng)該也比較感興趣,下面我說說一個mvc框架長什么樣。
路由機(jī)制
在互聯(lián)網(wǎng)我們都是通過url提供服務(wù),因此不同的url有不同的服務(wù)。用戶訪問不同的頁面也就獲得了不同的服務(wù)。那么我們的服務(wù)是如何通過url來區(qū)分不同的服務(wù)呢。
我們的web程序就要通過url尋找到不同的文件,進(jìn)行不同的業(yè)務(wù)邏輯處理。我們的路由機(jī)制就是根據(jù)url,尋找到對應(yīng)的controller,和action,然后由action進(jìn)行具體的業(yè)務(wù)邏輯處理。
一個簡單的controller
//定義一個controller
class UserControler extends Controller{
//定義一個action方法,注意一定是public的
public function index(){
// do business code
}
}
具體的對應(yīng)規(guī)則不同的框架映射不同。以下是CodeIgniter框架的URL路由,它會盡力的嘗試各種的可能,來分析URL的情況。
文件路徑/system/core/URI.php
// 看看是否是從命令行運(yùn)行的
if (php_sapi_name() == 'cli' or defined('STDIN')){
$this->_set_uri_string($this->_parse_cli_args());
return;
}
// 首先嘗試 REQUEST_URI 這個適應(yīng)大部分的情況
if ($uri = $this->_detect_uri()){
$this->_set_uri_string($uri);
return;
}
// 看看PATH_INFO變量是否存在?nginx需要配置
// Note: some servers seem to have trouble with getenv() so we'll test it two ways
$path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
if (trim($path, '/') != '' && $path != "/".SELF){
$this->_set_uri_string($path);
return;
}
// 沒有PATH_INFO,看看 QUERY_STRING?
$path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
if (trim($path, '/') != ''){
$this->_set_uri_string($path);
return;
}
//嘗試去從 $_GET 獲取信息
if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != ''){
$this->_set_uri_string(key($_GET));
return;
}
// 盡力了,放棄了路由
$this->uri_string = '';
return;
通過上面的嘗試,接下來就是如何利用路由機(jī)制加載正確的controller了。
Controller加載機(jī)制
我們來看看Codeigniter框架是如何加載到controller并且調(diào)用action的。
在/system/core/Codeigniter.php中有如下的代碼。Codeigniter在這之前會根據(jù)$_SERVER['PATH_INFO]里面的值來進(jìn)行賦值(這個都是靠自己的設(shè)定的,默認(rèn)的話CI他會有許多的if分支進(jìn)行判斷)。
//大約在250行
include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
$class = $RTR->fetch_class();
$method = $RTR->fetch_method();
//大約在308行
$CI = new $class();
//大約在359行
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
就這樣,通過這個就調(diào)用到了我們的controller及其方法了,接下來就是編寫自己的業(yè)務(wù)邏輯代碼了。
視圖view的顯示
當(dāng)我們的業(yè)務(wù)邏輯代碼寫完后,就需要頁面的展示了。很多常見的MVC框架在頁面的調(diào)用是這么寫的。
public function index(){
// ... 許多的業(yè)務(wù)邏輯代碼
$data = array('name'=>'abc', 'age'=>12, .... );
return $this->render('view/path/file.html',$data);
}
接著在視圖文件view/path/file.html里寫上一下代碼。
姓名 : <?=$name ?>
年齡 : <?php echo $age; ?>
</div>
這段如何將數(shù)據(jù)渲染到視圖中,這段代碼以前我一直很好奇,現(xiàn)在我明白了,我們來看看是如何實(shí)現(xiàn)的。
{
extract($var); // 抽取數(shù)組中的變量
ob_end_clean (); //關(guān)閉頂層的輸出緩沖區(qū)內(nèi)容
ob_start (); // 開始一個新的緩沖區(qū)
require TEMPLATE_ROOT . $template . '.html'; //加載視圖view
$content = ob_get_contents (); // 獲得緩沖區(qū)的內(nèi)容
ob_end_clean (); // 關(guān)閉緩沖區(qū)
//ob_end_flush(); // 這個是直接輸出緩沖區(qū)的內(nèi)容了,不用再次緩存起來。
ob_start(); //開始新的緩沖區(qū),給后面的程序用
return $content; // 返回文本,此處也可以字節(jié)echo出來,并結(jié)束代碼。
}
在這短短的幾行代碼中,全都是精華,就是這些非常重要的,全是php的內(nèi)置函數(shù),接下來我們來具體分析分析。
看看第一個extract($var)。這個函數(shù)從數(shù)組中將變量導(dǎo)入到當(dāng)前的符號表。剛剛就將$data數(shù)組里面的name、age抽取出來,這樣就可以在視圖view中使用$name $age。更詳細(xì)的請參考http://www.php.net/manual/zh/function.extract.php
第二個ob_end_clean()的作用是關(guān)閉頂層的緩沖區(qū),為了是之前的程序不小心echo出的一些文字給清楚了,為了下一行的重新開辟一塊緩沖區(qū)。
第三個ob_start()是開啟一塊新的緩沖區(qū),為了是將視圖的內(nèi)容放到緩沖區(qū)。當(dāng)然了,緩沖區(qū)有一定的大小,如果內(nèi)容超出了緩沖區(qū)的設(shè)定值,那么會自動的發(fā)送給server。
第四個require file,這個就是第一個參數(shù),根據(jù)自己的規(guī)則去加載視圖的文件。其中文件里可以夾雜php、html的代碼。你在這個render()函數(shù)聲明的任何局部變量或者這里能訪問到的任何全局變量,都可以在require的file文件中訪問到。
第五個$content = ob_get_contents ()很重要,是為了將緩沖區(qū)的內(nèi)容取出來,但不清除它。
第七個ob_start()是重新開啟一個緩沖區(qū),為了是下面的程序需要使用緩沖區(qū)。有寫框架可能不用對$content的內(nèi)容進(jìn)行操作了,那么直接ob_end_flush()將緩沖區(qū)的內(nèi)容輸出出來就行了。
這個是一個很簡單的展示視圖的過程。如果直接使用這個不方便對視圖view進(jìn)行模塊化,因此一些框架都不會這么直接用的。
我們從這個函數(shù)也可以看到程序有點(diǎn)類似程序中斷保護(hù)現(xiàn)場的感覺。只不過中斷保護(hù)現(xiàn)場會先保存數(shù)據(jù),然后在返回的時候恢復(fù)回來。這里只有關(guān)閉上一個緩沖區(qū),開啟一個新的緩沖區(qū),關(guān)閉這個緩沖哦過去,開啟另外一個緩沖區(qū)。
至此,我們看到一個簡單的PHP的MVC框架。如果你有興趣可以自己開發(fā)一個MVC框架,或者更深入點(diǎn)的HMVC。
- PHP從零開始打造自己的MVC框架之類的自動加載實(shí)現(xiàn)方法詳解
- PHP從零開始打造自己的MVC框架之入口文件實(shí)現(xiàn)方法詳解
- php打造屬于自己的MVC框架
- php實(shí)現(xiàn)最簡單的MVC框架實(shí)例教程
- php實(shí)現(xiàn)簡單的MVC框架實(shí)例
- 搭建自己的PHP MVC框架詳解
- PHP簡單的MVC框架實(shí)現(xiàn)方法
- PHP MVC框架路由學(xué)習(xí)筆記
- PHP仿tp實(shí)現(xiàn)mvc框架基本設(shè)計思路與實(shí)現(xiàn)方法分析
- PHP URL路由類實(shí)例
- php編寫一個簡單的路由類
- PHP從零開始打造自己的MVC框架之路由類實(shí)現(xiàn)方法分析
相關(guān)文章
Laravel5.1 框架數(shù)據(jù)庫查詢構(gòu)建器用法實(shí)例詳解
這篇文章主要介紹了Laravel5.1 框架數(shù)據(jù)庫查詢構(gòu)建器用法,結(jié)合實(shí)例形式詳細(xì)分析了laravel5.1框架查詢構(gòu)造器相關(guān)原理、使用技巧與操作注意事項,需要的朋友可以參考下2020-01-01php二維數(shù)組用鍵名分組相加實(shí)例函數(shù)
php二維數(shù)組以鍵名進(jìn)行分組相加的實(shí)例程序2013-11-11PHP file_get_contents函數(shù)讀取遠(yuǎn)程數(shù)據(jù)超時的解決方法
這篇文章主要介紹了PHP file_get_contents函數(shù)讀取遠(yuǎn)程數(shù)據(jù)超時的解決方法,本文直接給出解決方法代碼,需要的朋友可以參考下2015-05-05ThinkPHP框架實(shí)現(xiàn)定時執(zhí)行任務(wù)的兩種方法分析
這篇文章主要介紹了ThinkPHP框架實(shí)現(xiàn)定時執(zhí)行任務(wù)的兩種方法,結(jié)合實(shí)例形式分析了2種被動執(zhí)行定時任務(wù)的相關(guān)操作技巧與注意事項,需要的朋友可以參考下2018-09-09