PS - 個人博客原文:CI框架入門筆記
當前(2019-03-22)CodeIgniter 框架的最新版本是 3.1.5,於2017年6月發布,距今快兩年了也沒有更新,這與 Laravel 的更新速度相比差距太大了。因為確實,它是一個很古老的框架了(第一個版本在2006年發布),當初的設計原則,開發環境與現在都已經大為不同。它有自己的設計原則,有相配套的一大堆工具、庫,使用這些現有的工具就已經能很好地滿足日常開發所需。
雖然已經是2019年了,但是公司用的框架是CI框架,所以我也應當快速適應CI框架的開發模式。本文就針對CI框架開發中的一些重點問題進行梳理和記錄。CI框架的官網文檔( http://codeigniter.org.cn/user_guide/index.html )很完善,但是我覺得仍然有必要整理出自己的一套實用規則。
就一個常規PHP框架來說,我認為應當包含這幾個部分:
- (1)index.php 或全局 App 對象,一個提供統一入口,一個提供容器資源管理
- (2)路由控制
- (3)請求和響應對象的封裝,輸入數據過濾和驗證,輸出數據的驗證和轉義,各種輸入輸出方法
- (4)MVC 分層,控制器,模型和視圖層,以及 Service 層
- (5)數據庫操作:數據庫驅動、查詢構造器、通用的查詢方法
- (6)文件存儲、緩存管理
- (7)Session, Cookie管理
- (8)安全性、配置、國際化、自動加載、第三方擴展機制
- (9)常見的工具類(其實應當通過擴展提供)
- (10)模板語言(這個不是必要的,因為PHP本身就能輸出)
入門
了解框架
我們下載好CI框架解壓之后的初始目錄是下面這樣的:
application/
controller/
cache/
config/
controllers/
core/
helpers/
hooks/
language/
libraries/
logs/
models/
third_party/
views/
.htaccess
index.html
system/
core/
database/
fonts/
helpers/
language/
libraries/
index.html
.gitignore
composer.json
index.php
其中,我注意到,每個目錄下面都有一個index.html文件,其內容也都是一樣的:403 Forbidden。這是為了防止意外訪問嗎?
application/
就是是項目目錄,就是我們實際的項目代碼存放處,下面分了很多子目錄,看名字就知道會放哪種功能的代碼,這些子目錄目前除了包含一個 403 index.html 文件,沒有別的內容。system/
就是框架目錄,下面就是框架代碼。
根目錄的 index.php
是整個項目的唯一入口點。index.php 主要的功能是定義了一些系統目錄,包括項目目錄、視圖目錄、框架目錄,在最后調用了框架目錄下的 core/CodeIgniter.php
,這個文件是CI框架的入口點和結束點,即包含了CI框架的所有生命周期。它的執行過程如下:
* 定義全局常量,加載全局函數,環境檢測,PHP版本判斷
* 注冊錯誤處理函數,自動加載函數(Composer判斷)
* 加載一系列類:
* Hooks,鈎子函數類
* Config,配置類
* UTF-8,
* URI,(CI_URI)
* Router,路由類
* Output,輸出類
* Security,安全類
* Input,輸入類
* Lang,多語言類
* 加載控制器類
* 判斷控制器類、方法是否存在,不存在則404
* 調用控制器前置鈎子函數
* 實例化控制器
* 調用控制器后置鈎子函數
* 調用控制器方法(業務邏輯)
* 輸出響應
* 調用系統后置鈎子函數
在實例化控制器這一部分中,注意到它定義了一個靜態實例,代碼如下
# core/CodeIgniter.php
# 此處定義了一個全局函數 get_instance(),返回一個靜態對象。
function &get_instance()
{
return CI_Controller::get_instance();
}
// ...
$CI = new $class();
# core/Controller.php
class CI_Controller {
public function __construct()
{
self::$instance =& $this;
// ...
}
public static function &get_instance()
{
return self::$instance;
}
}
在之后的任意位置的代碼中,只要通過 get_instance()
方法就能獲取唯一的 Controller
對象,它其實就是CI框架中的“容器”。
調用控制器方法(即業務邏輯)通過這段代碼調用執行:
call_user_func_array(array(&$CI, $method), $params);
業務邏輯
在調用上述代碼之后,就進入到 application/
目錄下我們的實際的業務功能代碼。上面的 &$CI
就是 $class
名對象,即根據URL參數解析對應到 application/controllers/
目錄下的實際控制器類文件名。具體的映射方法可以看文檔( https://codeigniter.org.cn/user_guide/general/controllers.html )
比如,有一個 URI 是這樣的:/welcome,會解析為 application/controller/Welcome.php
文件,它應該是一個繼承自 CI_Controller
的類。/welcome
相當於 /welcome/index
,URI 的第一個部分是控制器,第二個部分是控制器的方法,所以這個 URI 會調用 Welcome 類的 index 方法。URI 中只有第一個部分時,那第二個部分默認是 index。在控制器方法中我們編寫實際的業務功能代碼。示例如下:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Welcome extends CI_Controller {
public function index()
{
$this->load->view('welcome_message');
}
}
加載功能模塊(類庫)
CI框架默認提供了模型、視圖、輔助函數、日志、配置、緩存等功能。這些功能模塊默認是不加載的,需要在控制器中進行手動按需加載。另外,libraries
目錄中的類庫,也是同上的加載機制,只不過這里面的類庫是開發人員自己寫的功能模塊。下面詳細描述每種模塊的加載方式。
model
首先,我們看到上節中的 $this->load
,這就是CI框架中的加載類(CI_Loader
),我們所有需要的類庫、模塊都可以通過它來加載。這個加載類實際上是“容器”的一個對象屬性,從下面 model 的源碼可以看到,實際上所有容器的“對象屬性”都可以直接通過模型獲取:
class CI_Model {
public function __get($key)
{
return get_instance()->$key;
}
}
在業務功能開發中,使用最頻繁的就是模型了。CI框架提供的模型類是 core/Model.php
即 CI_Model
類,它其實就是一個很簡單的類,沒有提供任何內容,如果你需要使用模型,那就應該在 application/models
下面新建一個繼承自 CI_Model
的類,然后在使用模型的地方先使用 $this->load->model()
來加載它,加載了之后,就能直接通過 $this->{模型名}
訪問該模型對象:
$this->load->model('User_model');
$result = $this->User_model->get_one($id);
model()
默認到 application/model/
目錄下尋找 user_model.php
這個文件名,然后加載 User_model
類,實例化之,並賦值作為“容器”的對象屬性,屬性名就是模型名。model()
方法部分源碼:
public function model($model, $name = '', $db_conn = FALSE)
{
// ...
$model = new $model();
$CI->$name = $model;
}
model()
方法的第二個參數可以指定屬性名,示例代碼如下:
$this->load->model('User_model', 'USER');
$result = $this->USER->get_one($id);
CI框架並沒有規定模型類中應當放什么,這取決於你。通常我們會寫一個 Base_Model 類,用來提供各種查詢方法,然后在具體的模型類中實現具體模型對應的業務方法。在控制器中直接調用模型類的業務方法獲取數據。在CI框架中我們使用模型類的理由就是封裝邏輯,不然所有的邏輯都寫在控制器中(我看到項目中現在有一部分老代碼就是這樣做的!)。CI也沒有提供進一步Service層,這由你自己決定。
database
一般在 Base_Model 類中,我們會加載一下默認的數據庫,封裝若干查詢方法:
class Base_Model
{
function __construct()
{
// ...
$this->load->database();
}
function save() {}
function get_one() {}
function get_all() {}
function update() {}
function delete() {}
function query() {}
...
}
調用了 database()
之后,會實際化一個 CI_DB
對象,並賦值為控制器的 $db
屬性,database()
部分源碼如下:
public function database($params = '', $return = FALSE, $query_builder = NULL)
{
// ...
if ($return === TRUE)
{
return DB($params, $query_builder);
}
// ...
$CI->db =& DB($params, $query_builder);
}
可以看到,當第二個參數為 TRUE 時,直接返回 DB 對象,並不會設置為控制器的屬性。這一點在具體的模型類中會有用,比如我們會在具體的模型類中加載不同的數據庫類:
class Article_model extends Base_Model
{
public function __construct()
{
$this->db = $this->load->database('myDb2', TRUE);
}
}
這里獲取 myDb2 數據庫對象,避免了污染全局的 $CI->db
對象。
最復雜的部分其實已經介紹完了,下面是一些常用的類庫介紹
helper
在使用自定義的輔助函數之前,需要加載一下,語法同上。只不過它會去 application/helpers
目錄下去尋找相應文件。
$this->load->helper('dt');
libraries
application/libraries
目錄下的類,加載語法同上,使用其實沒什么問題,值得注意的是如何寫一個自己的類庫。
$this->load->library('t');
config
配置文件也需要加載,然后 item
方法直接訪問配置文件中的值,自定義的配置需要設置為 $config
數組的屬性的形式。
# application/config/d.php
$config['ddd'] = '';
# Controller
$this->load->config('d');
$data = $this->config->item('ddd')
view
由於現在CI項目只是作為API,所以並無視圖,但為了介紹,下面提供了一個示例:
# application/views/welcome.php
# Controller
$this->load->view('welcome', $data);
這個視圖文件就是一個普通的PHP文件,可以直接輸出。
driver
驅動其實也是類庫的一種。
總結
就我入手這個CI項目的一個月經驗來看,日常業務開發就是這些東西,后面也不過是在這個框架基礎上不斷深入和完善。所以這篇文章作為入門總結應該算是足夠了。