天天看點

ci架構了解流程

入口檔案入口檔案主要完成下列工作: 1. 指定 CodeIgniter 架構所在目錄; 2. 定義 APPPATH 常量,訓示應用程式檔案根目錄; 3. 載入 codeigniter/CodeIgniter.php 檔案,啟動架構。 codeigniter/CodeIgniter.php 檔案這個檔案是 CodeIgniter 的基本檔案,主要完成初始化 CodeIgniter 架構和啟動應用程式兩項工作。 1. 執行個體化 CI_Benchmark,這個類用于标記應用程式執行消耗的時間; 2. 載入應用程式的配置檔案 require(APPPATH.?config/config‘.EXT); 3. 執行個體化 CI_Config,這個類用于将數組封裝為可以操作的配置服務; 4. 執行個體化 CI_Router,這個類用于分析 URL 請求,确定要執行的控制器和動作; 5. 執行個體化 CI_Output,這個類提供輸出内容的緩存和檢查服務; 6. 通過 $OUT->_display_cache($CFG, $RTR) 嘗試輸出緩存内容,如果成功,則結束程式運作; 7. 判斷控制器類定義檔案是否存在。如果不存在則通過 show_404() 顯示錯誤資訊; 8. 執行個體化 CI_Input,這個類提供對 $_GET、$_POST 的通路手段,并封裝了一些過濾方法; 9. 執行個體化 CI_URI,這個類提供對 URL 的分析、構造服務; 10. 執行個體化 CI_Language,這個類提供多語言字元串映射服務; 11. 載入 codeigniter/Base4.php 或者 codeigniter/Base5.php; 12. 載入 libraries/Controller.php; 13. 載入控制器類定義檔案; 14. 執行個體化控制器類; 15. 如果控制器使用了 scaffolding 功能,則調用控制器的 _ci_scaffolding() 方法,否則調用控制器動作方法; 16. 通過 $OUT->_display(); 輸出内容($OUT 是 CI_Output 的執行個體)。 CI_Benchmark 這個類很簡單,就是用 microtime() 函數記錄時間點,并提供 elapsed_time() 方法來計算兩個時間點之間消耗的時間。這個類功能不多,但是很實用。CodeIgniter 中大部分類都是這種設計思想,值得稱贊! CI_Config 這個類其實是在内部維護了一個數組,用來記錄應用程式的設定(類似 Windows 系統資料庫)。這種簡單的封裝可以強制應用程式按照固定的規範通路設定,同時又不将設定儲存為全局變量,避免無意中遭到破壞或篡改。 CI_Router CI_Router 功能很單一。CI_Router 首先分析出應用程式目前使用的 URL 模式:PATHINFO 或普通模式。接下來從 URL 位址中分析出控制器名字、動作名以及參數名和參數值。分析的結果儲存為 CI_Router 對象執行個體的成員變量。這裡比較有特點的是,CI_Router 可以根據開發者在應用程式設定裡面定義的模式來分析 URL,而不是使用某種固定的模式。 CI_Output CI_Output 有兩個主要功能:獲得應用程式執行的所有輸出内容和輸出緩存服務。應用程式執行的輸出結果都會儲存為 CI_Output 的成員變量。然後根據應用程式設定,CI_Output::_display() 方法會調用 CI_Output::_write_cache() 方法将輸出内容緩存起來。下一次當使用 CI_Output::_display_cache() 時如果緩存已經建立了,并且沒有過期,則會直接輸出緩存内容。在 CI_Output::_write_cache() 中,是根據 URL 位址和 URL 參數來确定緩存 ID 的。是以即便是同一個控制器和動作,隻要使用不同的 URL 參數,也會緩存不同的内容。這個類的功能很簡單,是以在許多動态頁面是無法使用的。例如使用者登入前和登入後,通路同一個控制器和動作并使用相同的 URL 參數,頁面内容也有可能是不同的。這時,CI_Output 的緩存就不能使用。因為從本質上來說,CI_Output 提供的緩存是在應用程式之外的,是以應用程式無法根據目前狀态來決定是否緩存頁面。當一個頁面被緩存後,對該頁面的通路實際上根本就不會執行應用程式代碼,而是由 CI_Output 取出緩存内容直接就輸出了。 CI_Input CI_Input 是輸入資料過濾器,并且提供了對 $_GET、$_POST 的封裝服務。例如用 CI_Input::post() 方法來通路 $_POST。由于多了這層封裝,CI_Input 可以在 post() 方法中對資料進行更多的過濾。這種封裝從出發點上看,是很不錯的。但是這也會造成一些問題。例如 CI_Input 隻有在調用 post() 方法時才能進行過濾。如果應用程式使用 $_POST 直接擷取資料,那麼實際上就繞過了安全屏障。如果應用程式使用了第三方庫,那麼這種風險更大,因為第三方庫很可能會直接使用 $_POST 等全局變量。是以有些開發者認為過濾應該是全局的,即在架構初始化時,就對所有輸入資料進行過濾。但初始化時的全局過濾靈活性很差,要麼全過濾,要麼都不過濾,沒法做到對個别資料的單獨過濾。 CI_Input 的另一個問題,就是沒有處理 magic_quotes。不管 magic_quotes 設定為什麼,CI_Input 都沒有對資料進行相關的處理。這樣一來,如果伺服器的 magic_quotes 設定不同,那麼應用程式得到的資料也是不一緻的。後來檢視資料庫驅動的代碼,發現 CI_Input 将對 magic_quotes 的處理放到了資料庫驅動中。這種設計是有很大缺陷的!如果應用程式取得資料後,并不是存入資料庫(例如直接顯示或存入檔案),那麼就必須自行判斷 magic_quotes 的狀态。這種判斷不但煩瑣,而且容易遺忘。是以架構有責任将所有資料整理為一緻的格式,要麼是應用 addslashes() 轉義過後的資料,要麼是沒有轉義的資料。奇怪的是 CI_Input 卻對輸入資料的字段名進行了 magic_quotes 檢查,并應用了 addslashes()。這是為了讓資料庫字段名不會成為 SQL 注入攻擊的根源。甚至,CI_Input 還會将 \n\r\n\r 替換為 \n。這種随意篡改原始資料的做法,非常不可取。總之,我個人認為 CodeIgniter 在這部分的設計是很糟糕的。不過要改善也很簡單,幾行代碼就可以了。然後修改一下資料庫驅動。但是由于已經有許多采用 CodeIgniter 開發的應用程式,是以這樣的更新改動,影響是非常大的。 CI_URI 由于 CodeIgniter 允許應用程式定義 URL 映射模式,是以需要專門的工具來生成 URL 位址。CI_URI 就是完成這些工作的。 CI_Language 這個類可以載入不同的語言檔案。然後應用程式就可以用 CI_Language::line() 方法取出某個項目的對應翻譯。每個語言檔案就是一個名值對數組。是以 CI_Language::line() 以項目名做為鍵名,就可以查詢到對應的翻譯。 codeigniter/Base codeigniter/Base4.php 和 codeigniter/Base5.php 功能一樣,隻不過分别适用于 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 中運作就會出錯。 Controller 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 的設計,沒什麼好說的,一個字:爛! CI_Loader 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 語句,而不用自己寫,提高應用程式在不同資料庫之間移植的能力。 “腳手架”功能 CodeIgniter 中提供了基本的“腳手架”功能,可以用幾行代碼即實作一個對某個資料表進行 CRUD 的界面。這和 phpMyAdmin 中的資料浏覽、編輯頁面類似,當然功能要簡單得多。 “腳手架”有什麼實用價值,衆說紛纭。但普遍認同的一點就是“腳手架”功能為處于開發初期的應用程式提供了管理資料的界面。開發者可以在後期替換掉“腳手架”的界面。但是,CodeIgniter 也太簡單了,就隻有 CRUD 操作,還不如 phpMyAdmin 好用。其他 CodeIgniter 還有許多其他的類和助手。這些類基本上都屬于提供各種輔助服務的範疇。有些類很不錯,像圖檔操作。但大部分類和助手實在太簡單,缺乏實用價值。像資料驗證助手,隻能做很基本的驗證,在絕大多數應用程式裡面都不能滿足要求。總結咳——咳——,總結時間到了。再次鄭重申明:本文所有文字均為作者個人了解和感想。作者盡量做到客觀,但人非聖賢,難免參雜個人好惡在其中。是以如果你看到不爽的文字,請自動無視,謝謝合作! CodeIgniter 是一個:簡單不簡潔、好用但可能不夠用的工具。幾個步驟就可以讓你的應用程式跑起來,是以簡單。因為簡單,是以好用。但糟糕的設計增加了複雜度,簡單的表面下是錯綜複雜的對象關系。因為過于簡單,是以可能不夠用。如果你隻是開發很簡單的應用程式,那麼 CodeIgniter 完全可以滿足你的需求。而且你也會獲得愉快的體驗。但如果應用程式具有一定的複雜度,CodeIgniter 就可能起到反作用。因為 CodeIgniter 在幾個主要類上的糟糕設計,你的應用程式最終也會受到牽連。而且 CodeIgniter 缺乏許多必須的服務,例如通路控制、使用者管理、自動化的資料表關聯處理、複雜緩存等。這些服務對于一個較為複雜的應用程式來說都是必須的。如果用 CodeIgniter 作為應用程式        

ci架構了解流程
ci架構了解流程

 收藏夏天