老刘 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 ...
随机推荐
- spring - boot 监控管理模块搭建
Spring-Actuator是Spring-boot对应用监控的集成模块,提供了我们对服务器进行监控的支持,使我们更直观的获取应用程序中加载的应用配置.环境变量.自动化配置报告等. 使用Spring ...
- 【Python】 命名空间与LEGB规则
命名空间与LEGB规则 之前隐隐约约提到过一些关于Python赋值语句的特殊性的问题,这个问题的根源就在于Python中的变量的命名空间机制和之前熟悉的C也好java也好都不太一样. ■ 命名空间 所 ...
- #Python3.6.2(32位) pip安装 和 pygame 环境配置
#首先确认电脑已经安装python ,可通过在命令行下 执行 python --version确认. 1. 到 https://pypi.python.org/pypi/setuptools/ 下载 ...
- Java NIO系列教程(六) 多路复用器Selector
多路复用器Selector是Java NIO编程的基础,熟练地掌握Selector对于掌握NIO编程至关重要.多路复用器提供选择已经就绪的任务的能力.简单来讲,Selector会不断地轮询注册在其上的 ...
- java.lang.Thread、java.lang.ThreadGroup和java.lang.ThreadLocal<T>详细解读
一.Thread类 public class Thread extends Object impments Runnable 线程是程序中的 执行线程.java虚拟机允许应用程序并发地运行多个执行线 ...
- RESTFUL风格 put 报错 HTTP Status 405 - JSPs only permit GET POST or HEAD
出现下图这种情况时是controller所return的jsp视图找不到, 所以提示请求只允许GET.POST.HEAD. 解决方案 1.若返回视图,把表单中name为_method的input值改为 ...
- SQL中的DML、DDL以及DCL
DML(data manipulation language)是数据操纵语言:它们是SELECT.UPDATE.INSERT.DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的 ...
- oracle exp(expdp)数据迁移(生产环境,进行数据对比校验)
前言:客户需要迁移XX 库 ZJJJ用户(迁移到其他数据库),由于业务复杂,客户都弄不清楚里面有哪些业务系统,为保持数据一致性,需要停止业务软件,中间件,杀掉oracle进程. 一.迁移数据倒出部分= ...
- CSS3动画箭头
<style type="text/css"> .arrow { display: block; width: 20px; height: 20px; position ...
- Struts2学习笔记四 OGNL
OGNL,全称为Object-Graph Navigation Language(对象图表达语言),它是一个功能强大的表达式语言,用来获取和设置Java对象的属性,调用java对象的方法,同时能够自动 ...