之前我們在入口檔案就直接調用了Route::run(),這樣做有不有什麼問題呢?
答案是有的!
有時候在進行路由之前和之後需要進行一些額外的處理,如果按照之前在入口檔案直接調用Route::run()的話,那麼這些處理過程只能寫在入口檔案中,但是入口檔案本不應該做這樣的事情,那麼我們該怎麼樣來解決這個問題呢?
我們引入一個前端控制器的概念,它相當於一個總控,所有外部的請求都在它的控制範圍內,那麼這些額外的處理是不是就可以放在這個控制器中了呢!!
好,我們現在先修改一下入口檔案:
02 |
defined('APP_PATH') define('APP_PATH',dirname(__FILE__) . '/..'); |
03 |
defined('FRAMEWORK_PATH') define('FRAMEWORK_PATH',APP_PATH . '/Library/Test'); |
04 |
defined('MODULES_PATH') define('MODULES_PATH',APP_PATH . '/UserApps/Modules'); |
05 |
defined('CONFIGS_PATH') define('CONFIGS_PATH',APP_PATH . '/UserApps/Configs'); |
06 |
include FRAMEWORK_PATH . '/function.php'; |
07 |
C(Config::factory(Config::PHP)); //寫入配置資訊 |
08 |
include FRAMEWORK_PATH . '/FrontController.php'; |
09 |
$frontController = FrontController::getInstance(); |
10 |
$frontController->run(); |
這段代碼實際上修改的內容不多,也就修改了:
1 |
include FRAMEWORK_PATH . '/FrontController.php'; |
2 |
$frontController = FrontController::getInstance(); |
3 |
$frontController->run(); |
這段代碼首先include了前端控制器這個檔案,然後執行個體化了前端控制器,然後調用前端控制器這個類的run方法。
為什麼不直接使用$frontController = new FrontController()?
如果你把握後面的代碼下載後運行會發現,這樣會報錯,為什麼呢?
這兒我需要介紹另外一個概念:單例模式。
單例模式就是在整個程式運行過程中只有一個執行個體,比如資料庫的串連,即使有很多類,但是可能這些類都公用資料庫的串連,那麼資料庫的串連就是單例的。
為什麼要使用單例呢?
大家可以想一下,前端控制器控制整個程式的運行,那麼它的確是不應該有多份的,是不是?
為了保證是單例的,所以我們一般使用一個靜態方法(getInstance)來執行個體化它,為了防止使用者直接new和複製一個對象,我們需要將__construct和__clone設定為私人。
03 |
private static $_instance = null; |
04 |
private function __construct() {} |
05 |
private function __clone() {} |
06 |
public static function getInstance() { |
07 |
if(null === self::$_instance) { |
08 |
self::$_instance = new Test(); |
11 |
return self::$_instance; |
15 |
$test = Test::getInstance(); |
16 |
$test2 = Test::getInstance(); |
如果執行這段代碼,會發現只輸出了一個1,說明執行個體化只執行了一次。
說了這麼多,大家應該都應該懂了吧,那就直接貼出前端控制器的代碼吧:
02 |
class FrontController { |
03 |
private static $_instance = null; |
04 |
private function __construct() {} |
05 |
private function __clone() {} |
06 |
public static function getInstance() { |
07 |
if(!(self::$_instance instanceof self)) { |
08 |
self::$_instance = new FrontController(); |
10 |
return self::$_instance; |
12 |
public function run() { |
當然,前端控制器中我是使用了instanceof來判定的,其實也可以直接用null === self::$_instance來判定。
這段代碼很簡單,實際上就把Route::run()轉移到FrontController了,其他的都沒有什麼變化。
大家可能注意到了,入口檔案的內容還是太多了,為什麼不能把入口檔案的東西遷移到FrontController呢,那麼那些可以遷移到FrontController呢,首先是定義的常量,其實只要使用者定義APP_PATH即可,其他的常量都可以由架構預設,其次是設定檔的寫入,這種內容架構完全可以自己搞定,所以,入口檔案的代碼變成了這樣:
2 |
defined('APP_PATH') define('APP_PATH',dirname(__FILE__) . '/..'); |
3 |
defined('FRAMEWORK_PATH') define('FRAMEWORK_PATH',APP_PATH . '/Library/Test'); |
4 |
include FRAMEWORK_PATH . '/FrontController.php'; |
5 |
$frontController = FrontController::getInstance(); |
6 |
$frontController->run(); |
我在此處留下了FRAMEWORK_PATH,是因為我要引用架構的檔案,其實也不定義FRMAEWORK_PATH,而改用include APP_PATH . '/Library/Test/FrontController.php'。
同樣,FrontController的代碼也發生了變化:
02 |
defined('APP_PATH') exit('未定義APP_PATH'); |
03 |
defined('FRAMEWORK_PATH') define('FRAMEWORK_PATH',APP_PATH . '/Library/Test'); |
04 |
defined('MODULES_PATH') define('MODULES_PATH',APP_PATH . '/UserApps/Modules'); |
05 |
defined('CONFIGS_PATH') define('CONFIGS_PATH',APP_PATH . '/UserApps/Configs'); |
06 |
include FRAMEWORK_PATH . '/function.php'; |
07 |
class FrontController { |
08 |
private static $_instance = null; |
09 |
private function __construct() { |
10 |
C(Config::factory(Config::PHP)); //寫入配置資訊 |
12 |
private function __clone() {} |
13 |
public static function getInstance() { |
14 |
if(!(self::$_instance instanceof self)) { |
15 |
self::$_instance = new FrontController(); |
17 |
return self::$_instance; |
19 |
public function run() { |
首先,這個檔案判定是否定義了APP_PATH,由於這個常量非常重要,其他路徑都依靠它,所以使用者如果不指定,程式就需要停止。其他的常量如果沒有定義,那麼就定義它,這個很簡單。
設定檔寫到了建構函式中了,因為不管getInstance調用多少次,建構函式只會被調用1次,而設定檔剛好也只需要寫入一次。