怎麼一步步編寫簡單的PHP的Framework(十)

      以前講了這麼多,實際上都只是爲框架搭建了一個架子而已,框架裏面尚未什麼東西, 從今天開始,我就開始從Controller,Model,View這幾塊兒來分別介紹一下。 php

       PS:以前的不少內容我都沒有細講,就好比路由,真正的框架路由確定不是經過GET方式指定的,而是正則,而且它須要兼容多個Server,多種方式。 數組

      咱們知道全部的請求都須要通過控制器,因此首先仍是說一下控制器。 服務器

       咱們以前已經 說過控制器的概念了,可是這個控制器仍是功能太弱了,由於它只是從功能上是控制器,框架並無爲它作任何事情,框架實際上能夠實現一些經常使用的功能,而後用戶定義的控制器繼承它,這樣用戶能夠少寫不少代碼的。 框架

       以前的控制器是: 函數


<?php
class IndexController {
	public function index() {
		echo 'Hello world';
	}
}
       如今假設框架已經實現了控制器的一些基本功能,這個類咱們稱爲Controller.php,那麼如今代碼就變成了:



<?php
class IndexController extends Controller {
	public function index() {
		echo 'Hello world';
	}
}
         這樣作有什麼好處呢,因爲Controller繼承了Base,因此IndexController也擁有了Base的功能,這樣就不須要用戶再編寫不少捕獲的代碼等。


          固然,這樣作也有缺點,測試IndexController的時候比較麻煩。 測試

          今天我先說一下控制器比較基礎的兩個功能,跳轉和轉發。 ui

           首先是跳轉,這個用的太廣泛了,好比用戶登陸的時候,點擊登陸按鈕,進入後臺處理的頁面,處理完畢以後就須要跳轉,那麼怎麼實現跳轉呢? this

           有幾種方式: url

           第一種:<script>location.href = "XXXX";</script>在JS中實現跳轉; code

           第二種:header("Location:url");具體使用可查看PHP手冊;

           第三種:在HTML的meta中設置refresh;

           因爲header調用的時候若是以前頁面已經有輸出,跳轉會失效,因此須要結合meta的refresh一塊兒使用,固然,你也能夠直接使用JS的這種方式來實現,只是我不太喜歡這種方式,由於我以前使用這種方式實現跳轉的時候出過問題。

           如今咱們定義跳轉這個函數的名字爲_redirect,爲何前面加上_呢,這也是個人一個習慣,對於函數,只要不是public,我都使用_做爲前綴。可能你們會問了,爲何不設置這個函數爲public呢,由於用戶編寫的控制器也只會繼承它而不會直接調用,因此我設置成爲了protected。

              

protected function _redirect(Array $arr) {


}


         暫時能夠將功能弄得弱一點,假設跳轉的參數經過數組傳遞過來,那麼咱們可使用類型提示(Array $arr)這種方式來搞定,若是傳遞的參數不是數組,那麼直接會報錯。

         咱們使用的方式能夠是這樣:


$this->_redirect(array(
        'action' => 'test',
        'controller' => 'Test',
        'param1' => '1'
));
           它表明的意思是跳轉到Test這個控制器的test這個Action,而且還傳遞了一個參數,這個參數名爲param1,值爲1。



protected function _redirect(Array $arr) {
		$controller = empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //設置了默認的控制器
		$action = empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //設置了默認的Action
		array_key_exists('controller',$arr) || $arr['controller'] = $controller;
		array_key_exists('action',$arr) || $arr['action'] = $action;
		$str = '/?';
		foreach($arr as $key => $val) {
			if(!is_int($key)) {
				$str .= ($key . '=' . $val . '&');
			}
		}
		$str = substr($str,0,strlen($str) - 1);
		Response::redirect($str);
	}


            這個就是我剛剛手寫的跳轉代碼,實際上就是把傳遞的數組拼接一下而後組成一個字符串,這個字符串就能夠當作是一個URL,因爲如今沒有對Route.php進行更多的處理,對於localhost/demo2/index.php?controller=a這種URL它跳轉就會出錯,暫時只支持localhost/index.php?controller=a這種URL,還有$controller和$action的獲取和Route.php中的代碼重複了,這些都須要在後面真正實現路由的時候再講解,暫時就這麼看看吧,雖然我本身都感受這樣的代碼很噁心。

            可能你們都注意到了,當這個函數拼接到URL以後,是直接調用了Response的redirect方法,這是爲何呢?

             第一:有可能在真正應用中,咱們直接在控制器中調用$this->_redirect知足不了咱們的需求,這個時候咱們就須要直接調用Response::redirect,好比跳轉到百度首頁就只能調用Response::redirect("http://www.baidu.com");

             第二:從邏輯上,跳轉是一個服務器對客戶端的響應,因此須要寫在Response中,具體的可參照Java。

             那麼咱們又必須新建一個Response.php這樣一個文件:


<?php
class Response extends Base {
	public static function redirect($url) {
		if(is_string($url)) {
			if(!headers_sent()) {
				header("Location:" . $url);
				exit();
			} else {
				$str = '<meta http-equiv="Refresh" contect="0;url='.$url.'">';
				exit($str);
			}
		} else {
			//錯誤處理
		}
	}
}
             這裏的邏輯比較簡單,實際上就是斷定是否有輸出,沒有輸出那麼就直接使用header("Location")進行跳轉,若是有輸出,那麼使用meta的refresh跳轉。


             注意:實際上還能夠在這個跳轉上開發更多的功能,可是因爲我只是大概講一下,因此更多的內容就不寫了,有興趣的人能夠去Toper上面看看。

             這樣,一個比較簡單的跳轉就完成了,那麼怎麼實現轉發呢?

              能夠簡單這樣理解,轉發實際上就是再調用了一下某一個Controller的某一個Action。

               因此這樣咱們就能夠比較簡單的實現轉發了:


protected function _forward(Array $arr) {
		$controller = empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //設置了默認的控制器
		$action = empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //設置了默認的Action
		if(array_key_exists('controller',$arr)) {
			$controller = $arr['controller'];
		}
		if(array_key_exists('action',$arr)) {
			$action = $arr['action'];
		}
		$controller .= 'Controller';
		if($controller === get_class()) {
			if(method_exists($this,$action)) {
				$this->$action();
			} else {
				//時間有限,不寫邏輯了
			}
		} else {
			if(class_exists($controller)) {
				$class = new $controller();
				if(method_exists($class,$action)) {
					$class->$action();
				} else {
					//時間有限,不寫了
				}
			} else {
				//時間有限,不寫了
			}
		}
	}
                實際上邏輯上就是斷定一下要調用的Action是否屬於本控制器,若是是本控制器,直接調用$this->$action()便可,不然,須要實例化這個控制器,即$class = new $controller(),而後再調用這個Action。


                原本覺得半個小時就能夠寫完,結果寫了一個小時了,因爲時間超出個人預算,因此代碼都是手寫的,不知道有不有什麼語法錯誤什麼的,反正看看思路就OK了。

                代碼點此下載

相關文章
相關標籤/搜索