如何在Symfony命令中包装Drush并传递未解析参数
当然可以实现这个需求啦!其实不用折腾事件调度器去修改未解析的输入,Symfony Console(Drush和你的myapp大概率都是基于它开发的)本身就有现成的方案来处理这种场景,下面给你两种实用的方法:
在你的MyAppDrushCommand类里定义命令时,把接收后续内容的参数设置为贪婪模式,它会自动把drush之后的所有内容(包括空格、短选项、长选项)都打包成数组(或者你可以转成字符串)传递给命令。
示例代码:
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; class MyAppDrushCommand extends Command { protected static $defaultName = 'drush'; protected function configure() { $this ->setDescription('A wrapper for running Drush commands via myapp') // 关键配置:IS_ARRAY + REMAINDER 让参数接收所有剩余输入 ->addArgument( 'drush_args', InputArgument::IS_ARRAY | InputArgument::REMAINDER, 'All arguments, flags, and options to pass to Drush' ); } protected function execute(InputInterface $input, OutputInterface $output) { $drushArgs = $input->getArgument('drush_args'); // 直接用数组传给Process更安全,避免字符串转义问题 $process = new Process(array_merge(['drush'], $drushArgs)); $process->run(); if (!$process->isSuccessful()) { $output->write($process->getErrorOutput()); return Command::FAILURE; } $output->write($process->getOutput()); return Command::SUCCESS; } }
当用户输入myapp drush some drush arguments maybe a --flag-too时,drush_args会得到数组['some', 'drush', 'arguments', 'maybe', 'a', '--flag-too'],直接和drush合并后传给Process就行。如果确实需要完整字符串,用implode(' ', $drushArgs)就能得到你想要的内容。
如果你一定要拿到drush之后的原始未解析字符串,可以通过InputInterface的getRawInput()方法(Symfony Console 4.4及以上版本支持)来实现:
protected function execute(InputInterface $input, OutputInterface $output) { // 获取完整的命令行原始输入 $rawInput = $input->getRawInput(); // 截取"drush"之后的部分,注意去掉开头的空格 $drushCommandPart = ltrim(substr($rawInput, strpos($rawInput, 'drush') + 5)); // 用shell命令行方式运行 $process = Process::fromShellCommandline("drush $drushCommandPart"); $process->run(); // 后续的输出处理和方法一一致... }
不过这种方法要注意边界情况(比如极端场景下输入里的drush可能是某个参数的一部分,但你的命令是myapp drush,这种概率极低),而且用fromShellCommandline存在潜在的shell注入风险,所以优先推荐方法一。
额外小贴士
如果你的myapp是基于Laravel Artisan开发的,完全不用担心,Artisan底层就是Symfony Console,上面的代码只需要把命令类继承Illuminate\Console\Command就能直接用。
内容的提问来源于stack exchange,提问作者Daniel Feder

