<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 簡介 可以將自定義代碼“注入”到現有代碼中的特定執行點。附加自定義代碼到某個事件,當這個事件被觸發時,這些代碼就會自動執行。 例如,郵件程序對象成功發出消息時可觸發?`messageSend`?事件。如想追蹤成功發送的消息,可以附加相應追蹤代碼到?`messageSend`?事件。 Yii 引入了名為`yii\base\Component`的基類以支持事件。如果一個類需要觸發事件就應該繼承`yii\base\Component`或其子類。 # 事件處理器 * 對象名和方法名數組形式指定的對象方法,如?`[$object, $method]`? ~~~ use app\events\MessageEvent; ... const EVENT_HELLO = 'sayHello'; public function actionIndex() { $event = new MessageEvent; $event->message = 'new message from actionIndex method'; $this->on(self::EVENT_HELLO,[$event,'hello']); $this->trigger(self::EVENT_HELLO,$event); // 打印 new message from actionIndex method } ~~~ 其中`app\events\MessageEvent`類代碼很簡單,如下: ~~~ <?php namespace app\events; use yii\base\Event; use Yii; class MessageEvent extends Event { public $message; public function hello($event) { echo $event->message; } } ~~~ * 類名和方法名數組形式指定的**靜態類方法**,如?`[$class, $method]` ~~~ use app\events\MessageEvent; ... const EVENT_HELLO = 'sayHello'; public function actionIndex() { $event = new MessageEvent; $event->message = 'new message from actionIndex method'; $this->on(self::EVENT_HELLO,['app\events\MessageEvent','hello']); $this->trigger(self::EVENT_HELLO,$event); } ~~~ 其中`app\events\MessageEvent`類代碼很簡單,如下: ~~~ <?php namespace app\events; use yii\base\Event; use Yii; class MessageEvent extends Event { public $message; static public function hello($event) { echo $event->message; } } ~~~ * 匿名函數,如?`function ($event) { ... }` ~~~ const EVENT_HELLO = 'sayHello'; public function actionIndex() { $this->on(self::EVENT_HELLO,function ($event){ // 綁定一個Event事件 foreach ($event->data as $data){ echo strtoupper($data),PHP_EOL; } }, ['abc','dev']); $this->trigger(self::EVENT_HELLO); // 觸發事件 得到結果:ABC DEV } ~~~ # 綁定與解除 有了事件handler,還要告訴Yii,這個handler是負責處理哪種事件的。這個過程,就是事件的綁定, 把事件和事件handler這兩個螞蚱綁在一根繩上,當事件跳起來的時候,就會扯動事件handler啦。 `yii\base\Component::on()` 就是用來綁定的,很容易就猜到, `yii\base\Component::off()` 就是用來解除的。對于綁定,有以下形式: ~~~ $person = new Person; // 使用PHP全局函數作為handler來進行綁定 $person->on(Person::EVENT_GREET, 'person_say_hello'); // 使用對象$obj的成員函數say_hello來進行綁定 $person->on(Person::EVENT_GREET, [$obj, 'say_hello']); // 使用類Greet的靜態成員函數say_hello進行綁定 $person->on(Person::EVENT_GREET, ['app\helper\Greet', 'say_hello']); // 使用匿名函數 $person->on(Person::EVENT_GREET, function ($event) { echo 'Hello'; }); ~~~ `yii\base\Component` 維護了一個handler數組,用來保存綁定的handler: ~~~ // 這個就是handler數組 private _events = []; // 綁定過程就是將handler寫入_event[] public function on($name, $handler, $data = null, $append = true) { $this->ensureBehaviors(); if ($append || empty($this->_events[$name])) { $this->_events[$name][] = [$handler, $data]; } else { array_unshift($this->_events[$name], [$handler, $data]); } } ~~~ 在事件的綁定邏輯上,按照以下順序: * 參數$append是否為true。為true表示所要綁定的事件handler要放在$\_event\[\]數組的最后面。這也是默認的綁定方式。 * 參數$append是否為false。表示handler要放在數組的最前面。這個時候,要多進行一次判定。 * 如果所有綁定的事件還沒有已經綁定好的handler,也就是說,將要綁定的handler是第一個,那么無論$append是否是true,該handler必然是第一個元素,也是最后一個元素。 * 如果$append為false,且要綁定的事件已經有了handler,那么,就將新綁定的事件插入到數組的最前面。 handler在$event\[\]數組中的位置很重要,代表的是執行的先后順序。這個在[多個事件handler的順序](http://www.digpage.com/event.html#id7)中會講到。 在解除時,就是使用 unset() 函數,處理 `$_event[]` 數組的相應元素。 `yii\base\Component::off()` 如下所示: ~~~ public function off($name, $handler = null) { $this->ensureBehaviors(); if (empty($this->_events[$name])) { return false; } // $handler === null 時解除所有的handler if ($handler === null) { unset($this->_events[$name]); return true; } else { $removed = false; // 遍歷所有的 $handler foreach ($this->_events[$name] as $i => $event) { if ($event[0] === $handler) { unset($this->_events[$name][$i]); $removed = true; } } if ($removed) { $this->_events[$name] = array_values($this->_events[$name]); } return $removed; } } ~~~ 要留意以下幾點: 當 $handler 為 null 時,表示解除 $name 事件的所有handler。 在解除 $handler 時,將會解除所有的這個事件下的 $handler 。雖然一個handler多次綁定在同一事件上的情況不多見,但這并不是沒有,也不是沒有意義的事情。在特定的情況下,確實有一個handler多次綁定在同一事件上。因此在解除時,所有的 $handler 都會被解除。而且沒有辦法只解除其中的一兩個。 # 事件的級別 ## 類級別事件 **當對象觸發事件時,它首先調用實例級別的處理器,然后才會調用類級別處理器** 先講講類級別的事件。類級別事件用于響應所有類實例的事件。比如,工頭需要了解所有工人的下班時間, 那么,對于數百個工人,即數百個Worker實例,工頭難道要一個一個去綁定自己的handler么? 這也太低級了吧?其實,他只需要綁定一個handler到Worker類,這樣每個工人下班時,他都能知道了。 與實例級別的事件不同,類級別事件的綁定需要使用 yii\base\Event::on() ~~~ Event::on( Worker::className(), // 第一個參數表示事件發生的類 Worker::EVENT_OFF_DUTY, // 第二個參數表示是什么事件 function ($event) { // 對事件的處理 echo $event->sender . ' 下班了'; } ); ~~~ 這樣,每個工人下班時,會觸發自己的事件處理函數,比如去打卡。之后,會觸發類級別事件。 類級別事件的觸發仍然是在 yii\base\Component::trigger() 中,還記得該函數的最后一個語句么: ~~~ Event::trigger($this, $name, $event); // 觸發類一級的事件 ~~~ 這個語句就觸發了類級別的事件。類級別事件,總是在實例事件后觸發。既然觸發時機靠后,那么如果有一天你要早退又不想老板知道,你就可以向小煤窯老板那樣,通過 $event->handled = true ,來終止事件處理。 從 yii\base\Event::trigger() 的參數列表來看,比 yii\base\Component::trigger() 多了一個參數 $class 表示這是哪個類的事件。因此,在保存 $_event[] 數組上, yii\base\Event 也比 yii\base\Component 要多一個維度: ~~~ // Component中的$_event[] 數組 $_event[$eventName][] = [$handler, $data]; // Event中的$_event[] 數組 $_event[$eventName][$calssName][] = [$handler, $data]; ~~~ 那么,反過來的話,低級別的handler可以在高級別事件發生時發生作用么?這當然也是不行的。由于類級別事件不與任意的實例相關聯,所以,類級別事件觸發時,類的實例可能都還沒有呢,怎么可能進行處理呢? 類級別事件的觸發,應使用 yii\base\Event::trigger() 。這個函數不會觸發實例級別的事件。值得注意的是, $event->sender 在實例級別事件中, $event->sender 指向觸發事件的實例,而在類級別事件中, 指向的是類名。在 yii\base\Event::trigger() 代碼中,有: ~~~ if (is_object($class)) { // $class 是trigger()的第一個參數,表示類名 if ($event->sender === null) { $event->sender = $class; } $class = get_class($class); // 傳入的是一個實例,則以類名替換之 } else { $class = ltrim($class, '\\'); } ~~~ 這段代碼會對 $evnet->sender 進行設置,如果傳入的時候,已經指定了他的值,那么這個值會保留,否則,就會替換成類名。 對于類級別事件,有一個要格外注意的地方,就是他不光會觸發自身這個類的事件,這個類的所有祖先類的同一事件也會被觸發。但是,自身類事件與所有祖先類的事件,視為同一級別: ~~~ // 最外面的循環遍歷所有祖先類 do { if (!empty(self::$_events[$name][$class])) { foreach (self::$_events[$name][$class] as $handler) { $event->data = $handler[1]; call_user_func($handler[0], $event); // 所有的事件都是同一級別,可以隨時終止 if ($event->handled) { return; } } } } while (($class = get_parent_class($class)) !== false); ~~~ 上面的嵌套循環的深度,或者叫時間復雜度,受兩個方面影響,一是類繼承結構的深度,二是 `$_event[$name][$class][] `數組的元素個數,即已經綁定的handler的數量。從實踐經驗看,一般軟件工程繼承深度超過十層的就很少見,而事件綁定上,同一事件的綁定handler超過十幾個也比較少見。因此,上面的嵌套循環運算數量級大約在100~1000之間,這是可以接受的。 但是,從機制上來講,由于類級別事件會被類自身、類的實例、后代類、后代類實例所觸發,所以,對于越底層的類而言,其類事件的影響范圍就越大。因此,在使用類事件上要注意,盡可能往后代類安排,以控制好影響范圍,盡可能不在基礎類上安排類事件。 ## 全局事件 接下來再講講全局級別事件。上面提到過,所謂的全局事件,本質上只是一個實例事件罷了。他只是利用了Application實例在整個應用的生命周期中全局可訪問的特性,來實現這個全局事件的。當然,你也可以將他綁定在任意全局可訪問的的Component上。 全局事件一個最大優勢在于:在任意需要的時候,都可以觸發全局事件,也可以在任意必要的時候綁定,或解除一個事件: ~~~ Yii::$app->on('bar', function ($event) { echo get_class($event->sender); // 顯示當前觸發事件的對象的類名稱 }); ~~~ ~~~ Yii::$app->trigger('bar', new Event(['sender' => $this])); ~~~ 上面的 `Yii::$app->on()` 可以在任何地方調用,就可以完成事件的綁定。而 `Yii::$app->trigger()` 只要在綁定之后的任何時候調用就OK了。
                  <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>

                              哎呀哎呀视频在线观看