Как запустить команду запуска Symfony 2 с контроллера
Мне интересно, как я могу запустить команду Symfony 2 из запроса браузера или из контроллера.
Это потому, что у меня нет возможности на хостинг для его запуска, и каждое задание cron настраивается администратором.
Я даже не включил функцию exec()
, поэтому, когда я хочу ее протестировать, я должен скопировать весь контент из команды в какой-либо контроллер тестирования, и это не лучшее решение.
Ответы
Ответ 1
Вам не нужны службы для выполнения команд с контроллера и, я думаю, лучше вызвать команду с помощью метода run
, а не через ввод строки в консоли, однако официальные документы предлагают вам вызвать команду через его псевдоним. Также см. этот ответ. Протестировано на 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
Зарегистрируйте свою команду как услугу и не забудьте позвонить setContainer
MyCommandService:
class: MyBundle\Command\MyCommand
calls:
- [setContainer, ["@service_container"] ]
В вашем контроллере вам просто нужно получить эту службу и вызвать метод execute с аргументами прав
Задайте вход с помощью метода 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, $ouput);
Ответ 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
Здесь альтернатива, позволяющая выполнять команды как строки так же, как и на консоли (нет необходимости в определении сервисов с этим).
Вы можете проверить этот контроллер пакетов, чтобы узнать, как это делается со всеми подробностями. Здесь я собираюсь обобщить его на некоторые детали (например, обработку среды, поэтому все команды будут запускаться в той же среде, в которой они вызывают).
Если вы хотите просто запускать команды из браузера, вы можете использовать этот пакет как есть, но если вы хотите запускать команды с произвольного контроллера, вот как это сделать:
В вашем контроллере определите такую функцию:
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;
}
}
Ответ 5
то же, что и @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);
}
Ответ 6
Вы можете просто создать экземпляр своей команды и запустить ее:
/**
* @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();
}
Ответ 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 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
Вы можете использовать этот пакет для запуска команд Symfony2 от контроллера (http-запрос) и параметров/параметров передачи в 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
для команды, потому что она не находится в ее определении.