通过Closure::bindTo的能力实现外部增加实例内部方法,并且可调用实例内私有变量和方法。
场景说明:用户实例里面没有一些action级操作,只存储了信息相关的数据,将action操作单独设定一个类,正常调用一个like方法的方法参数为function like($user1, $user2);
但希望是可以function like($user);
并且希望的调用方式是$user1->like($user2);
所以利用这个特性来实现;
首先定义了customMetho
d的trait用于可复用。包含isCustomMethod
判断是否自定义方式,callCustomMethod
调用实例方法,addCustomMethod
外部增加方法。
<?php
trait customMethod{
private static $_custom_method = [];
public static function isCustomMethod($method){
return isset(self::$_custom_method[$method]);
}
public function callCustomMethod($method, $args){
if(self::isCustomMethod($method)){
$callback = self::$_custom_method[$method];
$callback = $callback->bindTo($this, get_called_class());
return $callback(...$args);
}
return null;
}
public static function addCustomMethod($method, $function){
self::$_custom_method[$method] = $function;
}
}
根据刚才的场景定义定义一个User
类,并且在__call
的魔术方法内调用callCustomMethod
,理论上需要一个判断是否有自定义方法。
<?php
class User {
use customMethod;
public $name;
public function __construct($name){
$this->name = $name;
}
public function say(){
echo 'name:' . $this->name;
}
public function __call($method, $args){
return $this->callCustomMethod($method, $args);
}
}
然后定义一个Action类,包含一个User自身,以及Like方法;
<?php
class Action{
private $user;
public function __construct($user){
$this->user = $user;
}
public function like($user){
echo $this->user->name . '[like]:' . $user->name;
}
}
然后我们调试一下,通过addCustomMethod
的方式加入一个匿名函数:
<?php
User::addCustomMethod('like', function($user){
$like = new Action($this);
$like->like($user);
});
$user = new User('Lin');
$user->say();
echo PHP_EOL.'----'.PHP_EOL;
$user->like(new User('Yu'));
得到的输出结果,符合预想的结果:
Lin
----
Lin[like]:Yu
虽然这段代码的意义相当于:
<?php
$action = new Action($user1);
$action->like($user2);
但总感觉哪里不对劲,当然说like
方法写在User
里面是合适的;但是某些场景下,User
和Action
的代码是分开的或者不是同一个库下,那么这种委婉的方式增加方法调用感觉是一种方案;因User
里面的属性/方法是私有但外部需要使用的情况也是适用的。