Je me demande comment puis-je exécuter Symfony 2 command à partir d'une requête du navigateur ou d'un contrôleur.
C'est parce que je n'ai aucune possibilité sur l'hébergement de l'exécuter et que toutes les tâches cron sont configurées par l'administrateur.
Je n'ai même pas activé la fonction exec()
. Par conséquent, lorsque je souhaite la tester, je dois copier tout le contenu de la commande sur un contrôleur de test, ce qui n'est pas la meilleure solution.
Voir documentation officielle sur ce numéro pour les versions les plus récentes de Symfony
Vous n'avez pas besoin de services d'exécution de commande à partir du contrôleur et, je pense, il est préférable d'appeler commande via la méthode run
et non via la chaîne d'entrée de la console, cependant documentation officielle vous suggère d'appeler la commande via son alias. Voir aussi cette réponse . Testé sur Symfony 2.1-2.6.
Votre classe de commande doit être étendue 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);
}
}
Dans la plupart des cas, vous n'avez pas besoin de BufferedOutput
(de la réponse de Jbm) et il suffit de vérifier que $resultCode is 0
, sinon une erreur s'est produite.
Enregistrez votre commande en tant que service et n'oubliez pas d'appeler setContainer
MyCommandService:
class: MyBundle\Command\MyCommand
calls:
- [setContainer, ["@service_container"] ]
Dans votre contrôleur, vous devrez simplement obtenir ce service et appeler la méthode execute avec les arguments de droits.
Définissez l'entrée avec la méthode setArgument
:
$input = new Symfony\Component\Console\Input\ArgvInput();
$input->setArgument('arg1', 'value');
$output = new Symfony\Component\Console\Output\ConsoleOutput();
Appelez la méthode run
de la commande:
$command = $this->get('MyCommandService');
$command->run($input, $ouput);
Dans mon environnement (Symony 2.1), je devais apporter quelques modifications à la solution @Reuven pour que cela fonctionne. Les voici:
Définition du service - pas de changement.
Dans le contrôleur:
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);
}
Vous pouvez simplement créer une instance de votre commande et l'exécuter:
/**
* @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();
}
Voici une alternative qui vous permet d’exécuter des commandes sous forme de chaînes de la même façon que sur la console (il n’est pas nécessaire de définir des services avec celle-ci).
Vous pouvez vérifier le contrôleur de cet ensemble pour voir comment cela fonctionne avec tous les détails. Je vais résumer ici certains détails (comme la gestion de l'environnement, de sorte que toutes les commandes s'exécutent dans le même environnement où elles sont appelées).
Si vous voulez seulement exécuter des commandes à partir du navigateur, vous pouvez utiliser cet ensemble tel quel, mais si vous voulez exécuter des commandes à partir d'un contrôleur arbitraire, voici comment procéder:
Dans votre contrôleur, définissez une fonction comme celle-ci:
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;
}
Ensuite, vous pouvez l'invoquer à partir d'une action comme celle-ci:
public function dumpassetsAction()
{
$output = $this->execute('assetic:dump');
return new Response($output);
}
En outre, vous devez définir une classe agissant en tant que tampon de sortie, car aucune structure n'est fournie par la structure:
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;
}
}
identique à @malloc mais
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);
}
Si vous devez passer des arguments (et/ou des options), vous devez d'abord spécifier InputDefinition avant d'instancier un objet d'entrée.
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();
En guise de remarque, si vous utilisez si vous utilisez getContainer () dans votre commande, la fonction suivante peut être pratique pour votre commande.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();
}
Vous pouvez utiliser cet ensemble pour exécuter des commandes Symfony2 à partir du contrôleur (requête http) et transmettre des options/paramètres dans une URL.
Si vous exécutez une commande nécessitant l'option env
telle que assetic:dump
$stdout->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env')));
Vous devez créer un Symfony\Component\Console\Application
et définir la définition comme suit:
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);
Vous ne pouvez pas définir l'option env
à la commande car elle ne figure pas dans sa définition.