老刘 Yii2 源码学习笔记之 Component 类

类图关系

老刘 Yii2 源码学习笔记之 Component 类

属性与方法

class Component extends BaseObject
{ private $_events = [];
private $_eventWildcards = [];
private $_behaviors; public function __get($name)
public function __set($name, $value)
public function __isset($name)
public function __unset($name)
public function __call($name, $params)
public function __clone() public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
public function hasMethod($name, $checkBehaviors = true)
public function behaviors()
public function hasEventHandlers($name) public function on($name, $handler, $data = null, $append = true)
public function off($name, $handler = null)
public function trigger($name, Event $event = null)
public function getBehavior($name)
public function getBehaviors()
public function attachBehavior($name, $behavior)
public function attachBehaviors($behaviors)
public function detachBehavior($name)
public function detachBehaviors()
public function ensureBehaviors()
private function attachBehaviorInternal($name, $behavior)
}

除了className() 方法,BaseObject 父类的方法已经全部重写,因为 BaseObject 只是一个单独的基类,Component 类与 Event 和 Behavior 有更复杂的关联。

事件

事件的代码可以先看 on 方法。[$handler, $data] 数组 0 是 handler  2 是 data 参数

    /**
* @param $name 事件名称
* @param $handler 处理回调
* @param null $data 参数
* @param bool $append 是否追加
*/
public function on($name, $handler, $data = null, $append = true)
{
//初始化行为
$this->ensureBehaviors(); if (strpos($name, '*') !== false) {
if ($append || empty($this->_eventWildcards[$name])) {
$this->_eventWildcards[$name][] = [$handler, $data];
} else {
array_unshift($this->_eventWildcards[$name], [$handler, $data]);
}
return;
}
// 如果已经存在事件名称追加
if ($append || empty($this->_events[$name])) {
$this->_events[$name][] = [$handler, $data];
} else {
array_unshift($this->_events[$name], [$handler, $data]);
}
}

事件的执行,下面的代码可以看到 如果 $event 为 NULL, 会 new Event, 并设置 sender 属性为当前 类, $event->data 为 on 的data 参数,  方法的最后一行, Event::trigger($this, $name, $event)  会执行类级别的事件

 public function trigger($name, Event $event = null)
{
$this->ensureBehaviors(); $eventHandlers = [];
foreach ($this->_eventWildcards as $wildcard => $handlers) {
if (StringHelper::matchWildcard($wildcard, $name)) {
$eventHandlers = array_merge($eventHandlers, $handlers);
}
} if (!empty($this->_events[$name])) {
$eventHandlers = array_merge($eventHandlers, $this->_events[$name]);
} if (!empty($eventHandlers)) {
if ($event === null) {
$event = new Event();
}
if ($event->sender === null) {
$event->sender = $this;
}
$event->handled = false;
$event->name = $name;
foreach ($eventHandlers as $handler) {
$event->data = $handler[1];
call_user_func($handler[0], $event);
// stop further handling if the event is handled
if ($event->handled) {
return;
}
}
} // invoke class-level attached handlers
Event::trigger($this, $name, $event);
}

可以举个例子, 可以看一下,全部都执行了,可以把 $event 打印一下看一下data

namespace app\events;
use yii\base\Event; class MyEvent extends Event {
public $message; function __construct($message)
{
parent::__construct();
$this->message = $message;
}
} class SiteController extends Controller
{
const EVENT_TEST = 'test'; /**
* {@inheritdoc}
*/
function init()
{
parent::init(); // TODO: Change the autogenerated stub
$this->on(self::EVENT_TEST, [new \app\models\EventTest(), 'add'], ['a', 'b']);
Event::on(SiteController::className(), self::EVENT_TEST, function () { echo 'class level event';});
}
public function actionTest()
{
$event = new MyEvent('implements');
$this->trigger(self::EVENT_TEST, $event);
echo 'done';
}
}

行为

使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法,就好像类本来就有这些属性和方法一样。 而且不需要写一个新的类去继承或包含现有类。把行为注入到类中。举个例子

class MyClass extends yii\base\Component
{
// 空的
} // Step 2: 定义一个行为类,他将绑定到MyClass上
class MyBehavior extends yii\base\Behavior
{
// 行为的一个属性
public $property1 = 'This is property in MyBehavior.'; // 行为的一个方法
public function method1()
{
return 'Method in MyBehavior is called.';
}
} $myClass = new MyClass();
$myBehavior = new MyBehavior(); // Step 3: 将行为绑定到类上
$myClass->attachBehavior('myBehavior', $myBehavior); // Step 4: 访问行为中的属性和方法,就和访问类自身的属性和方法一样
echo $myClass->property1;
echo $myClass->method1();

以上是怎么做到呢, 可以看 Component  里的魔术方法 set、get、call ,对行为对象的属性和方法进行了注入。

public function __get($name)
{
//其他省略
// behavior property
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canGetProperty($name)) {
return $behavior->$name;
}
}
//其他省略
} public function __set($name, $value)
{
//其他省略
// behavior property
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name)) {
$behavior->$name = $value;
return;
}
}
//其他省略
} public function __call($name, $params)
{
$this->ensureBehaviors();
foreach ($this->_behaviors as $object) {
if ($object->hasMethod($name)) {
return call_user_func_array([$object, $name], $params);
}
}
throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
}

Behavior 类中可以绑定事件, 如下面的代码,继承 Behavior并设置 events 属性.

public function attach($owner)
{
$this->owner = $owner;
foreach ($this->events() as $event => $handler) {
$owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
}
}

举个具体的例子,下面的代码

namespace app\component;

use Yii;
use yii\base\Controller;
use yii\base\Behavior; class MyBehavior extends Behavior
{
public $param; public function events()
{
return [
Controller::EVENT_BEFORE_ACTION => 'handlerBeforeAction'
];
} public function handlerBeforeAction()
{
echo '由行为注册的组件事件, 执行 beforeAction <br>';
} public function behaviorMethod()
{
echo '行为中的定义的方法';
}
} namespace app\controllers; use Yii;
use yii\web\Controller;
use app\component\MyBehavior; /**
* Class CurdController
* @package app\controllers
*/
class BehaviorController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => MyBehavior::className(),
'param' => 'behavior param'
]
];
} public function actionIndex()
{
echo '行为中的属性: '.$this->param.'<br>';
$this->behaviorMethod();
echo '<br>';
}
}

执行结果为:

由行为注册的组件事件, 执行 beforeAction
行为中的属性: behavior param
行为中的定义的方法

行为设计灵活,  遵循设计原则对修改关闭,对扩展开放. 行为与 traits 区别可以参考文档核心概念. 行为的添加删除具体可以阅读类里面的详细内容.

属性

属性的设置可以参考魔术方法 set 和 get, 非常方便的像调用属性一样调用方法.

上一篇:基于docker的wekan部署


下一篇:老刘 Yii2 源码学习笔记之 Module 类