<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                屬性用于表征類的狀態,從訪問的形式上看,屬性與成員變量沒有區別。 你能一眼看出$object->foo?中的?foo?是成員變量還是屬性么?顯然不行。 但是,成員變量是就類的結構構成而言的概念,而屬性是就類的功能邏輯而言的概念,兩者緊密聯系又 相互區別。比如,我們說People類有一個成員變量?int?$age?,表示年齡。那么這里年齡就是屬性 ,?$age?就是成員變量。 再舉個更學術化點的例子,與非門: ~~~ class NotAndGate extends Object{ private $_key1; private $_key2; public function setKey1($value){ $this->_key1 = $value; } public function setKey2($value){ $this->_key2 = $value; } public function getOutput(){ if (!$this->_key1 || !$this->_key2) return true; else if ($this->_key1 && $this->_key2) return false; } } ~~~ 與非門有兩個輸入,當兩個輸入都為真時,與非門的輸出為假,否則,輸出為真。上面的代碼中,與非 門類有兩個成員變量,?$_key1?和?$_key2?。但是有3個屬性,表示2個輸入的?key1?和?key2?,以及表示輸出的?output?。 成員變量和屬性的區別與聯系在于: * 成員變量是一個“內”概念,反映的是類的結構構成。屬性是一個“外”概念,反映的是類的邏輯意義。 * 成員變量沒有讀寫權限控制,而屬性可以指定為只讀或只寫,或可讀可寫。 * 成員變量不對讀出作任何后處理,不對寫入作任何預處理,而屬性則可以。 * public成員變量可以視為一個可讀可寫、沒有任何預處理或后處理的屬性。 而private成員變量由于外部不可見,與屬性“外”的特性不相符,所以不能視為屬性。 * 雖然大多數情況下,屬性會由某個或某些成員變量來表示,但屬性與成員變量沒有必然的對應關系, 比如與非門的?output?屬性,就沒有一個所謂的?$output?成員變量與之對應。 在Yii中,由?yii\base\Object?提供了對屬性的支持,因此,如果要使你的類支持屬性, 必須繼承自yii\base\Object?。Yii中屬性是通過PHP的魔法函數?__get()?__set()?來產生作用的。 下面的代碼是yii\base\Object?類對于?__get()?和?__set()?的定義: ~~~ public function __get($name) // 這里$name是屬性名 { $getter = 'get' . $name; // getter函數的函數名 if (method_exists($this, $getter)) { return $this->$getter(); // 調用了getter函數 } elseif (method_exists($this, 'set' . $name)) { throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); } } // $name是屬性名,$value是擬寫入的屬性值 public function __set($name, $value) { $setter = 'set' . $name; // setter函數的函數名 if (method_exists($this, $setter)) { $this->$setter($value); // 調用setter函數 } elseif (method_exists($this, 'get' . $name)) { throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name); } } ~~~ ## 實現屬性的步驟[](http://www.digpage.com/property.html#id2 "Permalink to this headline") 我們知道,在讀取和寫入對象的一個不存在的成員變量時,?__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的其他與屬性相關的方法[](http://www.digpage.com/property.html#object "Permalink to this headline") 除了?__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[](http://www.digpage.com/property.html#objectcomponent "Permalink to this headline") 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。 ## Object的配置方法[](http://www.digpage.com/property.html#object-config "Permalink to this headline") Yii提供了一個統一的配置對象的方式。這一方式貫穿整個Yii。Application對象的配置就是這種配置方式的體現: ~~~ $config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../../common/config/main.php'), require(__DIR__ . '/../../common/config/main-local.php'), require(__DIR__ . '/../config/main.php'), require(__DIR__ . '/../config/main-local.php') ); $application = new yii\web\Application($config); ~~~ $config?看著復雜,但本質上就是一個各種配置項的數組。Yii中就是統一使用數組的方式對對象進行配置,而實現這一切的關鍵就在?yii\base\Object?定義的構造函數中: ~~~ public function __construct($config = []) { if (!empty($config)) { Yii::configure($this, $config); } $this->init(); } ~~~ 所有?yii\base\Object?的構建流程是: * 構建函數以?$config?數組為參數被自動調用。 * 構建函數調用?Yii::configure()?對對象進行配置。 * 在最后,構造函數調用對象的?init()?方法進行初始化。 數組配置對象的秘密在?Yii::configure()?中,但說破了其實也沒有什么神奇的: ~~~ public static function configure($object, $properties) { foreach ($properties as $name => $value) { $object->$name = $value; } return $object; } ~~~ 配置的過程就是遍歷?$config?配置數組,將數組的鍵作為屬性名,以對應的數組元素的值對對象的屬性賦值。因此,實現Yii這一統一的配置方式的要點有: * 繼承自?yii\base\Object?。 * 為對象屬性提供setter方法,以正確處理配置過程。 * 如果需要重載構造函數,請將?$config?作為該構造函數的最后一個參數,并將該參數傳遞給父構造函數。 * 重載的構造函數的最后,一定記得調用父構造函數。 * 如果重載了?yii\base\Object::init()?函數,注意一定要在重載函數的開頭調用父類的?init()。 只要實現了以上要點,就可以使得你編寫的類可以按照Yii約定俗成的方式進行配置。這在編寫代碼的過程中,帶來許多便利。 像你這么聰明的,肯定會提出來,如果配置數組的某個配置項,也是一個數組,這怎么辦? 如果某個對象的屬性,也是一個對象,而非一個簡單的數值或字符串時,又怎么辦? 這兩個問題,其實是同質的。如果一個對象的屬性,是另一個對象,就像Application里會引入諸多的Component一樣, 這是很常見的。如后面會看到的?$app->request?中的?request?屬性就是一個對象。 那么,在配置?$app?時,必然要配置到這個?reqeust?對象。 既然?request?也是一個對象,那么他的配置要是按照Yii的規矩來,也就是用一個數組來配置它。 因此,上面提到的這兩個問題,其實是同質的。 那么,怎么實現呢?秘密在于setter函數。由于?$app?在進行配置時,最終會調用?Yii::configure()函數。 該函數又不區分配置項是簡單的數值還是數組,就直接使用?$object->$name?=?$value?完成屬性的賦值。 那么,對于對象屬性,其配置值?$value?是一個數組,為了使其正確配置。 你需要在其setter函數上做出正確的處理方式。 Yii應用?yii\web\Application?就是依靠定義專門的setter函數,實現自動處理配置項的。 比如,我們在Yii的配置文件中,可以看到一個配置項?components?,一般情況下,他的內容是這樣的: ~~~ 'components' => [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - // this is required by cookie validation 'cookieValidationKey' => 'v7mBbyetv4ls7t8UIqQ2IBO60jY_wf_U', ], 'user' => [ 'identityClass' => 'common\models\User', 'enableAutoLogin' => true, ], 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning'], ], ], ], 'errorHandler' => [ 'errorAction' => 'site/error', ], ], ~~~ 這是一個典型嵌套配置數組。那么Yii是如何把他們配置好的呢? 聰明的你肯定想到了,Yii一定是定義了一個名為?setComponents?的setter函數。 當然,Yii并未將該函數放在?yii\web\Application?里,而是放在父類?yii\di\ServiceLocator?里面。 至于?ServiceLocator?是何方神圣,在后面?[_服務定位器(Service Locator)_](http://www.digpage.com/service_locator.html#service-locator)?部分會講到, 這里你只需要知道它是Application的父類,提供components屬性的setter方法就可以了: ~~~ public function setComponents($components) { foreach ($components as $id => $component) { $this->set($id, $component); } } ~~~ 這里有個成員函數,?$this->set()?,他是服務定位器用來注冊服務的方法。 我們暫時不講這個東西,留待?[_服務定位器(Service Locator)_](http://www.digpage.com/service_locator.html#service-locator)?部分再講。 現在只要知道這個函數把配置文件中的components?配置項搞定就可以了。 從?yii\base\Object::__construct()?來看,對于所有Object,包括Component的屬性,都經歷這么4個階段: 1. 預初始化階段。這是最開始的階段,就是在構造函數?__construct()?的開頭可以設置property的默認值。 2. 對象配置階段。也就是前面提到構造函數調用?Yii::configure($this,?$config)?階段。 這一階段可以覆蓋前一階段設置的property的默認值,并補充沒有默認值的參數,也就是必備參數。$config?通常由外部代碼傳入或者通過配置文件傳入。 3. 后初始化階段。也就是構造函數調用?init()?成員函數。 通過在?init()?寫入代碼,可以對配置階段設置的值進行檢查,并規范類的property。 4. 類方法調用階段。前面三個階段是不可分的,由類的構造函數一口氣調用的。 也就是說一個類一但實例化,那么就至少經歷了前三個階段。 此時,該對象的狀態是確定且可靠的,不存在不確定的property。 所有的屬性要么是默認值,要么是傳入的配置值,如果傳入的配置有誤或者沖突,那么也經過了檢查和規范。 也就是說,你就放心用吧。 如果覺得《深入理解Yii2.0》對您有所幫助,也請[幫助《深入理解Yii2.0》](http://www.digpage.com/donate.html#donate)。 謝謝!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看