首頁 後端開發 php教程 Yii中的屬性(Property)詳解

Yii中的屬性(Property)詳解

Dec 29, 2017 pm 07:04 PM
property 屬性 詳解

本文主要介紹了PHP的Yii框架中的屬性(Property),詳細說明了實現屬性的步驟,需要的朋友可以參考下。希望對大家有幫助。

在 PHP 中,類別的成員變數也稱為屬性(properties)。它們是類別定義的一部分,用來表現一個實例的狀態(也就是區分類的不同實例)。在具體實踐中,常常會想用一個稍微特殊些的方法來實現屬性的讀寫。例如,如果有需求每次都要對 label 屬性執行 trim 操作,就可以用以下程式碼實作:

$object->label = trim($label);
登入後複製

上述程式碼的缺點是只要修改 label 屬性就必須再次呼叫 trim() 函數。若將來需要用其它方式處理 label 屬性,例如首字母大寫,就必須修改所有給 label 屬性賦值的程式碼。這種程式碼的重複會導致 bug,這種實踐顯然需要盡可能避免。

為解決這個問題,Yii 引入了一個名為 yii\base\Object 的基類,它支援基於類別內的 getter 和 setter(讀取器和設定器)方法來定義屬性。如果某一類別需要支援這個特性,只需要繼承 yii\base\Object 或其子類別。

補充:幾乎每個 Yii 框架的核心類別都繼承自 yii\base\Object 或其子類別。這意味著只要在核心類別中見到 getter 或 setter 方法,就可以像呼叫屬性一樣呼叫它。
getter 方法是名稱以 get 開頭的方法,而 setter 方法名稱以 set 開頭。方法名稱中 get 或 set 後面的部分就定義了該屬性的名字。如下面程式碼所示,getter 方法getLabel() 和setter 方法setLabel() 操作的是label 屬性,:

namespace app\components;

use yii\base\Object;

class Foo extend Object
{
  private $_label;

  public function getLabel()
  {
    return $this->_label;
  }

  public function setLabel($value)
  {
    $this->_label = trim($value);
  }
}
登入後複製

(詳細解釋:getter 和setter 方法建立了一個名為label 的屬性,在這個在範例裡,它指向一個私有的內部屬性_label。兩者主要的差異是:當這種屬性被讀取時,對應的 getter 方法將被呼叫;而當屬性被賦值時,對應的 setter 方法就會被呼叫。如:

// 等效于 $label = $object->getLabel();
$label = $object->label;

// 等效于 $object->setLabel('abc');
$object->label = 'abc';
登入後複製

只定義了 getter 沒有 setter 的屬性是唯讀屬性。嘗試賦值給這樣的屬性將導致 yii\base\InvalidCallException (無效呼叫)異常。類似的,只有 setter 方法而沒有 getter 方法定義的屬性是只寫屬性,嘗試讀取這種屬性也會觸發異常。使用只寫屬性的情況幾乎沒有。

透過 getter 和 setter 定義的屬性也有一些特殊規則和限制:

這類屬性的名字是不區分大小寫的。如,$object->label 和 $object->Label 是同一個屬性。因為 PHP 方法名稱是不區分大小寫的。

如果此類屬性名和類別成員變數相同,則以後者為準。例如,假設以上 Foo 類別有個 label 成員變量,然後給 $object->label = 'abc' 賦值,將賦給成員變數而不是 setter setLabel() 方法。

這類屬性不支援可見性(存取限制)。定義屬性的 getter 和 setter 方法是 public、protected 還是 private 對屬性的可見性沒有任何影響。
這類屬性的 getter 和 setter 方法只能定義為非靜態的,若定義為靜態方法(static)則不會以相同方式處理。
回到開頭提到的問題,與其處處要呼叫 trim() 函數,現在我們只需在 setter setLabel() 方法內呼叫一次。如果 label 首字母變成大寫的新要求來了,我們只需要修改setLabel() 方法,而無須接觸任何其它程式碼。

實作屬性的步驟

我們知道,在讀取和寫入物件的一個不存在的成員變數時, __get() __set() 會被自動調用。 Yii正是利用這一點,提供對屬性的支援的。從上面的程式碼中,可以看出,如果存取一個物件的某個屬性, Yii會呼叫名為 get屬性名() 的函數。如, SomeObject->Foo , 會自動呼叫 SomeObject->getFoo() 。如果修改某一屬性,會呼叫對應的setter函數。 如, SomeObject->Foo = $someValue ,會自動呼叫 SomeObject->setFoo($someValue) 。

因此,要實作屬性,通常有三個步驟:

    繼承自 yii\base\Object 。
  • 宣告一個用來保存該屬性的私有成員變數。
  • 提供getter或setter函數,或兩者都提供,用於存取、修改上述的私有成員變數。 如果只提供了getter,那麼屬性為唯讀屬性,只提供了setter,則為唯寫。
  • 如下的Post類,實現了可讀可寫的屬性title:
class Post extends yii\base\Object  // 第一步:继承自 yii\base\Object
{
  private $_title;         // 第二步:声明一个私有成员变量

  public function getTitle()    // 第三步:提供getter和setter
  {
    return $this->_title;
  }

  public function setTitle($value)
  {
    $this->_title = trim($value);
  }
}
登入後複製

從理論上來講,將private $_title 寫成public $title ,也是可以實現對$post->title 的讀寫的。但這不是好的習慣,理由如下:

失去了類別的封裝性。 一般而言,成員變數對外不可見是比較好的程式設計習慣。 從這裡你也許沒看出來,但是假如有一天,你不想讓用戶修改標題了,你怎麼改? 怎麼確保程式碼中沒有直接修改標題? 如果提供了setter,只要把setter刪掉,那麼一旦有沒清理乾淨的對標題的寫入,就會拋出異常。 而使用 public $title 的方法的話,你改成 private $title 可以排查寫入的異常,但是讀取的也被禁止了。
對於標題的寫入,你會想要掉空格。 使用setter的方法,只需要像上面的程式碼段一樣在這個地方呼叫 trim() 就可以了。 但如果使用 public $title 的方法,那麼毫無疑問,每個寫入語句都要呼叫 trim() 。 你能保證沒有一處遺漏?
因此,使用 public $title 只是一時之快,看起來簡單,但今後的修改是個麻煩事。 簡直可以說是惡夢。這就是軟體工程的意義所在,透過一定的方法,讓程式碼易於維護、方便修改。 一時看著好像沒必要,但實際上吃過虧的朋友或被客戶老闆逼著修改上一個程式設計師寫的程式碼,問候過他親人的, 都會覺得這是十分必要的。

但是,世事無絕對。由於 __get() 和 __set() 是在遍歷所有成員變量,找不到匹配的成員變數時才被呼叫。 因此,其效率天生地低於使用成員變數的形式。在一些表示資料結構、資料集合等簡單情況下,且不需讀寫控制等, 可以考慮使用成員變數作為屬性,這樣可以提高一點效率。

另一個提高效率的小技巧就是:使用$pro = $object->getPro() 來取代$pro = $object->pro , 用$objcect->setPro($value)來代替$object->pro = $value 。 這在功能上是完全一樣的效果,但避免了使用 __get() 和 __set() ,相當於繞過了遍歷的過程。

這裡估計有人該罵我了,Yii好不容易實現了屬性的機制,就是為了方便開發者, 結果我卻在這裡教大家怎麼使用原始的方式,去提高所謂的效率。 嗯,確實,開發的便利性與執行高效率有一定的矛盾。我個人的觀點比較傾向以便利為先, 用好、用足Yii為我們創造的便利條件。至於效率的事情,更多的是框架自身需要注意的, 我們只要別寫出格外2的程式碼就OK了。

不過你完全可以放心,在Yii的框架中,極少出現 $app->request 之類的程式碼,而是使用 $app->getRequest() 。 換句話說,框架本身還是格外注重效率的,至於便利性,則留給了開發者。 總之,這裡只是點出來有這麼一個知識點,至於用不用,怎麼用,完全取決於你了。

值得注意的是:

由於自動呼叫 __get() __set() 的時機僅發生在存取不存在的成員變數時。 因此,如果定義了成員變數 public $title 那麼,就算定義了 getTitle() setTitle() , 他們也不會被呼叫。因為 $post->title 時,會直接指向該 pulic $title , __get() __set() 是不會被呼叫的。從根上就被切斷了。
由於PHP對於類別方法不區分大小寫,即大小寫不敏感, $post->getTitle() 和 $post->gettitle() 是呼叫相同的函數。 因此, $post->title 和 $post->Title 是同一個屬性。即屬性名也是不區分大小寫的。
由於 __get() __set() 都是public的, 無論將 getTitle() setTitle() 宣告為 public, private, protected,都沒有意義,外部同樣都是可以存取。所以,所有的屬性都是public的。
由於 __get() __set() 都不是static的,因此,沒有辦法使用static 的屬性。
Object的其他與屬性相關的方法

除了__get() __set() 之外, yii\base\Object 也提供了以下方法以便使用屬性:

  • __isset() 用來測試屬性值是否不為null ,在isset($object->property) 時被自動呼叫。 注意該屬性要有對應的getter。

  • __unset() 用來將屬性值設為 null ,在 unset($object->property) 時被自動呼叫。 注意該屬性要有對應的setter。

  • hasProperty() 用來測試是否有某個屬性。即,定義了getter或setter。 如果 hasProperty() 的參數 $checkVars = true (預設為true), 那麼只要有同名的成員變數也認為具有該屬性,如前面提到的 public $title 。

  • canGetProperty() 測試一個屬性是否可讀,參數 $checkVars 的意義同上。只要定義了getter,屬性即可讀取。 同時,如果 $checkVars 為 true 。那麼只要類別定義了成員變量,不管是public, private 還是 protected, 都認為是可讀。

  • canSetProperty() 測試一個屬性是否可寫,參數 $checkVars 的意義同上。只要定義了setter,屬性即可寫入。 同時,在 $checkVars 為 ture 。那麼只要類別定義了成員變量,不管是public, private 還是 protected, 都認為是可寫。

  • Object和Component

yii\base\Component 繼承自 yii\base\Object ,因此,他也具有屬性等基本功能。

但是,由於Componet也引入了事件、行為,因此,它並非簡單地繼承了Object的屬性實作方式,而是基於相同的機制, 重載了 __get() __set() 等函數。但從實現機制上來講,是一樣的。這個不影響理解。

前面說過,官方將Yii定位在一個基於組件的框架。可見組件這一概念是Yii的基礎。 如果你有興趣閱讀Yii的原始碼或是API文檔,你將會發現, Yii幾乎所有的核心類別都派生於(繼承自) yii\base\Component 。

在Yii1.1時,就已經有了component了,那時是 CComponent。 Yii2將Yii1.1中的CComponent分割成兩個類別: yii\base\Object 和 yii\base\Component 。

其中,Object比較輕量級些,透過getter和setter定義了類別的屬性(property)。 Component派生自Object,並支援事件(event)和行為(behavior)。因此,Component類別有三個重要的特性:

  • #屬性(property)

  • 事件(event)

  • 行為(behavior)

相信你或多或少了解過,這三個特性是豐富和拓展類別功能、改變類別行為的重要切入點。 因此,Component在Yii中的地位極高。

在提供更多功能、更多便利的同時,Component由於增加了event和behavior這兩個特性, 在方便開發的同時,也犧牲了一定的效率。 如果開發中不需要使用event和behavior這兩個特性,例如表示一些資料的類別。 那麼,可以不從Component繼承,而從Object繼承。 典型的應用場景就是如果表示使用者輸入的一組數據,那麼,使用Object。 而如果需要對物件的行為和能響應處理的事件進行處理,毫無疑問地應採用Component。 從效率來講,Object更接近原生的PHP類,因此,在可能的情況下,應優先使用Object。

相關推薦:

Yii運作機制及路由詳解

Yii如何隱藏URL中index.php

PHP Yii框架之資料庫查詢操作總結

以上是Yii中的屬性(Property)詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Win11管理員權限取得詳解 Win11管理員權限取得詳解 Mar 08, 2024 pm 03:06 PM

Windows作業系統是全球最受歡迎的作業系統之一,其新版本Win11備受矚目。在Win11系統中,管理員權限的取得是一個重要的操作,管理員權限可以讓使用者對系統進行更多的操作和設定。本文將詳細介紹在Win11系統中如何取得管理員權限,以及如何有效地管理權限。在Win11系統中,管理員權限分為本機管理員和網域管理員兩種。本機管理員是指具有對本機電腦的完全管理權限

Oracle SQL中的除法運算詳解 Oracle SQL中的除法運算詳解 Mar 10, 2024 am 09:51 AM

OracleSQL中的除法運算詳解在OracleSQL中,除法運算是一種常見且重要的數學運算運算,用來計算兩個數相除的結果。除法在資料庫查詢中經常用到,因此了解OracleSQL中的除法運算及其用法是資料庫開發人員必備的技能之一。本文將詳細討論OracleSQL中除法運算的相關知識,並提供具體的程式碼範例供讀者參考。一、OracleSQL中的除法運算

PHP模運算子的作用及用法詳解 PHP模運算子的作用及用法詳解 Mar 19, 2024 pm 04:33 PM

PHP中的模運算子(%)是用來取得兩個數值相除的餘數的。在本文中,我們將詳細討論模運算子的作用及用法,並提供具體的程式碼範例來幫助讀者更好地理解。 1.模運算子的作用在數學中,當我們將一個整數除以另一個整數時,就會得到一個商和一個餘數。例如,當我們將10除以3時,商數為3,餘數為1。模運算子就是用來取得這個餘數的。 2.模運算子的用法在PHP中,使用%符號來表示模

linux系統呼叫system()函數詳解 linux系統呼叫system()函數詳解 Feb 22, 2024 pm 08:21 PM

Linux系統呼叫system()函數詳解系統呼叫是Linux作業系統中非常重要的一部分,它提供了一種與系統核心互動的方式。其中,system()函數是常用的系統呼叫函數之一。本文將詳細介紹system()函數的使用方法,並提供對應的程式碼範例。系統呼叫的基本概念系統呼叫是使用者程式與作業系統核心互動的一種方式。使用者程式透過呼叫系統呼叫函數來請求作業系統

Linux的curl指令詳解 Linux的curl指令詳解 Feb 21, 2024 pm 10:33 PM

Linux的curl命令詳解摘要:curl是一種強大的命令列工具,用於與伺服器進行資料通訊。本文將介紹curl指令的基本用法,並提供實際的程式碼範例,幫助讀者更好地理解和應用該指令。一、curl是什麼? curl是命令列工具,用於發送和接收各種網路請求。它支援多種協議,如HTTP、FTP、TELNET等,並提供了豐富的功能,如檔案上傳、檔案下載、資料傳輸、代

詳細解析C語言學習路線 詳細解析C語言學習路線 Feb 18, 2024 am 10:38 AM

C語言作為一門廣泛應用在軟體開發領域的程式語言,是許多程式設計師初學者的首選。學習C語言不僅可以幫助我們建立程式設計的基礎知識,還可以提升我們解決問題和思考的能力。本文將詳細介紹一條C語言學習的路線圖,幫助初學者更好地規劃自己的學習進度。 1.學習基本文法在開始學習C語言之前,我們首先需要先了解C語言的基本文法規則。這包括變數和資料類型、運算子、控制語句(如if語句、

深入了解Promise.resolve() 深入了解Promise.resolve() Feb 18, 2024 pm 07:13 PM

Promise.resolve()詳解,需要具體程式碼範例Promise是JavaScript中一種用來處理非同步操作的機制。在實際開發中,常常需要處理一些需要依序執行的非同步任務,而Promise.resolve()方法就是用來傳回一個已經Fulfilled狀態的Promise物件。 Promise.resolve()是Promise類別的靜態方法,它接受一個

CSS中bottom屬性語法 CSS中bottom屬性語法 Feb 21, 2024 pm 03:30 PM

CSS中bottom屬性語法及程式碼範例在CSS中,bottom屬性用於指定一個元素與容器底部之間的距離。它可以控制一個元素相對於其父元素底部的位置。 bottom屬性的語法如下:element{bottom:value;}其中,element表示要套用該樣式的元素,value表示要設定的bottom值。 value可以是一個具體的長度值,例如像素

See all articles