Laravel延迟服务提供者`provide`未被调用

时间:2016-09-23 14:30:39

标签: php laravel-5 laravel-5.2

我有以下定义:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\SomeClass;

class SomeProvider extends ServiceProvider
{
    protected $defer = true;

    public function register()
    {
        $this->app->bind(SomeClass::class, function ($app)
        {
            return new SomeClass();
        });
    }

    public function provides()
    {
        die("This never gets called");
        return [SomeClass::class];
    }
}

它会按预期返回SomeClass的实例,但根据文档,如果$defer为true,则应调用provides()方法。无论我将$defer设置为什么,无论我是否真的要求SomeClass的实例,都不会调用provides()

我要求课程实例的方式如下:

App::make('SomeClass');

2 个答案:

答案 0 :(得分:3)

简答:
已编译的清单文件已由框架编译。

Laravel第一次构建应用程序(并在IoC中解析所有服务提供程序) 它写入名为 services.php 的缓存文件(即清单文件,放在:bootstrap/cache/services.php)。
因此,如果清除已编译的via php artisan clear-compiled命令,则应强制框架重建清单文件,您可以注意调用provides方法。 在下一个调用/请求provides方法不再被调用。

框架启动的顺序几乎是这样的:

//public/index.php
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
    \Illuminate\Foundation\Http\Kernel::__construct();
    \Illuminate\Foundation\Http\Kernel::handle();
        \Illuminate\Foundation\Http\Kernel::sendRequestThroughRouter();
            \Illuminate\Foundation\Http\Kernel::bootstrap();
                \Illuminate\Foundation\Application::bootstrapWith();
                    # where $bootstrapper is a item from \Illuminate\Foundation\Http\Kernel::$bootstrappers 
                    # and $this is instance of \Illuminate\Foundation\Application
                    \Illuminate\Foundation\Application::make($bootstrapper)->bootstrap($this);

其中一个bootstrappers是Illuminate\Foundation\Bootstrap\RegisterProviders 调用\Illuminate\Foundation\Application::registerConfiguredProviders()然后调用 \Illuminate\Foundation\ProviderRepository::__construct()最后:

\Illuminate\Foundation\ProviderRepository::load()

调用\Illuminate\Foundation\ProviderRepository::load()时,所有服务提供商都已注册 \Illuminate\Support\ServiceProvider::provides()也被称为。{/ p>

以下是您应该知道的代码段(来自\Illuminate\Foundation\ProviderRepository::load):

    /**
     * Register the application service providers.
     *
     * @param  array  $providers
     * @return void
     */
    public function load(array $providers)
    {
        $manifest = $this->loadManifest();

        // First we will load the service manifest, which contains information on all
        // service providers registered with the application and which services it
        // provides. This is used to know which services are "deferred" loaders.
        if ($this->shouldRecompile($manifest, $providers)) {
            $manifest = $this->compileManifest($providers);
        }

        // Next, we will register events to load the providers for each of the events
        // that it has requested. This allows the service provider to defer itself
        // while still getting automatically loaded when a certain event occurs.
        foreach ($manifest['when'] as $provider => $events) {
            $this->registerLoadEvents($provider, $events);
        }

        // We will go ahead and register all of the eagerly loaded providers with the
        // application so their services can be registered with the application as
        // a provided service. Then we will set the deferred service list on it.
        foreach ($manifest['eager'] as $provider) {
            $this->app->register($this->createProvider($provider));
        }

        $this->app->addDeferredServices($manifest['deferred']);
    }

\Illuminate\Foundation\ProviderRepository::compileManifest()是执行provides()方法的地方。

答案 1 :(得分:0)

在进行自己的测试之后,这似乎是一个问题(可能"问题"在这种情况下是一个强有力的词)。

如果您在具有类名称的服务提供商中注册某些内容,Laravel将返回该类并忽略服务提供商中的任何内容。我开始做你做的事情......

protected $defer = true;

public function register()
{
    $this->app->bind(SomeClass::class, function ($app)
    {
        return new SomeClass();
    });
}

public function provides()
{
    dd('testerino');
}

$test = \App::make('App\SomeClass');

$testSomeClass的一个实例。但是如果我做出以下改变......

$this->app->bind('test', function ($app) { ... }

并使用

$test = \App::make('test');

然后它会点击deffered函数并输出文本testerino

我认为这里的问题是Laravel知道你只是想抓住一堂课。在这种情况下,没有理由注册你要向容器注册的内容,除了告诉Laravel在App\SomeClass的实例应该创建{{1}的实例时,你不做任何事情。 1}}。

但是,如果你在调用App\SomeClass时告诉Laravel你想要一个App\SomeClass的实例,那么它实际上需要将该类绑定到App::make('test')所以我认为它开始支付关注服务提供商。