[PHP]类预定义,使类支持外置定义方法

通过Closure::bindTo的能力实现外部增加实例内部方法,并且可调用实例内私有变量和方法。

场景说明:用户实例里面没有一些action级操作,只存储了信息相关的数据,将action操作单独设定一个类,正常调用一个like方法的方法参数为function like($user1, $user2);但希望是可以function like($user);并且希望的调用方式是$user1->like($user2);所以利用这个特性来实现;

首先定义了customMethod的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里面是合适的;但是某些场景下,UserAction的代码是分开的或者不是同一个库下,那么这种委婉的方式增加方法调用感觉是一种方案;因User里面的属性/方法是私有但外部需要使用的情况也是适用的。