Symfony Process permet d’exécuter des scripts python, shell, exécuter des commandes etc… Ce qui permet d’ajouter une corde à votre arc lorsque vous développez votre application web.
Installation
Pour installer le composant Process rien de plus simple :
composer require symfony/process
Utilisation
Pour cet article on va prendre exemple, un script shell qui me permet d’afficher “Hello World” sur mon terminal (l’idée est géniale, j’en ai conscience). Ce script shell il est dans mon dossier public/helloworld.sh (avec les droits d’exécution).
// public/helloworld.sh #!/bin/bash echo "Hello World"
Utilisons le composant dans un controller :
// src/Controller/HelloWorldController.php namespace App\Controller; // ... use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; class HelloWorldController extends AbstractController { public function hello(Request $request) { $pathToHelloWorldScript = "/.../myproject/public/helloworld.sh"; $process = new Process(['sh', $pathToHelloWorldScript]); $process->run(); if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } // Cela affichera "Hello World" echo $process->getOutput(); // ... } }
Lorsqu’on utilise le composant Process, il va créer un sous processus qui va lancer votre commande, ce sous processus va être autonome mais sans la fonction isSuccessful(), la fonction getOutput() aurait retourner null ou notre valeur.
Utilisons maintenant dans une commande, désormais le script, on va lui ajouter quelques détails qu’il permettra de trier le retour de la commande.
Info : Un article est disponible concernant les commandes : Lien
// src/Command/HelloCommand.php namespace App\Command; // ... use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; class HelloCommand extends Command { protected static $defaultName = "app:hello"; protected function configure() { //... } protected function execute(InputInterface $input, OutputInterface $output) { $process = new Process(['ls', '-lsa']); $process->start(); foreach ($process as $type => $data) { if ($process::OUT === $type) { echo "\nDebug :".$data; } else { // $process::ERR === $type echo "\nErreur : ".$data; } } // Depuis Symfony 5.2, vous pouvez utiliser directement la variable Command::SUCCESS return 0; } }
En utilisant cette fonction $process->start(), cela lancera en tâche de fond la commande que vous avez faite (pour ma part ls -la) et poursuivre votre commande.
Tant que le process sera en runtime (actif), il restera dans le foreach. Il affichera les retours de votre process avec un tri par le type ($type).
Poussons le vice
Désormais, nous allons plus loin. Notre composant Process a une fonction qui s’appelle waitUntil, une fonction qui peut exécuter une fonction anonyme qui permet de rester dans le process tant qu’il n’est pas true.
Nous allons crée un nouveau script bash simple comme ci-dessous :
// public/helloworld.sh #!/bin/bash echo "Begin Processus" i=0 while [ $i -le 30 ] do let "i+=1" done echo "The Script is finish."
// src/Controller/HelloWorldController.php namespace App\Controller; // ... use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; class HelloWorldController extends AbstractController { public function hello(Request $request) { $pathToHelloWorldScript = "/.../myproject/public/helloworld.sh"; $process = new Process(['sh', $pathToHelloWorldScript]); $process->start(); $process->waitUntil(function ($type, $output) { // Tant que ce n'est pas true, je reste dans cette fonction anonyme. return $output === 'The Script is finish.'; }); // ... } }
Cependant, si on voudrait stop le process, une fonction le permet : $process->stop()
// src/Command/HelloCommand.php namespace App\Command; // ... use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; class HelloCommand extends Command { protected static $defaultName = "app:hello"; protected function configure() { //... } protected function execute(InputInterface $input, OutputInterface $output) { $process = new Process(['ls', '-lsa']); $process->start(); // ... $process->stop(3, SIGINT); return 0; } }
Désormais, vous pouvez exécuter des sous process avec le composant Process de Symfony. Cela dépanne bien lorsqu’il faut automatiser avec des scripts pythons ou autre.
N’hésitez pas à suivre la documentation officielle de Symfony, pour plus d’information.
Bonjour,
Merci pour cet article !
Une question, est-ce possible de lancer par exemple une commande Symfony personnalisée, disons le traitement de l’envoi d’une file d’attente d’email (process qui peut être très long) depuis l’interface web de notre site (utilisateur apache donc), tout en ayant les contraintes de mémoire et de timeout du php.ini de la cli ?
L’idée serait qu’un utilisateur web puisse lancer une commande longue en arrière-plan, et que celle-ci rende la main en continuant sa tâche jusqu’à ce qu’elle soit finie.
Bonjour Thomas,
Oui tout à fait comme le dernier exemple, vous pouvez mettre $process->start() sans utiliser la fonction waitUntil() (qui lui permet de faire rester dans votre process) et laisser le process faire sa vie.
J’espère avoir répondu à ta question 😀
Bonjour,
Peut-on exécuter une commande de symfony dans notre cron par exemple doctrine:schema:update ?
Bonjour Paul,
Oui tu le peux :D.