Cake PHP 3 - выполнение нескольких команд из 1 команды и протоколирование ошибок, если они возникают - PullRequest
0 голосов
/ 10 июля 2020

Я создаю приложение на Cake PHP 3.8, которое использует консольные команды для выполнения нескольких процессов.

Эти процессы довольно ресурсоемкие, поэтому я написал их с помощью команд потому что они могут легко истечь по тайм-ауту, если они выполняются в браузере. выполняется по порядку (Этап 1 ... Этап 5) вручную , т.е. src/Command/Stage1Command.php выполняется с:

$ php bin/cake.php stage1

Все 5 команд принимают один параметр - идентификатор - а затем выполняют некоторые работы. Это было настроено следующим образом (код в 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 - это идентификатор, который я хочу pass.

$ php bin/cake.php stage1 428

Вместо того, чтобы выполнять их вручную, я хочу достичь следующего:

  1. Создайте новую команду, которая проходит через набор идентификаторов фильтра, а затем вызывает каждую из 5 команд, передавая идентификатор.

  2. Обновить таблицу, чтобы показать результат (успех, ошибка) каждой команды.

Для (1) я создал src/Command/RunAllCommand.php, а затем использовал al oop в моей таблице фильтров, чтобы сгенерировать идентификаторы, а затем выполнить 5 команд, передав идентификатор. Скрипт выглядит так:

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

Это не работает. Выдает ошибку:

Идентификатор фильтра должен быть передан в качестве аргумента

Я могу сказать, что команды вызываются, потому что это мои собственные сообщения об ошибках от buildOptionsParser().

Это не имеет смысла, потому что строка $io->out($filter['id']) в RunAllCommand.php показывает, что идентификаторы фильтров считываются из моей базы данных. Как передать аргумент таким образом? Я слежу за документами по вызову других команд (https://book.cakephp.org/3/en/console-and-shells/commands.html#calling -другие-команды ).

Я не понимаю, как достичь (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(), чтобы записать это в базу данных, или есть другой способ, например try...catch в RunAllCommand?

Справочная информация: Идея при этом RunAllCommand.php будет выполняться через Cron . Это означает, что процессы, выполняемые каждым этапом, будут происходить через равные промежутки времени, не требуя ручного выполнения каких-либо сценариев или передачи идентификаторов вручную в качестве параметров команды.

1 Ответ

2 голосов
/ 10 июля 2020

Аргументы, отправленные «основной» команде, не передаются автоматически «вспомогательным» командам, которые вы вызываете с помощью executeCommand(), причина этого в том, что они вполне могут быть несовместимы, «основная» команда не имеет возможности узнать, какие аргументы следует или не следует передавать. Меньше всего вам нужно, чтобы подкоманда выполняла то, о чем вы ее не просили, только из-за аргумента, который использует основная команда.

Итак, вам нужно передать аргументы, которые вы хотите, чтобы ваш подкоманды для получения вручную, это будет второй аргумент \Cake\Console\BaseCommand::executeCommand(), а не конструктор команд, он вообще не принимает никаких аргументов (если вы не перезаписали базовый конструктор).

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

Обратите внимание, что массив аргументов не ассоциативен, значения передаются как записи с одним значением, точно так же, как PHP получит их в $argv переменной, ie:

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

Что касается ошибок, executeCommand() возвращает код выхода команды. Вызов $this->abort() в вашей подкоманде вызовет исключение, которое перехватывается в executeCommand(), и его код возвращается точно так же, как обычный код выхода из метода 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');
}

Если вам нужна дополнительная информация для регистрации, вы, конечно, можете войти в свои подкоманды, где эта информация доступен, или, возможно, сохранить информацию об ошибке в команде и предоставить метод для чтения этой информации, или создать исключение с подробностями об ошибке, которые ваша основная команда могла бы перехватить и оценить. Однако создание исключения не будет слишком приятным при автономном выполнении команд, поэтому вам нужно будет выяснить, какой вариант лучше всего подходит для вашего случая.

...