Yii框架特點及測試考慮

技術小美發表於2017-11-23

1. 簡介

Yii 是一個基於部件、用於開發大型 Web 應用的高效能 PHP 框架。它將 Web 程式設計中的可重用性發揮到極致,能夠顯著加速開發程式。Yii(讀作“易”)代表簡單(easy)、高效(efficient)、可擴充套件 (extensible)。它是輕量級的,又裝配了很好很強大的快取部件,因此尤其適合開發大流量的應用,比如門戶、論壇、內容管理系統(CMS)、電子商務系統,等等。


Yii 是一個純OOP的通用Web程式設計框架,和大多數 PHP 框架一樣,Yii 是一個 MVC 框架。


Yii在設計時借鑑和整合了很多其他著名web程式設計框架和應用的思想和工作:


● Prado:這是Yii的思想的主要來源。Yii採用了它的基於部件和事件驅動的程式設計正規化,資料庫抽象層,模組應用框架,國際化和本地化以及其它一些東西。

● Ruby on Rails:Yii繼承了它的易於配置的特點。Yii還參考了它的活動記錄設計模式的實現。

● jQuery:作為JavaScript框架的基礎整合到Yii中

● Symfony:Yii參考了它的過濾器設計以及外掛框架

● Joomla:Yii參考了它的模組設計以及訊息傳遞體系。

2. YII的設計模式


下面將以一個樹圖描述了一個簡單的web服務所產生的目錄結構。通過這個目錄結構,我們會發現除了Web常見的資源、圖片、CSS、主題等元素外,還有Yii所特有的部件、控制器、模組、訊息、檢視等元素。下面我們將介紹Yii特有的元素的功能及實現。

testdrive/

index.php Web 應用入口指令碼檔案

assets/ 包含公開的資原始檔

css/ 包含 CSS 檔案

images/ 包含圖片檔案

themes/ 包含應用主題

protected/ 包含受保護的應用檔案

yiic yiic 命令列指令碼

yiic.bat Windows 下的 yiic 命令列指令碼

commands/ 包含自定義的 `yiic` 命令

shell/ 包含自定義的 `yiic shell` 命令

components/ 包含可重用的使用者部件

MainMenu.php `MainMenu` 掛件類

Identity/php 用來認證的 `Identity` 類

views/ 包含掛件的檢視檔案

mainMenu.php `MainMenu` 掛件的檢視檔案

config/ 包含配置檔案

console.php 控制檯應用配置

main.php Web 應用配置

controllers/ 包含控制器的類檔案

SiteController.php 預設控制器的類檔案

extensions/ 包含第三方擴充套件

messages/ 包含翻譯過的訊息

models/ 包含模型的類檔案

LoginForm.php `login` 動作的表單模型

ContactForm.php `contact` 動作的表單模型

runtime/ 包含臨時生成的檔案

views/ 包含控制器的檢視和佈局檔案

layouts/ 包含佈局檢視檔案

main.php 所有檢視的預設佈局

site/ 包含 `site` 控制器的檢視檔案

contact.php `contact` 動作的檢視

index.php `index` 動作的檢視

login.php `login` 動作的檢視

system/ 包含系統檢視檔案


2.1. YII的MVC模式


Yii 實現了 Web 程式設計中廣為採用的“模型-檢視-控制器”(MVC)設計模式。MVC 致力於分離業務邏輯和使用者介面,這樣開發者可以很容易地修改某個部分而不影響其它。在 MVC 中,模型表現資訊(資料)和業務規則;檢視包含使用者介面中用到的元素,比如文字、表單輸入框;控制器管理模型和檢視間的互動。


除了 MVC,Yii 還引入了一個叫做 application 的前端控制器,它表現整個請求過程的執行環境。Application 接收使用者的請求並把它分發到合適的控制器作進一步處理。


下圖描述了一個 Yii 應用的靜態結構:


圖 1 Yii 應用的靜態結構


下圖描述了一個 Yii 應用處理使用者請求時的典型流程:


圖 2 Yii 應用的典型流程


1. 使用者訪問 http://www.example.com/index.php?r=post/show&id=1,Web 伺服器執行入口指令碼 index.php 來處理該請求。

2. 入口指令碼建立一個應用例項並執行之。

3. 應用從一個叫 request 的應用部件獲得詳細的使用者請求資訊。

4. 通過 urlManager 這個應用部件,確定使用者要請求的控制器和動作。

5. 應用建立一個被請求的控制器例項來進一步處理使用者請求,控制器確定由它的actionShow 方法來處理 show 動作。然後它建立並應用和該動作相關的過濾器,如果過濾器允許的話,動作被執行。

6. 動作從資料庫讀取一個 ID 為 1 的 Post 模型。

7. 動作使用 Post 模型來渲染一個叫 show 的檢視。

8. 檢視讀取 Post 模型的屬性並顯示之。

9. 檢視執行一些掛件。

10. 檢視的渲染結果嵌在佈局中。

11. 動作結束檢視渲染並顯示結果給使用者。

2.1.1. 除錯模式


一個 Yii 應用能夠根據 YII_DEBUG 常量的指示以除錯模式或者生產模式執行。預設情況下該常量定義為 false,代表生產模式。要以除錯模式執行,在包含 yii.php 檔案前將此常量定義為 true。應用以除錯模式執行時效率較低,因為它會生成許多內部日誌。從另一個角度來看,發生錯誤時除錯模式會產生更多的除錯資訊,因而在開發階段非常有用。但在正式上線之後,則需要去掉除錯模式,因為除錯模式會列印很多額外資訊,這些資訊會在一定程度上影響其執行的效能。

2.2. 應用


應用表示一個請求處理的上下文。它的主要任務是分解使用者的請求並且把它分發到恰當的控制器中進行進一步的處理。它也儲存應用級的配置,因此也被稱為前端控制器。


預設情況下,應用是 CWebApplication 類的一個例項。 要對其進行定製, 通常是在應用例項被建立的時候提供一個配置檔案 (或陣列) 來初始化其屬性值。 另一個定製應用的方法就是擴充套件 CWebApplication 類。配置是一個鍵值對的陣列。 每個鍵名都對應應用例項的一個屬性, 相應的值為屬性的初始值。 如果應用的配置非常複雜,也可以將其分解成多個檔案,每個檔案返回配置陣列的一部分。


應用根目錄表示包含所有安全敏感的PHP指令碼和資料的根目錄。預設情況下,它是包含入口指令碼的目錄的名為protected的子目錄。應當組織Web使用者訪問這個目錄。


可以通過靈活的部件框架方便的進行應用功能的定製和擴充套件。應用管理著部件的集合,每個部件實現特定的功能。例如,應用可能在CUrlManager 和 CHttpRequest的幫助下處理一個使用者的請求。


Yii預定義了一個核心部件集合來提供Web應用中的常用功能,下面是CWebApplication類中預定義 核心部件:


assetManager:CAssetManager – 管理私有資原始檔的釋出

authManager:CAuthManager – 管理基於角色的訪問控制(RBAC)

cache:CCache – 提供資料快取功能

clientScript:CClientScript – 管理客戶端指令碼(javascript和CSS)

coreMessages:CPhpMessageSource – 提供Yii框架所使用的轉換過的核心訊息

db:CDbConnection – 提供資料庫連線

errorHandler:CErrorHandler – 處理未被捕獲的PHP錯誤和異常

messages:CPhpMessageSource – 提供Yii應用使用的轉換過的訊息

request:CHttpRequest – 提供和使用者請求相關的資訊

securityManager:CSecurityManager – 提供安全相關的服務,如雜湊,加密

session:CHttpSession – 提供會話相關的功能

statePersister:CStatePersister – 提供全域性持續狀態方法

urlManager:CUrlManager – 提供URL解析和建立功能

user:CWebUser – 表示當前使用者的身份資訊

themeManager:CThemeManager – 管理主題


處理一個使用者請求時,一個應用將會經歷這樣一個生命週期:


1. 設定自動載入和錯誤處理類

2. 註冊核心應用部件

3. 載入應用配置

4. 使用CApplication::init()初始化應用

載入靜態應用部件

5. 觸發onBeginRequest事件

6. 處理使用者請求:

解析使用者請求

建立控制器

執行控制器

7. 觸發onEndRequest事件

2.3. 控制器


控制器是 CController 或者其子類的例項。 使用者請求應用時,建立控制器。 控制器執行請求action,action通常引入必要的模型並提供恰當的檢視。 最簡單的action僅僅是一個控制器類方法,此方法的名字以action開始。


控制器有預設的action。使用者請求沒指定哪一個action執行時,將執行預設的action。 預設情況下,預設的action名為index。可以通過設定CController::defaultAction改變預設的action。


下邊是最小的控制器。因為控制器未定義任何action,請求時會丟擲異常。

class SiteController extends CController

{

}



CWebApplication在處理一個新請求時,例項化一個控制器。程式通過控制器的ID,並按一定規則確定控制器類及控制器類所在位置。


一個動作可以定義為一個函式或者一個動作類。當請求這個動作時,控制器例項化這個類。這樣就可以複用和重用動作。


過濾器是配置為控制器動作之前或之後執行的一段程式碼。例如可以設計一個效能過濾器用於計時動作執行的時間。一個動作可以有多個過濾器。過濾器可以定義為控制器類的方法,也可以是CFilter類的子類。

2.4. 模型


模型是 CModel 或其子類的例項。 模型用於保持資料以及和資料相關的業務規則。模型描述了一個單獨的資料物件。它可以是資料表中的一行資料或者使用者輸入的一個表單。資料中的各個欄位都描述了模型的一個屬性。這些屬性都有一個標籤,都可以被一套可靠的規則驗證。


Yii 從表單模型和 active record 實現了兩種模型。 它們都繼承自基類 CModel。


表單模型是CFormModel的例項。表單模型用於儲存通過收集使用者輸入得來的資料。這樣的資料通常被收集,使用,然後被拋棄。例如,在一個登入頁面上,我們可以使用一個表單模型來描述諸如使用者名稱,密碼這樣的由終端使用者提供的資訊。


Active Record (AR) 是一種物件導向風格的,用於抽象資料庫訪問的設計模式。任何一個 AR 物件都是 CActiveRecord 或其子類的例項, 它描述的資料表中的單獨一行資料。這行資料中的欄位被描述成 AR 物件的一個屬性。


2.5. 檢視



檢視是一個包含了主要的使用者互動元素的PHP指令碼。他可以包含PHP語句,但是建議這些語句不要去改變資料模型,且最好能夠保持其單純性(單純作為檢視)!為了實現邏輯和介面分離,大部分的邏輯應該被放置於控制器或模型裡,而不是檢視裡。


檢視有一個當其被渲染(render)時用於校驗的名稱。檢視的名稱與其指令碼名稱是一樣的。例如:檢視 edit 的名稱出自一個名為 edit.php 的指令碼檔案。通過 CController::render() 呼叫檢視的名稱可以渲染一個檢視。這個方法將在 protected/views/ControllerID 目錄下尋找對應的檢視檔案。


在檢視指令碼內部,可以通過 $this 來訪問控制器例項。可以在檢視裡以 $this->propertyName 的方式 pull 控制器的任何屬性。也可以用以下 push 的方式傳遞資料到檢視裡:


$this->render(`edit`, array(

`var1`=>$value1,

`var2`=>$value2,

));



在以上的方式中, render() 方法將提取陣列的兩個引數到變數裡。其產生的結果是,在檢視指令碼里,我們可以直接訪問變數 $var1 和 $var2。


佈局是一種特殊的檢視檔案用來修飾檢視。它通常包含了使用者互動過程中常用到的一部分檢視。


元件是 CWidget 或其子類的例項。它是一個主要用於描述特定意圖的組成部分。元件通常內嵌於一個檢視來產生一些複雜卻獨立的使用者介面。例如,一個日曆元件可以用於渲染一個複雜的日曆介面。元件可以在使用者介面上更好的實現重用。


系統檢視的渲染通常用於展示 Yii 的錯誤和日誌資訊。例如,當使用者請求來一個不存在的控制器或動作時,Yii 會丟擲一個異常來解釋這個錯誤。 這時,Yii 就會使用一個特殊的系統檢視來展示這個錯誤。

2.6. 部件


Yii 應用構建於物件是規範編寫的部件之上。部件是 CComponent 或其衍生類的例項。使用部件主要就是涉及訪問其屬性和掛起/處理它的事件。基類 CComponent 指定了如何定義屬性和事件。


部件的屬性就像物件的公開成員變數,可以被讀取或設定部件屬性。


部件事件是一種特殊的屬性,它可以將方法(稱之為事件控制程式碼(event handlers))作為它的值。繫結(分配)一個方法到一個事件將會導致方法在事件被掛起處自動被呼叫。因此部件行為可能會被一種在部件開發過程中不可預見的方式修改。部件事件以 on 開頭的命名方式定義。


部件可以繫結一個或者多個行為。一個行為(behavior) 就是一個物件,其方法可以被它繫結的部件通過收集功能的方式來實現 `繼承(inherited)`,而不是專有化繼承(即普通的類繼承)。簡單的來說,就是一個部件可以以`多重繼承`的方式實現多個行為的繫結。


2.7. 開發流程



用yii開發一個web程式的基本流程,如下所示:


1. 建立目錄結構

2. 配置 application,即修改application配置檔案

3. 每種型別的資料都建立一個 model 類來管理

4. 每種型別的使用者請求都建立一個 controller 類。 依據實際的需求對使用者請求進行分類。一般來說,如果一個model類需要使用者訪問,就應該對應一個controller類。

5. 實現 actions 和相應的 views。這是真正需要我們編寫的工作。

6. 在controller類裡配置需要的action filters 。

7. 如果需要主題功能,編寫 themes。

8. 如果需要 internationalization國際化功能,編寫翻譯語句。

9. 使用 caching 技術快取資料和頁面。

10. 最後調整好程式和釋出。

3. 測試考慮


Yii提供了自動化測試的方法,包括單元測試和功能測試,其中單元測試是驗證一個單元的程式碼能夠像預想的一樣工作,對於Yii來說,就是驗證類中的每個方法都可以正確的工作,輸入不同的引數和資料,來驗證類中每個方法能夠輸出符合預期的資料和行為。


功能測試是確定一個功能、特徵(例如部落格系統中的發表管理)可以像預想的一樣工作。由於一個功能可能包含多個類,因此相比較單元測試,功能測試在更高的級別。


Yii的測試框架是建立在PHPUnit之上的。

3.1. 定義Fixtures


一套自動化的case往往會被執行多次。為了保證這個測試的過程是可重複的,我們需要在執行case的時候,讓其初始狀態是處於一個已知確定的狀態。這個狀態,我們叫做fixture,通俗的講,就是在測試之前所需要的環境的所有初始狀態。


建立一個資料庫的fixture在web應用開發過程中可能是一個最耗時的部分。Yii框架引入了一個叫做CDbFixtureManager的應用元件來緩和這個問題,大體來說,在執行一個測試集合時它做了這樣幾件事情:

在所有case執行前,它把與本次執行case相關的所有資料表置於一個已知的狀態。在一個單一的case執行前,它把一些特定的表復位到一個已知的狀態。


在執行一個方法的過程中,它提供了對與這些fixture有關的資料的訪問。


為了使用CDbFixtureManager這個類,一般會進行如下配置:

return array(

`components`=>array(

`fixture`=>array(

`class`=>`system.test.CDbFixtureManager`,

),

),

);


對於這些我們想提供作為fixture的資料,一般會將其放在/protected/tests/fixtures目錄下。這些資料檔案按照php的形式組織在一起,統稱為fixture file。每個fixture file會返回一個陣列,該陣列包含一些特定的表的某些行的預設值。並且檔名跟表名保持一致。

3.2. 單元測試


我們可以總結出如下在Yii中書寫單元測試的原則:


● 單元測試是通過實現繼承自CTestCase或CDbTestCase類的XyzTest類來實現,其中Xyz表示要測試的類名。CTestCase用於通用單元測試而CDbTestCase用於活動記錄模型類的測試。由於PHPUnit_Framework_TestCase是這兩個類的父類,因此我們可以使用繼承自此類的所有方法。

● 單元測試類以檔案XyzTest.php的形式儲存。為了方便起見,一般單元測試的case會儲存在protected/tests/unit下。

● 測試類主要是一個待測試的類的方法的集合,如果這個待測試的類叫做Abc,那麼這個測試類一般會起名為testAbc。

● 一個測試類經常包含一系列的斷言,(例如assertTrue, assertEquals),作為驗證目標類行為的檢驗點。


以如何為active record模型類建立單測為例,我們來看看究竟如何寫一個單測case。


假設我們想測試一個blog的評論模型類,首先要建立一個CommonTest.php並儲存在/protected/tests/unit/CommentTest.php,程式碼如下:

class CommentTest extends CDbTestCase

{

public $fixtures=array(

`posts`=>`Post`,

`comments`=>`Comment`,

);


……
 

}



在上述類中,我們制定了fixture的成員變數是一個陣列,該陣列將會使用者接下來的測試case中。


Fixture names允許我們在測試方法中以一種非常方便的方式來訪問fixture data。最典型的用法如下:

$comments = $this->comments;


接下來,我們就可以書寫textApprove方法來測試評論檢視類中的approve 方法了。

3.3. 功能測試


● 像單元測試一樣,功能測試時通過實現繼承自CWebTestCase類的XyzTest類來實現,其中Xyz表示要測試的類名。由於 PHPUnit_Extensions_SeleniumTestCase是CwebTestCase的父類,因此可以採用繼承自此類的所有方法。

● 功能測試類以檔案XyzTest.php的形式儲存。

● 測試類主要包含命名為testABC的測試方法的集合,其中Abc經常是要測試的功能的名字,例如testLogin

● 一個測試方法經常包含一系列的指令來發出命令以與被測試Web應用互動。它也包含一些斷言狀態來驗證Web應用如預想一樣響應。

3.4. 其他

3.4.1基於module的單元測試方法


有的yii版本不支援基於phpUnit 的單元的測試,那麼我們也可以有其他的辦法來做單元測試。具體的方法就是把test作為一個module。目錄部署結構為:


webRoot/protected/modules/test/controller/ABC.php


在上述ABC.php裡,我們可以建立繼承自業務的類,並在這些類中構建對應的action,每個action都可以呼叫需要測試的函式。通過在瀏覽器上直接請求這些action,並列印出相關資訊,即可實現對上述這些函式的測試。如想測試的action函式為actionAdd。


根據yii的url解析規則,上述Add方法的呼叫即為該url的請求http://*.*.*.*/index.php?r=test/ABC/Add 。 如果適當的加上返回結果的展現,那麼就能很明確的知道該函式的返回值與預期的是否相符。

3.4.2Firebug輔助測試


Firebug也是當今web開發程式設計師的一個利器,同時也可以作為測試人員的一個參考。Firebug會記錄各個請求的引數,post資料,響應資料。當功能出現問題時,測試人員可以根據事先設計的引數和預期的返回的資料來判定是view傳遞的引數錯誤,還是php根據邏輯所計算出的返回資料值不對。從而來定位錯誤的真正位置。


(全文完)

作者:mimical

本文轉自百度技術51CTO部落格,原文連結:http://blog.51cto.com/baidutech/743559,如需轉載請自行聯絡原作者


相關文章