Yii2 框架整體結構




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. 請求與響應

這一節其實主要講的就是請求。
請求的內容會比較多,還涉及到網絡協議等知識,這里不再展開。
列舉一下重要的幾點:

  1. 路由美化 (將原始的路由修改成比較美觀的地址,見導圖 請求部分)
  2. url 解析(將美化過的路由解析成原始請求)
  3. 請求管理 (包括請求頭部,請求體,解析器等等)

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 會耽誤你很長時間。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM