今天在看 composer 自动加载的原理的时候看见了一个 给实例化对象的私有属性赋值的操作,特此记录一下
一般我们都知道,在类的外部是没有办法为类的私有属性赋值或者改变私有属性的值的。如:
<?php
class Animal {
private static $cat = "cat";
private $dog = "dog";
public $pig = "pig";
}
echo (new Animal)->$dog; //报错
但是如果真的要想为Animal的对象实例的私有属性重新赋值【当然改变私有属性的值的情况很少见,说起看来这有一点点hack的意味了】,可以利用php的 闭包 来实现。
实现的demo
<?php
class Animal {
private static $cat = "cat";
private $dog = "dog";
public $pig = "pig";
}
$animal = new Animal;
/*
* 获取Animal类静态私有成员属性
*/
$cat = static function() {
return Animal::$cat;
};
/*
* 获取Animal实例私有成员属性
*/
$dog = function() {
return $this->dog;
};
/*
* 获取Animal实例公有成员属性
*/
$newDog = function() {
return $this->dog;
};
/**
* bind函数:
* 参数1($closure) : 表示闭包函数
* 参数2($newthis): 你把这个匿名的方法是否放在一个实例中,如果放在实例中,这个参数就是一个实例,如果不放在实例中,那就放null。当你不放在实例中,也就是不存在$this。也就不能用$this(经试验 为null时仅可调用 static 的变量)
* 参数3($newscope): 相当于类和实例调用的区别,函数的作用域, 传类表示静态调用方式,内部可以“类名::属性”的方式使用;实例表示实例调用方式,内部可以“->”
*/
$bindCat = Closure::bind($cat, null, new Animal());// 给闭包绑定了Animal实例的作用域,但未给闭包绑定$this对象
$bindDog = Closure::bind(function() use ($animal){// 给闭包绑定了Animal类的作用域,同时将Animal实例对象作为$this对象绑定给闭包
$animal->dog = 'a new dog';
}, null, 'Animal');
$bindNewDog = Closure::bind($newDog, $animal, $animal);// 将Animal实例对象作为$this对象绑定给闭包,保留闭包原有作用域
echo $bindCat(),'<br>';// 根据绑定规则,允许闭包通过作用域限定操作符获取Animal类静态私有成员属性
echo $bindDog(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象(Animal实例对象)获取Animal实例私有成员属性
echo $bindNewDog(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象获取Animal实例公有成员属性
这里改变实例对象的私有属性值的要点是将实例对象通过 function() user (){}
的方式来把它引入到闭包中,因为闭包已经处于Animal(或说是实例对象)的作用域中,所以就可以访问他私有属性的值了
关于php闭包的bind函数的更多细节,可以参考一下这篇文章