【问题标题】:Ignore a custom Laravel Artisan command on production忽略生产中的自定义 Laravel Artisan 命令
【发布时间】:2020-05-27 14:29:13
【问题描述】:

我编写了一个自定义 Artisan 命令(我们称之为 MyDusk.php),它扩展/抽象了核心 Dusk 命令的一些功能。

此自定义命令从 Dusk 包中扩展 Laravel\Dusk\Console\DuskCommand

问题是,在生产中没有安装 Dusk 包(它在 composer.json 中的 require-dev 下)

因此,当 composer 在生产环境中生成其自动加载文件时,它会在到达 MyDusk.php 时出错,因为它找不到 Laravel\Dusk\Console\DuskCommand

PHP Fatal error:  Class 'Laravel\Dusk\Console\DuskCommand' not found in app/Console/Commands/Dusk.php on line 10

In Dusk.php line 10:

  Class 'Laravel\Dusk\Console\DuskCommand' not found  

我尝试将 Dusk 包移动到 require,这样它就可以在生产环境中使用(我知道,这并不理想),但是核心 Dusk 服务提供程序中有一行在生产环境中运行时会引发异常,从而阻止了这种情况:

# From: vendor/laravel/dusk/src/DuskServiceProvider.php
if ($this->app->environment('production')) {
    throw new Exception('It is unsafe to run Dusk in production.');
}

我正在尝试最优雅的解决方案,让我的自定义 Dusk 命令成为应用程序的一部分并在本地访问,而不会在生产环境中引发错误。

一个想法:将我的 Dusk 命令编写为自己的包,这也仅在 require-dev 中。

还有其他想法吗?

【问题讨论】:

    标签: php laravel laravel-artisan laravel-dusk


    【解决方案1】:

    我只是看了一下 API,您可以这样做:

    您可以将您的命令移至App\Console\Commmands\Local\DuskCommand.php

    默认情况下,如果您检查内核中的commands() 方法,它只会加载在App\Console\Commands 中找到的命令。这将不包括子目录。

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');
    
        require base_path('routes/console.php');
    }
    

    这是默认的commands() 方法。您可以将此实现切换为以下实现:

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $paths = [
            __DIR__ . '/Commands'
        ]; 
    
        if(app()->environment('local')) {
            $paths[] = __DIR__ . '/Commands/Local';
        }
    
        $this->load($paths);
    
        require base_path('routes/console.php');
    }
    

    因此,在本地,我们还将加载基于App\Console\Commands\Local 的命令。

    诚然,我自己并没有尝试过,但我认为它应该可以工作。

    编辑:我试了一下,它似乎工作得很好。我想,我会试着解释一下。基本上,在做了一个composer dump-autoload 之后,Laravel 正在监听这个事件并做两件事:

    "post-autoload-dump": [
        "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
        "@php artisan package:discover --ansi"
    ]
    

    第二个是尝试运行 Auto-Package Discovery 命令,这就是它会失败的地方。 Artisan 可执行文件实际上使用控制台内核启动应用程序。

    $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
    
    $status = $kernel->handle(
        $input = new Symfony\Component\Console\Input\ArgvInput,
        new Symfony\Component\Console\Output\ConsoleOutput
    );
    

    在解析内核时,它还会尝试启动它需要的命令,以便 Artisan 可以使用它们,这就是它失败的地方。

    但是,就像我上面提到的,如果您只启动生产中需要的命令,则不会发生此问题。

    【讨论】:

    • 遇到了另一个障碍:因为 MyDusk.php 正在扩展 Laravel\Dusk\Console\DuskCommand;,所以 composer 在找不到 DuskCommand 时会在生产中抛出错误。为了解决这个问题,我可以将MyDusk 移出app/ 目录,这样它就不会在生产中自动加载psr-4(并将它的新位置添加到autoload-dev)。但是,现在需要 2 个非典型步骤来完成这项工作 - 我认为在这一点上,将其制作为自己的包是有意义的 - 特别是因为我可以看到它在其他项目中很有用。
    • 我实际上在 Laravel 7+ 中发现了完全相反的行为。子目录中的命令绝对是通过控制台内核的 load() 方法自动加载的。不幸的是,我发现没有简单的方法可以选择性地忽略自动加载中的一个或多个。
    【解决方案2】:

    接受的答案似乎在 Laravel 6 中不起作用。这对我有用:

    1. 使用 php artisan make:command YourCommand 创建您的命令并将其移动到 app/Console/Local。
    2. 将其命名空间更改为 App\Console\Local。
    3. 然后,在 app/Console/Kernel.php 中:
    protected function commands()
    {
        $paths = [
            __DIR__ . '/Commands'
        ];
    
        if(app()->environment('local')) {
            $paths[] = __DIR__ . '/Local';
        }
    
        $this->load($paths);
    
        require base_path('routes/console.php');
    }
    
    1. 享受 ;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-06
      相关资源
      最近更新 更多