1.2 - Laravel 5.6 - Extend 扩展机制

扩展机制也挺重要。先看下文档的解释

extend 方法可以修改解析的服务。例如,当一个服务被解析后,你可以添加额外的代码去修饰或配置这个服务。 extend 方法接受一个闭包,闭包的唯一参数和返回值都是一个服务:


$this->app->extend(Service::class, function($service) {
    return new DecoratedService($service);
});

不明白?我也不明白。我用我的话讲一下。

extend主要的作用是在解析后,使用一个闭包函数产生的值(通常为当前实体类的子类)替换对应父实体类,从而对其产生扩展影响。这个闭包的参数和返回值都必须是对象。比如:


app()->extend('service', function ($service, $app) {
    return new DecoratedService($service);
});

这里DecoratedService 是 service 的子类。

更简单来说,当我们从Container容器中取出一个实例后,用这个类的子类实例替换掉当前这个父类实例,达到扩展的作用。

当然这是一种用法。还有一些细节,我们去看下源代码。

我们看下源代码:

1.先获取Container中这个id($abstract)的别名

2.查看容器已有的实例数组instance里面有没有对应的实例,如果有直接执行我们extend的这个闭包方法的返回值,存入这个数组中。就是替换了原来的实例。

2.2.然后使用了rebound(),目的是看看有没有附带的回调函数,触发它,这个我们在回调函数中会提。

这说明extend的时候会触发回调函数。

3.如果instance中没有找到对应的实例,就把这个闭包函数存入extenders数组,做个记录,以后用。

并且如果这个id已经被resolved过了,还要触发rebound函数,触发一些对应的回调函数。


public function extend($abstract, Closure $closure)
{
    $abstract = $this->getAlias($abstract);
    if (isset($this->instances[$abstract])) {
        $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
        $this->rebound($abstract);
    } else {
        $this->extenders[$abstract][] = $closure;
        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }
}

现在就很清楚了。

总结

extend的机制就是如果instance中存在实例,就用extend中的闭包执行结果替换掉。如果instances实例列表中不存在就存起来以后备用。

某个适用场景可以是:用子类实例替换父类实例达到扩展的作用。

但不管怎么样,都会触发当前存在的回调函数一次。下一章讲一下回调函数。

实测实例

实例1:替换依赖的实例对象

本来的Boss实例的依赖Money是一个Cheque对象,最后解析的时候,被替换成了Dollar对象。


public function testClosure(){
    app()->bind('boss', Boss::class);

    $this->app->when(Boss::class)
    ->needs(Money::class)
    ->give(Cheque::class);
    app()->extend(Money::class, function() {
    return new Dollar();
    });
    $boss= app()->make('boss');
    $output = $boss->getA();
    $this->assertEquals($output, 1);
}

//

interface Money
{
    public function getAmount();
}

class Dollar implements Money
{
    public function getAmount()
    {
        return 1;
    }
}

class Cheque implements Money
{
    public function getAmount()
    {
        return 100000;
    }
}

class Worker
{
    private $money;
    public function __construct(Money $money)
    {
        $this->money = $money; // prints '1'
    }

    public function getA(){
        return $this->money->getAmount();
    }
}

class Boss
{
    private $money;
    public function __construct(Money $money)
    {
        $this->money = $money; // prints '100000'
    }
    public function getA(){
        return $this->money->getAmount();
    }
}

实例2:子类替换父类,向下拓展

public function testClosure()
{
    app()->extend(Money::class, function() {
    return new Dollar();
    });
    $money= app()->make(Money::class);
    $output = $money->getAmount();
    $this->assertEquals($output, 1);
}

class Money
{
    public function getAmount(){
        return 100;
    }
}

class Dollar extends Money
{
    public function getAmount()
    {
        return 1;
    }
}

class Cheque extends Money
{
    public function getAmount()
    {
        return 100000;
    }
}

实例3:向其他地方拓展, extend第二个参数闭包的第一个参数就是 extend第一个参数的实例,在这里就是我们事先绑定的Money::class实例。

public function testClosure()
{
    app()->bind('money', Money::class);
    app()->extend('money', function($money) {
    return new Currenty($money);
    });
    $boss= app()->make('money');
    $output = $boss->getAmount();
    $this->assertEquals($output, "harveynorman");
}

class Currenty{
    protected $money;
    public function __construct(Money $money)
    {
        $this->money = $money;
    }
    public function getAmount(){
        return "harveynorman";
    }
}

class Money
{
    public function getAmount(){
        return 100;
    }
}

class Dollar extends Money
{
    public function getAmount()
    {
        return 1;
    }
}

class Cheque extends Money
{
    public function getAmount()
    {
        return 100000;
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

大佬你这些文章get到点了 ,感谢下。

3年前 评论

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