
Yii2
框架是一個非常龐大但是並不臃腫的
php
框架。
使用 Yii2
框架,可以極大的提升開發效率。
秉持着要知其然也要知其所以然的思想,花了一周的時間,看了 linuor 的 《深入理解Yii2.0》
根據我的理解梳理了一下 Yii2
框架的整體結構。
此后簡稱框架。
1. Yii 框架基礎
此框架的三個基礎概念:
- 屬性
- 事件
- 行為
畢竟三生萬物。
1.1 屬性
一看到這個詞可能很迷惑,那么什么是屬性呢。
比如說,你現在正在玩一款角色扮演的游戲,角色雙手分別有一把 十方大劍
,一個 十方盾牌
。
那么從面向對象的角度而言,你的角色類需要有 左手武器
和 右手武器
兩個成員變量。
並且角色類可以從 左手武器
和 右手武器
中獲得 力量屬性。
這么一說屬性這一概念,就很容易理解了。
1.2 事件
還是以角色扮演游戲為例(這里要對不起不玩游戲的朋友了,原諒我匱乏的想象力,或者你有好的比喻也可以跟我說),游戲里面都有任務系統,而任務會分為主線任務和支線任務,在做主線任務是通常會觸發一系列的支線任務。這些支線任務的觸發就可以認為是一系列的事件。
1.3 行為
框架的行為也可以使用角色扮演游戲來舉例,你在 無盡荒原 撿到了一本魔法書,學會了禁咒魔法,召喚神龍
。
於是你把程序一改,給你的類加一個召喚魔法的方法,這是不可能的。我們必須讓你的類擁有一個動態添加方法的功能,於是所有拿到魔法書的人或者限定職業的人,都能學會這個禁咒,這就是 行為
的作用,動態給類增加方法。
以上,有了這三個 框架 實現的武器,我們才能更快速和靈活的搭建上層的程序。
2. 設計模式
2.1 依賴注入
框架采用了現在世面上面最常見的一種模式, MVC
模式,具體細節不再闡述,現在網上關於 MVC
的介紹可以說是爛大街了。
MVC
只是一種大框架上的設計模式,其核心思想是分層,最終目的是解耦。框架在 MVC
的基礎上,應用了很多經典的設計模式以及后來發展的設計模式。
其中最重要的就是:
- 依賴注入
- 服務定位器。
那么什么是依賴注入呢
先來一段沒有依賴注入的代碼:
<?php
// 這段代碼將 db1 中的t1表的數據備份到 db2 庫的 t2 中。
// 所使用的變量都在邏輯過程中申請。
class Archive {
public function doArchive() {
$dataDB = new DB1();
$data = $dataDB->query("select * from t1");
$backDB = new DB2();
foreach ($data as $key => $value) {
$backDB->query("insert into t2 values ". implode(',', $value));
}
die("備份完成");
}
}
那么如果再來一個需求,讓你把 db3
的數據備份到 db4
中,邏輯相同,那么你該怎么辦呢?
有道友就說了,那還不簡單, copy & paste
,搞定。
於是,你多了一個方法或者一個類。
一直到你的方法和類增加到 10 個以上時,你都不會有什么感覺。
直到產品說現在需求變了,讓你把所有的備份庫都改成 db3,那么,你就要把手頭的工作都停下來,然后將代碼中的備份庫全改成 db3。
而且,如果你使用的是靜態語言,那么,你就要把代碼再重新編譯一遍。關於靜態語言的編譯時長:為什么c編譯需要這么長時間
那么就有了下面這種:
class ArchiveNew {
private $originDb;
private $backDb;
public function doArchive() {
$dataDB = new $this->originDb();
$data = $dataDB->query("select * from t1");
$backDB = new $this->backDb();
foreach ($data as $key => $value) {
$backDB->query("insert into t2 values ". implode(',', $value));
}
die("備份完成");
}
/**
* @param mixed $originDb
*/
public function setOriginDb($originDb)
{
$this->originDb = $originDb;
}
/**
* @param mixed $backDb
*/
public function setBackDb($backDb)
{
$this->backDb = $backDb;
}
}
這個方法,把所有的需要用到的數據庫都放到了外面來進行管理,那么我們稱這幾個數據庫為依賴,稱在外部設置數據庫的行為為依賴注入。
這只是一種很原始的使用方式,你可以繼續延伸,將外部依賴放到統一的地方去管理,那么就有了注入容器(di Container)
2.2 服務定位器
服務定位器像是一個注冊中心,向服務定位器中注冊一個 a 服務,可以使用 a 這個名字從服務定位器中取出這個服務。
$locator = new ServiceLocator;
$locator->set('a');
$locator->get('a');
服務定位器是基於依賴注入的,在獲取服務時,其實會在容器中先注冊一個服務。
3. 請求與響應
這一節其實主要講的就是請求。
請求的內容會比較多,還涉及到網絡協議等知識,這里不再展開。
列舉一下重要的幾點:
- 路由美化 (將原始的路由修改成比較美觀的地址,見導圖 請求部分)
url
解析(將美化過的路由解析成原始請求)- 請求管理 (包括請求頭部,請求體,解析器等等)
4. 數據庫
雖然這張圖有些搞笑,但是無疑說明了 數據庫(MySQL
)對於 PHP
的重要性。
4.1 類型轉換
框架為了兼容各種數據庫,對數據類型做了多層封裝,並且做了一系列的轉換規則:
4.2 事務
框架支持事務嵌套,但是嵌套的事務必須成對出現(注意!!!)。
看到事務這一節的時候,正巧同事出了一個 bug
,在腳本的循環中,出錯之后沒有 commit
或者 rollback
,導致,接下來的生成的事務都成了這個事務的子事務。而框架的嵌套事務,實際上是使用代碼模擬的,如果父事務沒有提交,那么子事務永遠不會提交。
4.3 事件
const EVENT_INIT = 'init'; // 初始化對象時觸發
const EVENT_AFTER_FIND = 'afterFind'; // 執行查詢結束時觸發
const EVENT_BEFORE_INSERT = 'beforeInsert'; // 插入結束時觸發
const EVENT_AFTER_INSERT = 'afterInsert'; // 插入之前觸發
const EVENT_BEFORE_UPDATE = 'beforeUpdate'; // 更新記錄前觸發
const EVENT_AFTER_UPDATE = 'afterUpdate'; // 更新記錄后觸發
const EVENT_BEFORE_DELETE = 'beforeDelete'; // 刪除記錄前觸發
const EVENT_AFTER_DELETE = 'afterDelete'; // 刪除記錄后觸發
這些事件和其他事件沒有什么分別,都是在特定的時候會執行,就不一一闡述了。
4.4 樂觀鎖和悲觀鎖
框架自帶了樂觀鎖的實現,如果有類似需求,可以在重載 yii\db\ActiveRecord::optimisticLock()
方法,返回數據庫中的版本號字段即可。在更新與刪除時,框架會做相應的操作,來保證,更新的數據是自己拿到的數據,而不是被別人給修改過了。
因為悲觀鎖不適用於 web
應用,所以框架並沒有實現悲觀鎖。
5. 總結
Yii2
框架的整體架構要比 Yii1
版本提升了一個逼格。並且得益於 namespace
特性,框架的目錄結構清晰了很多。
對於不想了解框架底層實現的程序員,可以僅僅了解基本使用,就可以上手寫業務代碼,而不會影響你的開發效率。
只有當你遇見一個很蛋疼的框架內部報錯或者 bug
的時候,你才會想到,我去,這怎么這么蛋疼,怎么框架還會有 bug
,但是很多時候是環境、配置或者特定的代碼導致的。如果不了解框架的內部邏輯,這個報錯或者 bug
會耽誤你很長時間。