我有以下定义:
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');
答案 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');
而$test
是SomeClass
的一个实例。但是如果我做出以下改变......
$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')
所以我认为它开始支付关注服务提供商。