提问者:小点点

CakePHP 3-从1命令中执行多个命令并记录错误(如果发生错误)


我正在用CakePHP3.8构建一个应用程序,它使用控制台命令来执行几个进程。

这些进程非常耗费资源,所以我用命令编写它们,因为如果在浏览器中执行,它们很容易超时。

有5个不同的脚本执行不同的任务:src/command/stage1command.php,...src/command/stage5command.php

脚本按顺序执行(阶段1.。。阶段5)手动,即src/command/stage1command.php通过以下方式执行:

$ php bin/cake.php stage1

所有5个命令都接受一个参数--一个ID--然后执行一些工作。 这设置如下(BuildOptionSparser()中的代码存在于每个命令中):

class Stage1Command extends Command
{
    protected function buildOptionParser(ConsoleOptionParser $parser)
    {
        $parser->addArgument('filter_id', [
            'help' => 'Filter ID must be passed as an argument',
            'required' => true
        ]);
        return $parser;
    }
}

所以我可以如下执行“阶段1”,假设428是我要传递的ID。

$ php bin/cake.php stage1 428

我希望实现以下目标,而不是手动执行这些目标:

>

  • 创建一个新命令,该命令循环遍历一组筛选器ID,然后调用5个命令中的每一个,传递ID。

    更新一个表以显示每个命令的结果(成功,错误)。

    对于(1),我创建了src/command/runallcommand.php,然后在我的过滤器表上使用一个循环来生成ID,然后执行5个命令,传递ID。 脚本如下所示:

    namespace App\Command;
    use Cake\ORM\TableRegistry;
    // ...
    
    class RunAllCommand extends Command
    {
        
        public function execute(Arguments $args, ConsoleIo $io)
        {
            $FiltersTable = TableRegistry::getTableLocator()->get('Filters');
    
            $all_filters = $FiltersTable->find()->toArray();
    
            foreach ($all_filters as $k => $filter) {
                $io->out($filter['id']);
            
                // execute Stage1Command.php        
                $command = new Stage1Command(['filter_id' => $filter['id']]);
                $this->executeCommand($command);
         
                // ...
    
                // execute Stage5Command.php
                $command5 = new Stage5Command(['filter_id' => $filter['id']]);
                $this->executeCommand($command5);
            }
        }
    }
    

    这不管用。 它会给出一个错误:

    筛选器ID必须作为参数传递

    我可以判断这些命令正在被调用,因为这些是我自己来自buildoptionsparser()的错误消息。

    这是没有意义的,因为runallcommand.php中的$io->out($filter['id'])行显示正在从我的数据库中读取过滤器ID。 如何以这种方式传递一个参数? 我正在阅读有关调用其他命令的文档(https://book.cakephp.org/3/en/console-and-shell/Commands.html#calling-other-commands)。

    我不明白如何实现(2)。 在每个命令中,我都添加了如下代码,当发生错误时,该错误会停止执行该命令的其余部分。 例如,如果它在stage1command中执行,它应该中止并移到stage2command:

    // e.g. this code can be anywhere in execute() in any of the 5 commands where an error occurs.
    $io->error('error message');
    $this->abort();
    

    如果在任何地方调用$this->abort(),我需要将其记录到数据库中的另一个表中。 我是否需要在$this->abort()之前添加代码才能将其写入数据库,或者是否有其他方法,例如runallcommand中的try...catch

    背景信息:这样做的想法是,runallcommand.php将通过cron执行。 这意味着每个阶段执行的过程将定期进行,而不需要手动执行任何脚本,也不需要手动将ID作为命令参数传递。


  • 共1个答案

    匿名用户

    发送到“main”命令的参数不会自动传递给您用executeCommand()调用的“sub”命令,原因是它们很可能不兼容,“main”命令无法知道哪些参数应该传递或不应该传递。 您最不希望的是一个sub命令做一些您没有要求它做的事情,仅仅是因为main命令使用了一个参数。

    因此,您需要传递希望您的sub命令手动接收的参数,这将是\cake\console\baseCommand::executeCommand()的第二个参数,而不是命令构造函数,它根本不需要任何参数(除非您覆盖了基构造函数)。

    $this->executeCommand($stage1, [$filter['id']]);
    

    注意,参数数组不是关联的,这些值作为单值条目传递,就像PHP在$argv变量中接收它们一样,即:

    ['positional argument value', '--named', 'named option value']
    

    关于错误,executeCommand()返回命令的退出代码。 在sub命令中调用$this->abort()将触发异常,该异常将在executeCommand()中捕获,并返回其代码,就像从sub命令的execute()方法返回的正常退出代码一样。

    因此,如果您只需要记录一个失败,那么您可以简单地计算返回代码,如:

    $result = $this->executeCommand($stage1, [$filter['id']]);
    // assuming your sub commands do always return a code, and do not 
    // rely on `null` (ie no return value) being treated as success too
    if ($result !== static::CODE_SUCCESS) {
        $this->log('Stage 1 failed');
    }
    

    如果您需要记录其他信息,那么您当然可以在子命令内部记录这些信息,或者抛出一个包含错误细节的异常,主命令可以捕获和评估这些错误细节。 但是,当单独运行命令时,抛出异常并不是太好,因此您必须考虑在您的情况下什么是最好的选项。