dtc9222 2016-09-23 14:30
浏览 172
已采纳

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

I have the following definition:

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];
    }
}

And it returns an instance of SomeClass as expected, except that according to the documentation, if $defer is true then the provides() method should be called. No matter what I set $defer to, and no matter if I actually ask for an instance of SomeClass or not, provides() is never called.

The way I'm asking for an instance of the class is as follows:

App::make('SomeClass');
  • 写回答

2条回答 默认 最新

  • doulu2576 2016-09-23 18:23
    关注

    Short answer:
    Your compiled manifest file is already compiled by framework.

    On the first time when Laravel build the application (and resolves all of services providers in IoC) it writes to cached file named services.php (that is, the manifest file, placed in: bootstrap/cache/services.php).
    So, if you clear the compiled via php artisan clear-compiled command it should force framework to rebuild the manifest file and you could to note that provides method is called. On the next calls/requests provides method is not called anymore.

    The sequence of framework boot is nearly like this:

    //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);
    

    One of bootstrappers is Illuminate\Foundation\Bootstrap\RegisterProviders which invokes \Illuminate\Foundation\Application::registerConfiguredProviders() and then invokes \Illuminate\Foundation\ProviderRepository::__construct() and finally:

    \Illuminate\Foundation\ProviderRepository::load()

    When \Illuminate\Foundation\ProviderRepository::load() is called all services providers is registered and \Illuminate\Support\ServiceProvider::provides() are called also well.

    And here is the snippet you should know (from \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() is the place where your provides() method is performed.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算
  • ¥15 powerbuilder中的datawindow数据整合到新的DataWindow
  • ¥20 有人知道这种图怎么画吗?
  • ¥15 pyqt6如何引用qrc文件加载里面的的资源
  • ¥15 安卓JNI项目使用lua上的问题
  • ¥20 RL+GNN解决人员排班问题时梯度消失
  • ¥60 要数控稳压电源测试数据
  • ¥15 能帮我写下这个编程吗
  • ¥15 ikuai客户端l2tp协议链接报终止15信号和无法将p.p.p6转换为我的l2tp线路