强化版Macroable

在阅读Laravel源码的过程中,发现了Macroable这个trait,觉得如果能将带有依赖注入的方法也”揉杂”到指定类中就好了。于是经过尝试,有了下面的强化版Macroable。

<?php

use Closure;
use Illuminate\Support\Traits\Macroable;
use ReflectionClass;
use ReflectionMethod;
use BadMethodCallException;

trait InjectMacroable
{
    use Macroable;

    /**
     * Register a custom macro.
     *
     * @param  object $mixin
     * @param  array  $methods
     *
     * @return void
     */
    public static function enhanceMacro($mixin, $methods)
    {
        if(! is_array($methods)) $methods = [$methods];
        if(! empty($methods)){
            $reflection_class = new ReflectionClass($mixin);
            foreach($methods as $method){
                if(!self::hasMacro($method) && $reflection_method = $reflection_class->getMethod($method)){
                    static::$macros[$reflection_method->name] = $reflection_method->getClosure($mixin);
                }
            }
        }
    }

    /**
     * Mix another object into the class.
     *
     * @param  object  $mixin
     * @return void
     *
     * @throws \ReflectionException
     */
    public static function mixin($mixin)
    {
        $methods = (new ReflectionClass($mixin))->getMethods(
            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
        );

        foreach ($methods as $method) {
            if($method->isConstructor() || $method->isDestructor()) continue;
            $method->setAccessible(true);

            if(!self::hasMacro($method->name) && $closure=$method->getClosure($mixin)){
                static::macro($method->name, $closure);
            }
        }
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException(sprintf(
                'Method %s::%s does not exist.', static::class, $method
            ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            return app()->call($macro, $parameters);
        }

        return call_user_func_array($macro, $parameters);
    }
}

如果需要在AController中引入BController的Test方法:

class BController{
    public function Test(Request $request, Cache $cache)
    {
        //todo
    }
}
class AController{
    use InjectMacroable ;
    public function __construct()
    {
        self::enhanceMacro(new BController(), 'Test');
    }
}

如果想将BController里的所有方法都引入,则:

self::mixin(new BController());

在路由中定义AController@Test即可访问。
引入的普通方法也能正常访问。
因为多数场景都是调用非静态方法,所以这里只实现了__call方法,有需要的同学可以自行尝试实现__callStatic。
另外,想引入其他类的方法也可以通过继承之类的方式实现,这里只是做一些不同的尝试。
最后,希望和大家一起享受代码的快乐。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

没看明白,区别是什么 :sweat_smile:

1年前 评论
milksoul (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!