老刘 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, 非常方便的像调用属性一样调用方法.
老刘 Yii2 源码学习笔记之 Component 类的更多相关文章
- 老刘 Yii2 源码学习笔记之 Action 类
Action 的概述 InlineAction 就是内联动作,所谓的内联动作就是放到controller 里面的 actionXXX 这种 Action.customAction 就是独立动作,就是直 ...
- 老刘 Yii2 源码学习笔记之 Module 类
关系类图 从上图可以看出 Application 类继承了 Module,在框架中的是非常重要角色. 加载配置 public function setModules($modules) { forea ...
- yii2源码学习笔记(九)
Application是所有应用程序类的基类,接下来了解一下它的源码.yii2\base\Application.php. <?php /** * @link http://www.yiifra ...
- yii2源码学习笔记(八)
Action是所有控制器的基类,接下来了解一下它的源码.yii2\base\Action.php <?php /** * @link http://www.yiiframework.com/ * ...
- yii2源码学习笔记(二十)
Widget类是所有部件的基类.yii2\base\Widget.php <?php /** * @link http://www.yiiframework.com/ * @copyright ...
- yii2源码学习笔记(十八)
View继承了component,用于渲染视图文件:yii2\base\View.php <?php /** * @link http://www.yiiframework.com/ * @co ...
- yii2源码学习笔记(十七)
Theme 类,应用的主题,通过替换路径实现主题的应用,方法为获取根路径和根链接:yii2\base\Theme.php <?php /** * @link http://www.yiifram ...
- yii2源码学习笔记(十四)
Module类是模块和应用类的基类. yiisoft\yii2\base\Module.php <?php /** * @link http://www.yiiframework.com/ * ...
- yii2源码学习笔记(十三)
模型类DynamicModel主要用于实现模型内的数据验证yii2\base\DynamicModel.php <?php /** * @link http://www.yiiframework ...
随机推荐
- 缺少libssl.so.4文件
1.报错代码: /usr/local/pureftpd/sbin/pure-ftpd: error while loading shared libraries: libssl.so.4: wrong ...
- [bzoj1497][NOI2006]最大获利_网络流_最小割
最大获利 bzoj-1497 题目大意:可以建立一个点,花费一定的代价:将已经建立的两个点之间连边,得到一定收益.有些节点之间是不允许连边的. 注释:1<=点数<=5,000,1<= ...
- input输入框限制输入正整数、小数、字母、文字
有的时候需要限制input的输入格式: 例如,输入大于0的正整数 <input onkeyup="if(this.value.length==1){this.value=this.va ...
- 2018上C语言程序设计(高级)作业- 第0次作业
准备工作(10分) 1.在博客园申请个人博客. 2.加入班级博客(2班班级博客链接地址)(1班班级博客链接地址) 3.关注邹欣老师博客.关注任课老师博客. 4.加入讨论小组,学习过程中遇到问题不要随意 ...
- 听翁恺老师mooc笔记(6)--指针运算
指针值加1就是将指针值加上sizeof(指针所指变量的类型) 1+1=2,那么指针加1是加上了1这个数字吗?试一下,在代码中定义了char数组,char也是整数,数组名是ac,ac中有10个元素,0- ...
- sys模块的使用
import sys,time ''' if sys.argv[1]=='sleepy': print('nongsi') else: print('....')''' #进度条 for i in r ...
- ArcGIS地图打印那些事
记录了通过ArcGIS的PringTask进行地图打印,以及借助html2canvas实现屏幕截图的方法.两个方法各有适用的情景.过程中涉及的相关资源和问题给出链接,自行查看. 1.需求背景 地图打印 ...
- nyoj 星期几?
星期几? 时间限制:500 ms | 内存限制:65535 KB 难度:2 描述 Acmer 小鱼儿 埋头ku算一道题 条件:已知给定 一日期 告诉你 ...
- nyoj 鸡兔同笼
鸡兔同笼 时间限制:3000 ms | 内存限制:65535 KB 难度:1 描述 已知鸡和兔的总数量为n,总腿数为m.输入n和m,依次输出鸡和兔的数目,如果无解,则输出"No an ...
- C语言Linix服务器网络爬虫项目(一)项目初衷和网络爬虫概述
一.项目初衷和爬虫概述 1.项目初衷 本人的大学毕设就是linux上用c写的一个爬虫,现在我想把它完善起来,让他像一个企业级别的项目.为了重复发明轮子来学习轮子的原理,我们不使用第三方框架(这里是说的 ...