覆盖 Laravel 容器中的单例

Override Singleton in Laravel Container

我想知道是否有一种简单的方法可以覆盖 Laravel 框架核心中的单例服务集?

例如我正在尝试使用以下提供商重写 app:name 命令服务“”:

use Hexavel\Console\AppNameCommand;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;

class NameCommandProvider extends ServiceProvider
{
    /**
     * Register any other events for your application.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @return void
     */
    public function boot(Dispatcher $events)
    {
        $events->listen(ArtisanStarting::class, function ($event) {
            $event->artisan->resolve('command.app.name');
        }, -1);
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('command.app.name', function ($app) {
            return new AppNameCommand($app['composer'], $app['files']);
        });
    }
}

我 100% 一切正常,因为进行了广泛的检查,无论我将我的服务提供商放在什么顺序(高于或低于 ConsoleSupportServiceProvider)它仍然加载原始 AppNameCommand 而不是我的自定义。

我已经找到了解决方法,但是如果可能的话,了解未来单例服务的行为会很高兴吗? (如果有任何区别,这将使用 Laravel 5.2。)

我看了这个案子,好像不简单。如果您在自定义提供程序中使用 singleton,它将最终被默认提供程序(延迟的提供程序)覆盖,所以它似乎不会是这样。

检查简单的方法行不通后,在这种情况下你需要做的是分析Laravel注册这个命令时发生了什么。

因此,在您的情况下,您首先搜索 command.app.name - 您会看到它在 Illuminate\Foundation\Providers\ArtisanServiceProvider 中,并且存在您可能想要覆盖的方法 registerAppNameCommand

所以现在您查找 ArtisanServiceProvider 的出现以查看它的启动位置 - 您会看到它在 $providers 属性 中的 Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 中(您可能希望变化)。

所以最后你应该寻找 ConsoleSupportServiceProvider 的出现,你会看到它在 config/app.php.

那么在这种情况下你需要做什么:

  • config/app.php 中的更改 - 将 Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 更改为您的自定义 ConsoleSupportServiceProvider
  • 在您的自定义中,您应该从 \Illuminate\Foundation\Providers\ConsoleSupportServiceProvider 扩展,但将 $providersIlluminate\Foundation\Providers\ArtisanServiceProvider 更改为您的自定义 ArtisanServiceProvider
  • 最终创建自定义 ArtisanServiceProvider,它将从 \Illuminate\Foundation\Providers\ArtisanServiceProvider 扩展,您在 singleton
  • 中使用自定义 class 覆盖 registerAppNameCommand

使用这种方式您将实现您的目标(我已经验证自定义 class 将被使用 运行 命令 php artisan app:name)。

或者您可能希望在您的自定义 ArtisanServiceProvider 中从 $devCommands 中删除 'AppName' => 'command.app.name', 并使用您的自定义服务提供商,就像您在注册单身人士的地方所显示的那样,但我还没有尝试过这个方法。

实际上有一种更简洁的方法可以做到这一点。你基本上想扩展一个核心绑定,这可以通过使用 extend 方法来实现:

$this->app->extend('command.app.name', function ($command, $app) {
    return new AppNameCommand($app['composer'], $app['files']);
});

Jason Lewis has a really nice article regarding Laravel's IoC on Tutsplus。请务必检查一下 ;)