CakePHP 3 - 从 1 个命令执行多个命令并在发生错误时记录错误
CakePHP 3 - executing multiple Commands from 1 Command and logging errors if they occur
我正在 CakePHP 3.8 中构建一个应用程序,它使用 Console Commands 来执行多个进程。
这些进程非常占用资源,所以我用命令编写它们,因为如果在浏览器中执行它们很容易超时。
有 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。
更新一个table以显示每个命令的结果(成功、错误)。
对于 (1),我创建了 src/Command/RunAllCommand.php
,然后在我的 table 过滤器上使用循环来生成 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);
}
}
}
这行不通。它给出了一个错误:
Filter ID must be passed as an argument
我可以看出正在调用命令,因为这些是我自己来自 buildOptionsParser()
的错误消息。
这没有意义,因为 RunAllCommand.php
中的行 $io->out($filter['id'])
显示正在从我的数据库中读取过滤器 ID。你如何以这种方式传递论点?我正在关注有关调用其他命令的文档 (https://book.cakephp.org/3/en/console-and-shells/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()
在任何地方被调用,我需要将其登录到我的数据库中的另一个 table。我是否需要在 $this->abort()
之前添加代码才能将其写入数据库,或者是否有其他方式,例如try...catch
在 RunAllCommand
?
背景信息: 这个想法是 RunAllCommand.php
将通过 Cron 执行。这意味着每个阶段执行的过程将定期发生,无需手动执行任何脚本 - 或手动将 ID 作为命令参数传递。
发送到“主”命令的参数不会自动传递给您使用 executeCommand()
调用的“子”命令,原因是它们很可能不兼容, “main”命令无法知道应该或不应该传递哪些参数。你最不想要的是子命令做一些你没有要求它做的事情,只是因为主命令使用了一个参数。
所以你需要传递你希望你的子命令手动接收的参数,那将是 \Cake\Console\BaseCommand::executeCommand()
的第二个参数,而不是命令构造函数,它根本不接受任何参数 (除非你覆盖了基础构造函数)。
$this->executeCommand($stage1, [$filter['id']]);
请注意,参数数组不是关联的,值作为单值条目传递,就像 PHP 会在 $argv
变量中接收它们一样,即:
['positional argument value', '--named', 'named option value']
关于错误,executeCommand()
return命令的退出代码。在您的子命令中调用 $this->abort()
将触发一个异常,该异常在 executeCommand()
中被捕获并且其代码 return 就像您的子命令 execute()
中的正常退出代码一样方法。
因此,如果您只需要记录失败,那么您可以简单地评估 return 代码,例如:
$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');
}
如果您需要记录其他信息,那么您当然可以在该信息可用的地方记录您的子命令,或者可能在命令中存储错误信息并公开一个方法来读取该信息,或者抛出您的主命令可以捕获和评估的错误详细信息异常。但是,当 运行 命令独立时抛出异常并不会太好,因此您必须弄清楚在您的情况下最好的选择是什么。
我正在 CakePHP 3.8 中构建一个应用程序,它使用 Console Commands 来执行多个进程。
这些进程非常占用资源,所以我用命令编写它们,因为如果在浏览器中执行它们很容易超时。
有 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。
更新一个table以显示每个命令的结果(成功、错误)。
对于 (1),我创建了 src/Command/RunAllCommand.php
,然后在我的 table 过滤器上使用循环来生成 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);
}
}
}
这行不通。它给出了一个错误:
Filter ID must be passed as an argument
我可以看出正在调用命令,因为这些是我自己来自 buildOptionsParser()
的错误消息。
这没有意义,因为 RunAllCommand.php
中的行 $io->out($filter['id'])
显示正在从我的数据库中读取过滤器 ID。你如何以这种方式传递论点?我正在关注有关调用其他命令的文档 (https://book.cakephp.org/3/en/console-and-shells/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()
在任何地方被调用,我需要将其登录到我的数据库中的另一个 table。我是否需要在 $this->abort()
之前添加代码才能将其写入数据库,或者是否有其他方式,例如try...catch
在 RunAllCommand
?
背景信息: 这个想法是 RunAllCommand.php
将通过 Cron 执行。这意味着每个阶段执行的过程将定期发生,无需手动执行任何脚本 - 或手动将 ID 作为命令参数传递。
发送到“主”命令的参数不会自动传递给您使用 executeCommand()
调用的“子”命令,原因是它们很可能不兼容, “main”命令无法知道应该或不应该传递哪些参数。你最不想要的是子命令做一些你没有要求它做的事情,只是因为主命令使用了一个参数。
所以你需要传递你希望你的子命令手动接收的参数,那将是 \Cake\Console\BaseCommand::executeCommand()
的第二个参数,而不是命令构造函数,它根本不接受任何参数 (除非你覆盖了基础构造函数)。
$this->executeCommand($stage1, [$filter['id']]);
请注意,参数数组不是关联的,值作为单值条目传递,就像 PHP 会在 $argv
变量中接收它们一样,即:
['positional argument value', '--named', 'named option value']
关于错误,executeCommand()
return命令的退出代码。在您的子命令中调用 $this->abort()
将触发一个异常,该异常在 executeCommand()
中被捕获并且其代码 return 就像您的子命令 execute()
中的正常退出代码一样方法。
因此,如果您只需要记录失败,那么您可以简单地评估 return 代码,例如:
$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');
}
如果您需要记录其他信息,那么您当然可以在该信息可用的地方记录您的子命令,或者可能在命令中存储错误信息并公开一个方法来读取该信息,或者抛出您的主命令可以捕获和评估的错误详细信息异常。但是,当 运行 命令独立时抛出异常并不会太好,因此您必须弄清楚在您的情况下最好的选择是什么。