转自:http://www.digpage.com/property.html
一、属性
变量与属性、函数与方法的区别:
class A {
public $name;
function b(){
$otherName = 1;
$this->name = $otherName;
}
}
(1)类中的有主的变量叫属性。$name就是A的属性,$otherName无主,就叫变量。你可以$a->name,但不能$a->otherName(2)函数是单独存在的,面向过程;方法是依赖于类存在的,面向对象。方法是类里的"函数"。
如果要使你的类支持属性,必须继承自yii\base\Object。Yii中属性是通过PHP的魔法函数 __get() __set() 来产生作用的。在读取和写入对象的一个不存在的成员属性时, __get() __set() 会被自动调用。(PS:取和写入对象的一个不存在的成员方法用__call() )
如果访问一个对象的某个属性, Yii会调用名为get属性名()的函数。如果修改某个属性,会调用名为get属性名()的函数
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);
}
}
实现属性的3步走
继承自 yii\base\Object 。
声明一个用于保存该属性的私有成员变量。
提供getter或setter函数,或两者都提供,用于访问、修改上面提到的私有成员变量。 如果只提供了getter,那么该属性为只读属性,只提供了setter,则为只写
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);
}
}
一个提高效率的小技巧就是:使用 $pro =
$object->getPro() 来代替 $pro = $object->pro, 用
$objcect->setPro($value) 来代替 $object->pro = $value 。 这在功能上是完全一样的效果,但是避免了使用 __get() 和 __set() ,相当于绕过了遍历的过程。
注意的是:
由于自动调用 __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的与属性相关的7个方法
__get()
__set()
__isset() 用于测试属性值是否不为 null
__unset() 用于将属性值设为 null
hasProperty() 用于测试是否有某个属性。
canGetProperty() 测试一个属性是否可读,只要定义了getter,属性即可读。
canSetProperty() 测试一个属性是否可写,只要定义了setter,属性即可写。
Object和Component
yii\base\Component 继承自 yii\base\Object。Object具有属性功能,Component具有属性、事件、行为的功能。
Yii定位于一个基于组件的框架。Yii几乎所有的核心类都派生于(继承自) yii\base\Component
Application通过配置数组注册组件的过程详见 SL部分https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/80158100
二、事件
(1)事件的定义:在特定的时点,触发执行预先设定的一段代码。一个事件handler本质上就是一段PHP代码,即一个PHP函数。事件是代码解耦的一种方式
当需要使用事件时,请从 yii\base\Component 进行继承。同时,Yii中还有一个与事件紧密相关的yii\base\Event
(2)事件handler-----事件处理程序,负责事件触发后怎么办的问题。
一个事件handler必须具有以下形式:
function ($event) {//$event就是前面提到的 yii\base\Event
}
(3)事件的绑定-----yii\base\Component::on()告诉Yii这个handler是负责处理哪种事件的。把事件和事件handler这绑在一起,当事件被触发时,事件handler执行。
假设被绑定的类继承yii\base\Component,该类的实例化对象是$Component
$Component->on(事件名,事件handler,$data=null,$apppend=true);//$data表示$event->data,$pppend=true则表示该事件被最先执行
(4)事件的触发-----yii\base\Component::trigger()
$Component->trigger(事件名,$event=null);
(5)事件的解除-----yii\base\Component::off()
//移除某个事件
$Component->off(事件名);
//移除全部事件
yii\base\Component::off();
(6)多个事件handler的顺序
要使后加上的事件handler先运行,只需在调用 yii\base\Component::on() 进行绑定时,将第四个参数设为 $append 设为 false 那么这个handler就会被放在数组的最前面了,它就会被最先执行
(7)事件的级别
<7.1>实例级别事件
事件的触发、处理全部都在实例范围内,不与类的其他实例发生关系,也不与其他类、其他实例发生关系。
<7.2>类级别事件用于响应所有类实例的事件。
<7.3>全局级别事件
全局事件,本质上只是一个实例事件罢了。他只是利用了Application实例在整个应用的生命周期中全局可访问的特性,来实现这个全局事件的。
全局事件一个最大优势在于:在任意需要的时候,都可以触发全局事件,也可以在任意必要的时候绑定,或解除一个事件:
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了。
三、行为
(1)行为的定义
使用行为可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法,就好像类本来就有这些属性和方法一样。 而且不需要写一个新的类去继承或包含现有类。
Yii中的行为,其实是 yii\base\Behavior 类的实例。
(2)写一个行为
定义一个行为,就是写一个 Behavior的子类,子类中包含了所要注入的属性和方法
class MyBehavior extends \yii\base\Behavior
{
public $prop1;
public function method1()
{
return 'aaa';
}
}
(3)行为的绑定---静态绑定与动态绑定 静态绑定2种方式:通过配置文件绑定 或者 在被绑定的类中加behaviors方法
动态绑定
$Component->attachBehaviors([
'myBehavior1' => new MyBehavior, // 这是一个命名行为
'MyBehavior::className(), // 这是一个匿名行为
]);
(4)行为的解除 //解除名为myBehavior的行为
$Component->detachBehavior('myBehavior');
//解除所有绑定好的行为
$Component->detachBehaviors();
四、注意及实例
1、注意
(1)事件/行为可以绑定到任何一个类上,这个类可以是控制器类、模型类、组件类。。只要是类,就可以绑定行为/事件
(2)事件、行为、组件的区别:
看config/web.php的components这块
[
'class' => 'ClassName',
'propertyName' => 'propertyValue',
'on eventName' => $eventHandler,//事件
'as behaviorName' => $behaviorConfig,//行为
]
说明一个组件里可以绑定事件或行为。一个行为里可以封装属性、方法、事件。组件包括行为,行为包括事件。
2、实例
假设我想在组件类request和普通类Test分别添加自定义的事件和行为,使用静态绑定和动态绑定两种方式。然后在commond命令行下测试效果
config/console文件----------组件类request
'components' => [
'request' => [
'on myeventname' => ['app\common\MyEvent','event1'],
'as mybehaviorname' => [
'class' => \app\common\MyBehavior::class,
'property1' =>'request-property',//重写自定义行为里的属性,但无法重写行为里的方法
],
],
]
app\common\Test.php文件----------普通类Testnamespace app\common;
use yii\base\Component;
class Test extends Component
{
const EVENT_XF = 'event-xf';
// public function __construct()
// {
// $this->on(self::EVENT_XF, ['app\common\MyEvent', 'event1'],'hi!!!');
// }
// public function behaviors()
// {
// return [
// 'myBehavior' => MyBehavior::className(),
// ];
// }
}
app\common\MyEvent.php文件----------自定义事件namespace app\common;
use yii\base\Event;
class MyEvent
{
static public function event1 (Event $event) {
echo '自定义事件,事件内容是:'; var_dump($event);
}
}
app\common\MyEvent.php文件----------自定义行为namespace app\common;
use yii\base\Behavior;
use yii\base\Event;
use yii\db\ActiveRecord;
class MyBehavior extends Behavior
{
const BEHAVIOR_EVENT = 'behavior-event';//自定义的一个事件名
public $property1 = 'This is property in MyBehavior.';//行为的一个属性
public function method1() //行为的一个方法
{
return 'Method in MyBehavior is called.';
}
public function events() //重载events() 使得在事件触发时,调用行为中的一些方法
{
// // 法1:静态绑定的方式,无法指定data
// return [
// ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
// self::BEHAVIOR_EVENT => ['app\common\MyEvent','event1'],
// ];
// 法2:静态绑定的方式,可以指定data
$this->owner->on(ActiveRecord::EVENT_BEFORE_VALIDATE,[$this,'beforeValidate'],'GGGG');
$this->owner->on(self::BEHAVIOR_EVENT,['app\common\MyEvent','event1'],'HHHH');
return [];
}
// 注意beforeValidate 是行为的成员函数,而不是绑定的类的成员函数。
public function beforeValidate(Event $event)
{
echo '自定义行为里的触发事件,行为里的事件无法传data内容';var_dump($event);
}
}
app\commands\TestController.php----------命令行测试
namespace app\commands;
use Yii;
use yii\console\Controller;
use yii\di\Container;
use app\common\Test;
use yii\base\Event;
use app\common\MyBehavior;
use yii\db\ActiveRecord;
class TestController extends Controller
{
public function actionTest1()
{
//事件触发--配置文件console的request组件类加事件
$event = new Event();
$event->sender = 'aaa';
$event->data = 'bbb';//不是在on绑定的时候指定的data不算
Yii::$app->request->trigger('myeventname',$event);
echo PHP_EOL;
//事件触发--普通自定义类加事件
$TestClass = new Test();
$event2 = new Event();
$TestClass->on($TestClass::EVENT_XF, ['app\common\MyEvent', 'event1'],'hi!!!');//此句也可在coutry的construct里绑定。并且此处第3个参数为指定的data
$event2->sender = 'ccc';
$TestClass->trigger(Test::EVENT_XF, $event2);
}
public function actionTest2()
{
//行为中的属性、方法、事件--配置文件web的request组件类加行为
$event = new event();
$event->sender = 'wuhuaguo';
echo Yii::$app->request->property1, PHP_EOL;
echo Yii::$app->request->method1(), PHP_EOL;
Yii::$app->request->trigger(ActiveRecord::EVENT_BEFORE_VALIDATE, $event);//行为里的事件
Yii::$app->request->trigger(MyBehavior::BEHAVIOR_EVENT, $event);//行为里调用外部事件
echo PHP_EOL;
//行为中的属性、方法、事件--普通类加行为
$TestClass = new Test();
$event2 = new Event();
$TestClass->attachBehaviors(['myBehavior' => new MyBehavior]);//此处可以改成在country类里使用behaviors()函数绑定
$event2->sender = 'wuhuaguo2';
echo $TestClass->property1, PHP_EOL;
echo $TestClass->method1(), PHP_EOL;
$TestClass->trigger(ActiveRecord::EVENT_BEFORE_VALIDATE, $event2);//行为里的事件
$TestClass->trigger(MyBehavior::BEHAVIOR_EVENT, $event2);//行为里调用外部事件
}
}
测试结果: