Yii2中的情境(scenario)和驗證規則(rule)的詳解

來源:互聯網
上載者:User
Yii2的rule用於對模型屬性進行驗證,scenario使用者定義不同情境下需要驗證的模型,下面這篇文章主要給大家介紹了關於Yii2中情境(scenario)和驗證規則(rule)的相關資料,文中通過範例程式碼介紹的非常詳細,需要的朋友可以參考下。

前言

情境,顧名思義,就是一個情景,一種場面。在yii2中也有情境,這個情境跟你所理解的情境含義差不多。

和使用者有互動的系統必不可少的功能包括收集使用者資料、校正和處理。實際業務中,往往還需要將資料進行持久化儲存。出於安全考慮,開發人員應當牢牢把握“用戶端的輸入都是不可信”的準則,用戶端傳過來的資料先進行過濾和清洗後再儲存或傳遞到內部系統。

Yii2推薦使用Model類來收集和校正使用者資料,持久化的ActiveRecord類是其子類。Model類的load和validate兩個方法,分別用來收集和校正用戶端資料。哪些資料應該被收集,哪些資料需要在什麼情境下驗證,便是本文的主題:情境(scenario)和驗證規則(rule)。

下面話不多說了,來隨著小編一起看看詳細的介紹吧。

系統結構

先引入一個簡單的業務系統:系統中存在學生和教師兩種角色,資料庫中使用了三張表儲存角色資訊:

user: [id, username, password, status, 其他通用屬性]

student: [id, user_id, student_no, grade, class, 其他學生屬性]

teacher: [id, user_id, work_no, title, telphone, 其他教師屬性]

實際業務不限於對這三張表的增刪查改操作。為了簡化問題,後續僅討論user和student兩張表的資料變更(給出teacher表是為了讓讀者不認為設計資料庫的人是腦殘:明明可以放到一張表的,為什麼要拆開!)。

學生報名

學生報名是典型的增刪查改操作,送分題。學生報名的簡要程式碼範例如下:

public function actionSignup(){ $data = Yii::$app->request->post(); $user = new User(); $user->load($data); if ($user->save()) {  $student = new Student([   "user_id" => $user->id,  ]);  $student->load($data);  if ($student->save()) {   // redirect to success page  } else {   $user->delete();  } } // render error page}

相信有Yii2使用經驗的人都能根據資料庫的欄位約束快速的把User和Student類的rules方法寫出來。例如User類檔案內容可能如下:

namespace app\models;class User extends \yii\db\ActiveRecord{ public function rules() {  return [   [["username", "password", "status",], "required"],   ["username", "unique"],   // other rules  ]; } // other method}

定義資料的驗證規則,這是大多數人對rules的第一印象,並且是一個很好的印象:它打回非法的資料,讓正常的資料進入系統中。安全的實踐應該盡量定義完整的規則,充分驗證資料。也建議每一個Yii2開發人員對內建的核心校正器熟悉。

修改資訊

修改資訊,也是典型的增刪查改操作。實現代碼和報名差別不大,這裡僅討論兩點:

1、使用者密碼的驗證

註冊時會校正使用者密碼是否8-16位,密碼的規則可能是: ["password", "string", "length" => [8, 16]] 。明文儲存密碼是不可取的,插入資料庫時至少會做MD5加密,password變成32位。假設使用者修改資訊時未修改密碼,再次儲存時密碼規則校正出錯(長度不符合),無法儲存!

怎麼解決這個問題呢?翻閱Yii文檔,發現了規則中的when屬性可以救場。一種可能的驗證規則是:

public function rules(){ return [   ["password", "string", "length" => [8, 16], 'when' => function ($model) {    return $model->isNewRecord;   }],   // other rules  ];

只有在註冊(新增資料)時才校正密碼欄位。問題解決,完美!

2、防止使用者私自改密碼

假設有個小聰明的傢伙(例如湯姆),發現系統是用Yii架構做的,想搞點小破壞炫耀一下水平。在發送修改資訊的表單時,湯姆增加&password=12345678這一段資料。系統使用$user->load($data)收集使用者輸入,更新password欄位,帶來如下後果:rules設定更新時不校正密碼欄位,12345678直接作為password的值儲存到資料庫中。這個操作帶來連鎖反應:使用者再次登入時,加密過後的密碼與資料庫中的純文字密碼不匹配,導致湯姆無法登入系統。煩人的是湯姆是個刺頭,登入不上後整天騷擾客服,不省心!

怎麼樣防止這種情況出現呢?一種解決的方法是阻止修改密碼:

unset($data["password"]); $user->load($data);// 或者$password = $user->password;$user->load($data);$user->password = $password;

把使用者輸入的密碼過濾掉,私自修改密碼的問題就解決了。

但是問題還沒有結束:湯姆可以轉向修改其他欄位,比如說性別,身份證等。更嚴重情況是修改student中user_id,就可以更改任意學生的資訊。事情十分嚴重,需要馬上修複漏洞。

可以按照密碼的方法,逐個屏蔽受保護屬性,但顯得囉嗦難看(雖然好使)。如果受保護屬性多,可以僅允許白名單進入,具體操作為:新增一個UpdateInfoForm類繼承Model,屬性是白名單屬性合計。用UpdateInfoForm類過濾使用者資料,校正通過後再更新到user和student中:

$form = new UpdateInfoForm();$form->load($data);if ($form->validate()) { $user->load($form->attributes); $student->load($form->attributes); // next biz}

這種方式更優雅,但仔細一想代價不小:屬性和驗證規則要重複寫一遍;user和student儲存時又重複校正屬性。這種方式看起來優雅,實際上卻冗餘又低效。

scenario的登場,完美的解決解決上述問題。

情境(scenario)

分析上面問題,會發現關鍵點是批量賦值(massive assignment)和資料校正(validate)兩個方法。如果對不同的情境指定賦值欄位和檢驗規則,問題就迎刃而解。

Yii中的scenario有 安全屬性 和 活躍屬性 兩個概念。安全屬性用在批量賦值的load方法,只有安全屬性才能被賦值;活躍屬性用在規則校正的validate方法,在活躍屬性集中並且定義了校正規則的屬性才會被校正。活躍屬性和安全屬性的關係是,安全屬性是活躍屬性的子集。

\yii\base\Model類定義了預設情境:SCENARIO_DEFAULT(值為default)。預設情境下,出現在rules方法中的屬性既是活躍屬性,又是安全屬性(這句話基本正確,看後續解釋)。為不同情境指定活躍屬性、安全屬性以及校正器,可以通過覆蓋senarios或rules兩個方法實現(幾乎每個Model類都會重寫rules方法,senarios用得少)。

rules

先看rules方法。預設的屬性加校正器定義方式,讓每個屬性既是安全屬性,也是活躍屬性。如果想讓某個屬性不是安全屬性(不能通過load批量賦值),在屬性名稱前加驚嘆號!即可。例如student中的user_id欄位:

public function rules(){ return [  ["!user_od", "required"],  ["!user_id", "integer"],  ["!user_od", "unique"],  // other rules ];}

user_id是活躍屬性,在寫入資料庫時會被校正。但它不是安全屬性,不能通過load方法進行賦值,解決了安全隱患。

再看rules方法按情境區分校正器規則的做法:定義校正器時on屬性指定規則在哪些情境下生效,except屬性則排除一些情境(如果不指定on和except,規則對所有情境生效)。例如:

public function rules(){ return [  ["password", "string", "length" => [8, 16], "on" => ["signup"]], // 僅在signup情境時才被驗證  ["status", "integer", "except" => ["signup"], // 除了signup情境,其他情況都校正  // other rules ];}

在原來基礎上新增驚嘆號和on/except屬性,非常簡便的就定義了非安全屬性以及分情境指定校正規則。

scenarios

另外一種更清晰定義安全屬性和活躍屬性的做法是重寫scenarios方法。scenarios方法返回一個數組,數組的鍵是情境名稱,值是活躍屬性集合(包飯安全屬性)。例如student表的可能實現如下:

public function scenarios(){ return [  self::SCENARIO_DEFAULT => ["!user_id", "grade", "class", xxxx],  "update" => ["grade", "class", xxxx], ];}

預設情形下(學生報名),年級、班級這些資訊是安全屬性,但user_id不是,只能在程式內部賦值,並在插入資料時被校正;在修改資訊時,user_id不是活躍屬性,既不能被批量賦值,也不需要校正(事實上它不應該改變)。

scenarios方法只能定義活躍屬性和安全屬性,無法定義校正規則,需要和rules配合使用。

總結

金肯定義完善的資料校正規則

業務複雜時定義多個情境,仔細為每個情境定義安全屬性和校正規則

優先使用rules;屬性較多、rules複雜時,可以配合scenarios方法迅速理清安全屬性和活躍屬性

參考

http://www.yiiframework.com/doc-2.0/guide-input-validation.html

您可能感興趣的文章:

MixPHP、Yii和CodeIgniter的並發壓力測試的小結

PHP基於非遞迴演算法實現先序、中序及後序遍曆二叉樹操作的樣本

PHP使用兩個棧實現隊列功能的方法的講解

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.