如何从控制器运行 symfony 2 运行命令
Posted
技术标签:
【中文标题】如何从控制器运行 symfony 2 运行命令【英文标题】:How can I run symfony 2 run command from controller 【发布时间】:2012-05-08 11:18:24 【问题描述】:我想知道如何从浏览器查询或控制器运行 Symfony 2 命令。
这是因为我没有任何可能在托管上运行它,并且每个 cron 作业都是由管理员设置的。
我什至没有启用exec()
功能,所以当我想测试它时,我必须将命令中的所有内容复制到某个测试控制器,这不是最佳解决方案。
【问题讨论】:
【参考方案1】:在这个问题上查看 official documentation 以获得较新版本的 Symfony
您不需要从控制器执行命令的服务,我认为最好通过run
方法而不是通过控制台字符串输入调用命令,但是official docs 建议您通过它的别名调用命令。另请参阅this answer。在 Symfony 2.1-2.6 上测试。
您的命令类必须扩展 ContainerAwareCommand
// Your command
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
class MyCommand extends ContainerAwareCommand
// …
// Your controller
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
class SomeController extends Controller
// …
public function myAction()
$command = new MyCommand();
$command->setContainer($this->container);
$input = new ArrayInput(array('some-param' => 10, '--some-option' => true));
$output = new NullOutput();
$resultCode = $command->run($input, $output);
在大多数情况下,您不需要BufferedOutput
(来自Jbm的答案),检查$resultCode is 0
就足够了,否则会出错。
【讨论】:
我在 2.4 中没有setContainer
方法
您还需要将执行的命令名称作为ArrayInput
的第一个元素(至少在Symfony2.6 上)。
如果命令很耗时,您可能需要增加执行时间以避免超时:ini_set('max_execution_time', 600);
也是如此。
这感觉完全过于复杂了 Symfony 强迫开发人员在这里做的事情......我的意思是认真的 :)【参考方案2】:
将您的命令注册为服务,不要忘记致电setContainer
MyCommandService:
class: MyBundle\Command\MyCommand
calls:
- [setContainer, ["@service_container"] ]
在您的控制器中,您只需获取此服务,并使用权限参数调用执行方法
用setArgument
方法设置输入:
$input = new Symfony\Component\Console\Input\ArgvInput([]);
$input->setArgument('arg1', 'value');
$output = new Symfony\Component\Console\Output\ConsoleOutput();
调用命令的run
方法:
$command = $this->get('MyCommandService');
$command->run($input, $output);
【讨论】:
$output var 用于捕获控制台回复?谢谢 是的,有writeLine
的方法可以在控制台写一行
谢谢,这很有帮助!尽管如此,我在服务中包装教义:generate:entity 命令时遇到了一些问题。因为: 1) 命令的 HelperSet 为空。 2) isInteractive 为真。解决方案:1)在我的服务构造函数中,我实例化了一个应用程序并将它的 helperSet 传递给命令:$application = new Symfony\Component\Console\Application();
$this->doctrineGenEntityCmd->setHelperSet($application->getHelperSet());
2)$input->setInteractive(false)
【参考方案3】:
在我的环境(Symony 2.1)中,我必须对@Reuven 解决方案进行一些修改才能使其正常工作。他们在这里:
服务定义 - 没有变化。
在控制器中:
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
...
public function myAction()
$command = $this->get('MyCommandService');
$input = new ArgvInput(array('arg1'=> 'value'));
$output = new ConsoleOutput();
$command->run($input, $output);
【讨论】:
【参考方案4】:您可以简单地创建一个命令实例并运行它:
/**
* @Route("/run-command")
*/
public function someAction()
// Running the command
$command = new YourCommand();
$command->setContainer($this->container);
$input = new ArrayInput(['--your_argument' => true]);
$output = new ConsoleOutput();
$command->run($input, $output);
return new Response();
【讨论】:
【参考方案5】:这是一种替代方法,可让您以与在控制台上相同的方式将命令作为字符串执行(无需使用此定义服务)。
您可以查看this bundle's controller 以了解所有详细信息是如何完成的。在这里,我将对其进行总结,省略某些细节(例如处理环境,因此这里所有命令都将在它们被调用的同一环境中运行)。
如果你只想从浏览器运行命令,你可以直接使用那个包,但是如果你想从任意控制器运行命令,这里是如何做到的:
在你的控制器中定义一个这样的函数:
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\StringInput;
private function execute($command)
$app = new Application($this->get('kernel'));
$app->setAutoExit(false);
$input = new StringInput($command);
$output = new BufferedOutput();
$error = $app->run($input, $output);
if($error != 0)
$msg = "Error: $error";
else
$msg = $output->getBuffer();
return $msg;
然后你可以从这样的动作中调用它:
public function dumpassetsAction()
$output = $this->execute('assetic:dump');
return new Response($output);
另外,你需要定义一个类作为输出缓冲区,因为框架没有提供:
use Symfony\Component\Console\Output\Output;
class BufferedOutput extends Output
public function doWrite($message, $newline)
$this->buffer .= $message. ($newline? php_EOL: '');
public function getBuffer()
return $this->buffer;
【讨论】:
在 LiipFunctionalTestBundle 中还有一个相当不错的实现,它使用 php://temp 流和内置的 StreamOutput 类来捕获输出,因此您不需要自己创建第二个输出缓冲区类. github.com/liip/LiipFunctionalTestBundle/blob/master/Test/… 请注意,BufferedOutput
从 Symfony 2.4 开始可用。【参考方案6】:
与@malloc 相同 但是
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
...
public function myAction()
$command = $this->get('MyCommandService');
// $input[0] : command name
// $input[1] : argument1
$input = new ArgvInput(array('my:command', 'arg1'));
$output = new ConsoleOutput();
$command->run($input, $output);
【讨论】:
【参考方案7】:如果您必须传递参数(和/或选项),那么在 v2.0.12 中(以后版本可能是这样),您需要在实例化输入对象之前先指定 InputDefinition。
use // you will need the following
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputDefinition,
Symfony\Component\Console\Input\ArgvInput,
Symfony\Component\Console\Output\NullOutput;
// tell symfony what to expect in the input
$inputDefinition = new InputDefinition(array(
new InputArgument('myArg1', InputArgument::REQUIRED),
new InputArgument('myArg2', InputArgument::REQUIRED),
new InputOption('debug', '0', InputOption::VALUE_OPTIONAL),
));
// then pass the values for arguments to constructor, however make sure
// first param is dummy value (there is an array_shift() in ArgvInput's constructor)
$input = new ArgvInput(
array(
'dummySoInputValidates' => 'dummy',
'myArg2' => 'myValue1',
'myArg2' => 'myValue2'),
$inputDefinition);
$output = new NullOutput();
附带说明一下,如果您在命令中使用 getContainer(),则以下函数可能对您的 command.php 很方便:
/**
* Inject a dependency injection container, this is used when using the
* command as a service
*
*/
function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
$this->container = $container;
/**
* Since we are using command as a service, getContainer() is not available
* hence we need to pass the container (via services.yml) and use this function to switch
* between conatiners..
*
*/
public function getcontainer()
if (is_object($this->container))
return $this->container;
return parent::getcontainer();
【讨论】:
【参考方案8】:您可以使用此捆绑包从控制器(http 请求)运行 Symfony2 命令并在 URL 中传递选项/参数。
https://github.com/mrafalko/CommandRunnerBundle
【讨论】:
【参考方案9】:如果您运行需要env
选项的命令,例如assetic:dump
$stdout->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env')));
您必须创建一个Symfony\Component\Console\Application
并像这样设置定义:
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\NullOuput;
// Create and run the command of assetic
$app = new Application();
$app->setDefinition(new InputDefinition([
new InputOption('env', '', InputOption::VALUE_OPTIONAL, '', 'prod')
]));
$app->add(new DumpCommand());
/** @var DumpCommand $command */
$command = $app->find('assetic:dump');
$command->setContainer($this->container);
$input = new ArgvInput([
'command' => 'assetic:dump',
'write_to' => $this->assetsDir
]);
$output = new NullOutput();
$command->run($input, $output);
您无法将选项 env
设置为命令,因为它不在其定义中。
【讨论】:
Silex 应用扩展了 Pimple Container 并实现了 HttpKernelInterface、TerminableInterface,并且没有setDefinition
方法。以上是关于如何从控制器运行 symfony 2 运行命令的主要内容,如果未能解决你的问题,请参考以下文章
运行命令doctrine:schema:update symfony时出错