天天看點

codeIgniter架構基本結構 (二)

[color=blue][b][size=medium]codeigniter/Base[/size][/b][/color]

[color=red]codeigniter/Base4.php[/color] 和 [color=red]codeigniter/Base5.php [/color]功能一樣,隻不過分别适用于 PHP4 和 PHP5 而已。其中定義了 CI_Base 類和一個非常重要的 get_instance() 函數。

get_instance() 函數傳回一個 CI_Base 類在整個應用程式中的唯一執行個體。

這裡有一個有趣的發現。Base4.php 和 Base5.php 中的 CI_Base 和 get_instance() 有這完全不同的實作。

在 Base4.php(對應 PHP4)中,CI_Base 直接繼承自 CI_Loader。CI_Base 執行個體化時,将 自身的引用儲存到了 CI_Base::$load 中。也就是說 CI_Base 執行個體的 $load 實際上指向自己。然後 $load 被複制到一個名為 $OBJ 的全局變量。

在 PHP4 版的 get_instance() 函數中,如果檢查到 $CI(這是 CI_Base 的執行個體,也就是控制器的執行個體)存在,就傳回 $CI,否則傳回全局變量 $OBJ->load。但由于在 PHP4 中,$OBJ->load 實際上就是一個 CI_Base 的執行個體。是以。。。。是以。。。。。。還是傳回了一個 CI_Base 的執行個體。真搞不懂作者為什麼這樣寫,簡直要讓人發瘋。

不管怎麼樣,應用程式其他地方調用 get_instance() 都會獲得一個 CI_Base 的執行個體。

在 Base5.php(對應 PHP5)中,用一個 singleton 模式來解決了這個問題。是以 CI_Base 也不再需要從 CI_Loader 繼承了。不過這也留下了隐患(CI_Loader 執行個體要什麼時候擷取呢?),是以在 CI_Base 的繼承類 Controller 中,隻好通過判斷是否是運作 PHP5 來決定是不是要執行個體化一個 CI_Loader。

真的很無語啊,這種設計雖然可以用,但是很糟糕。在 PHP4 種,CI_Loader 的方法和成員變量暴露在了 CI_Base 中。如果應用程式不小心調用了這些方法或使用了這些成員變量。那麼應用程式在 PHP5 中運作就會出錯。

[size=medium][color=blue][b]Controller[/b][/color][/size]

Controller 類是所有控制器的基礎類。

Controller 執行個體化時會将 CI_Input、CI_Benchmark、CI_Config、CI_URI、CI_Output、CI_Language 的執行個體複制到 Controller 執行個體的成員變量中。然後根據應用程式設定,自動載入檔案。

但是這裡作者顯然沒有處理好,是以不得不用 `global $IN, $BM, $CFG, $URI, $LANG, $OUT;` 這樣的全局變量來傳遞幾個重要的對象執行個體。

Controller 本身并沒提供 model、helper 的載入服務。這些都由 CI_Loader 來提供。但是,CI_Loader 的各種載入服務,卻又用 get_instance() 擷取控制器的執行個體,然後調用 Controller(控制器都是 Controller 的繼承類哦)的 _ci_initialize()、_ci_init_database() 等方法來做初始化。

神啊!救救我吧!這種錯綜複雜的關系,真的要人命啊!

Controller 的 $ci_is_loaded 成員變量用于儲存已經載入的對象執行個體。是以每次用 Controller::_ci_load_model() 載入子產品後,都要将該子產品登記到 $ci_is_loaded,以避免重複載入。

Controller 裡面大部分是一些初始化各種服務的方法,例如初始化資料庫、Model 的方法。還有就是用 _ci_scaffolding() 調用 CodeIgniter 的“腳手架”功能。

對 Controller 的設計,沒什麼好說的,一個字:爛!

[size=medium][color=blue][b]CI_Loader[/b][/color][/size]

CI_Loader 提供各種載入服務,例如載入 Model、Helper、View 等。但是(我真的很痛恨“但是”這個詞),CI_Loader 卻需要 Controller 來完成初始化。那麼又是誰來調用 CI_Loader 呢?答案是 Controller。

這種緊密的耦合,完全是沒有必要的!

控制器開始執行

分析到這裡,終于進入應用程式的代碼了。應用程式控制器中,可以用 $this->load 來載入各種服務,然後就可以調用這些載入的服務了。

雖然 CodeIgniter 在 CI_Base、Controller 和 CI_Loader 上設計很糟糕,但開發者如果不在乎這些,那麼開發過程還是很愉快的。

下面我們再來看看 CodeIgniter 主要服務的特點。

資料庫通路

與大部分架構不同,CodeIgniter 的 Model 類沒有提供資料庫通路功能。所有資料庫操作都是通過資料庫驅動程式來進行的。

所有資料庫驅動均繼承自 CI_DB 類。等等,我怎麼找不到 CI_DB 類的定義呢?因為 CI_DB 類是在 Controller 中用 `eval(‘class CI_DB extends CI_DB_driver { }’);` 這行代碼來定義的。定義這樣一個空殼,估計是作者為以後擴充資料庫驅動留下的伏筆。

CodeIgniter 的資料庫驅動,功能都很簡單,和 AdoDB Lite 類似,但是缺乏 AdoDB Lite 那麼多的擴充庫。我個人認為反倒不如用 AdoDB Lite 來替換這部分。當然了,CodeIgniter 目前已經有不少資料庫驅動了,是以替換成 AdoDB Lite 好處不多。

CodeIgniter 也提供了一個 ActiveRecord 實作,不過這個 ActiveRecord 可沒有一點半點的“ORM”能力。但是 CodeIgniter 的 ActiveRecord 不需要為每一個資料表都構造一個執行個體。通常一個執行個體就可以處理多個資料表的操作。

例如 `$query = $this->db->get(‘mytable’);` 和 `$query = $this->db->get(‘mytable2′);` 就可以分别取得 mytable 和 mytable2 的資料。

說實話,作者可能用錯了名字。

CodeIgniter 中的“ActiveRecord”實際上是表資料入口模式——TableDataGateway。

CodeIgniter 中的 ActiveRecord 基本上隻是一個對資料表進行 CRUD 操作的公共接口。沒有提供 RoR、CakePHP、FleaPHP 等架構具有的資料表關聯自動處理能力。和自己寫 SQL 相比,沒什麼優勢。唯一的好處就是作者所說的可以讓 ActiveRecord 來生成這些簡單的 SQL 語句,而不用自己寫,提高應用程式在不同資料庫之間移植的能力。

[size=medium][color=blue][b]“腳手架”功能[/b][/color][/size]

CodeIgniter 中提供了基本的“腳手架”功能,可以用幾行代碼即實作一個對某個資料表進行 CRUD 的界面。這和 phpMyAdmin 中的資料浏覽、編輯頁面類似,當然功能要簡單得多。

“腳手架”有什麼實用價值,衆說紛纭。但普遍認同的一點就是“腳手架”功能為處于開發初期的應用程式提供了管理資料的界面。開發者可以在後期替換掉“腳手架”的界面。

但是,CodeIgniter 也太簡單了,就隻有 CRUD 操作,還不如 phpMyAdmin 好用。

其他

CodeIgniter 還有許多其他的類和助手。這些類基本上都屬于提供各種輔助服務的範疇。有些類很不錯,像圖檔操作。但大部分類和助手實在太簡單,缺乏實用價值。像資料驗證助手,隻能做很基本的驗證,在絕大多數應用程式裡面都不能滿足要求。