Laravel中通过 Macroable 宏来扩展类的能力

50 篇文章 2 订阅

我们以 Illuminate\Support\Traits\Macroable 为例分析。

通过 trait 可以很方便的在任何类中使用。

Laravel 提供的 Macroable 可以在不改变类结构的情况下为其扩展功能。也就是为一个类动态注入一些方法,并且和该类本身的方法拥有同样的作用域和调用方式。

Macroable 的核心是基于匿名函数的绑定功能,关于这一块请参考:https://blog.csdn.net/raoxiaoya/article/details/103894397

trait Macroable
{
    /**
     * The registered string macros.
     *
     * @var array
     */
    protected static $macros = [];

    /**
     * Register a custom macro.
     *
     * @param  string $name
     * @param  object|callable  $macro
     *
     * @return void
     */
    public static function macro($name, $macro)
    {
        static::$macros[$name] = $macro;
    }

    /**
     * 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) {
            $method->setAccessible(true);

            static::macro($method->name, $method->invoke($mixin));
        }
    }

    /**
     * Checks if macro is registered.
     *
     * @param  string  $name
     * @return bool
     */
    public static function hasMacro($name)
    {
        return isset(static::$macros[$name]);
    }

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

        if (static::$macros[$method] instanceof Closure) {
            return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
        }

        return call_user_func_array(static::$macros[$method], $parameters);
    }

    /**
     * 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 call_user_func_array($macro->bindTo($this, static::class), $parameters);
        }

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

我们可以看到执行绑定是在 __callStatic 和 __call 里面,

如果一个类想要借助 Macroable 来提升能力,基本操作是:

1、use Macroable;

2、调用 macro 添加一个方法到 $macros
macro 可以添加匿名函数和对象,之所能通过调用匿名函数的方式调用对象,前提是该对象要实现 __invoke() 方法,也就是说,调用这个对象的入口是 __invoke() 方法。

例如:
 	class Foo
    {
        use Macroable;
    }

    final class Join
    {
        public function __invoke(...$string)
        {
            return implode('-', $string);
        }
    }

    Foo::macro('join', new Join());
或者
 	Foo::macro('join', function(...$string){
        return implode('-', $string);
    });

3、或者调用 mixin 将一个对象包含的全部方法都注册到当前类中。
使用了反射类获取到public和protected的方法,并且将protected方法设置为可访问,最后调用了这些方法,将返回值注入到 $macros;我们知道注册进去的都是可以被当
作闭包调用的,因此我们反射的这个对象里面的方法的返回值应该是闭包,例如:

final class Str
{
    public function join()
    {
        // 返回匿名函数
        return function(...$string){
            return implode('-', $string);
        };
    }

    public function split()
    {
        // 返回匿名函数
        return function(string $string){
            return explode('-', $string);
        };
    }
}

执行注入
mixin(new Str())

为了更便捷的扩展一个类的功能,macro, mixin, hasMacro 三个方法都被设计为静态调用,可以通过类直接调用来扩展方法。

比如 Maatwebsite/Laravel-Excel 插件就是在服务容器provider中为集合类Collection扩展导出功能的
https://blog.csdn.net/raoxiaoya/article/details/103920781

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值