web-dev-qa-db-fra.com

Exécution de shell asynchrone dans PHP

J'ai un script PHP qui doit appeler un script Shell mais ne se soucie pas du tout de la sortie. Le script Shell effectue un certain nombre d'appels SOAP et est lent à terminer. Par conséquent, je ne souhaite pas ralentir la demande PHP en attendant une réponse. En fait, la requête PHP devrait pouvoir se fermer sans mettre fin au processus Shell.

J'ai examiné les différentes fonctions exec(), Shell_exec(), pcntl_fork(), etc., mais aucune ne semble offrir exactement ce que je veux. (Ou, s'ils le font, je ne vois pas comment.) Des suggestions?

189
AdamTheHutt

S'il "ne se soucie pas de la sortie", l'exécutif du script ne peut-il pas être appelé avec le & afin d'arrière-plan du processus?

EDIT - incorporant ce que @ AdamTheHut a commenté dans cet article, vous pouvez ajouter cela à un appel à exec:

" > /dev/null 2>/dev/null &"

Cela redirigera stdio (premier >) et stderr (2>) vers /dev/null et s'exécutera en arrière-plan.

Il y a d'autres façons de faire la même chose, mais c'est la plus simple à lire.


Une alternative à la double redirection ci-dessus:

" &> /dev/null &"
204
warren

J'ai utilisé at pour cela, car il s'agit vraiment d'un processus indépendant.

<?php
    `echo "the command"|at now`;
?>
52
Czimi

Sur Linux, vous pouvez effectuer les opérations suivantes:

$cmd = 'Nohup Nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = Shell_exec($cmd);

Cela exécutera la commande à l'invite de commande, puis renverra simplement le PID, que vous pouvez vérifier pour> 0 pour vous assurer qu'il a fonctionné.

Cette question est similaire: PHP a-t-il des threads?

19
Darryl Hein

Pour tous les utilisateurs Windows: j'ai trouvé un bon moyen d'exécuter un script PHP asynchrone (en réalité, il fonctionne avec presque tout).

Il est basé sur les commandes popen () et pclose (). Et fonctionne bien à la fois sur Windows et Unix.

function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
} 

Code original de: http://php.net/manual/en/function.exec.php#86329

19
LucaM

php-execute-a-background-process a de bonnes suggestions. Je pense que le mien est très bon, mais je suis partial :)

12
Mark Biek

Sous Linux, vous pouvez démarrer un processus dans un nouveau thread indépendant en ajoutant une esperluette à la fin de la commande.

mycommand -someparam somevalue &

Sous Windows, vous pouvez utiliser la commande DOS "démarrer"

start mycommand -someparam somevalue
7
Leo

la bonne façon de le faire est de 

  1. fourchette()
  2. setsid ()
  3. execve ()

fork forks, setsid indique au processus en cours de devenir un processus maître (pas de parent), execve indique au processus appelant d'être remplacé par le processus appelé. afin que le parent puisse quitter sans affecter l'enfant.

 $pid=pcntl_fork();
 if($pid==0)
 {
   posix_setsid();
   pcntl_exec($cmd,$args,$_ENV);
   // child becomes the standalone detached process
 }

 // parent's stuff
 exit();
5
pierre_moon

J'ai utilisé ça ...

/** 
 * Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.  
 * Relies on the PHP_PATH config constant.
 *
 * @param string $filename  file to execute
 * @param string $options   (optional) arguments to pass to file via the command line
 */ 
function asyncInclude($filename, $options = '') {
    exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}

(où PHP_PATH est un const défini comme define('PHP_PATH', '/opt/bin/php5') ou similaire)

Il passe en arguments via la ligne de commande. Pour les lire en PHP, voir argv .

4
philfreo

J'ai aussi trouvé Composant de processus Symfony utile pour cela.

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
// ... run process in background
$process->start();

// ... do other things

// ... if you need to wait
$process->wait();

// ... do things after the process has finished

Voyez comment cela fonctionne dans son GitHub repo .

3
Anton Pelykh

La seule façon qui a vraiment fonctionné pour moi était:

Shell_exec('./myscript.php | at now & disown')
3
Gordon Forsythe

Vous pouvez également exécuter le script PHP en tant que daemon ou cronjob : #!/usr/bin/php -q 

1
Ronald Conco

Utilisez un fifo nommé.

#!/bin/sh
mkfifo trigger
while true; do
    read < trigger
    long_running_task
done

Ensuite, chaque fois que vous souhaitez démarrer une tâche longue, écrivez simplement une nouvelle ligne (non bloquante dans le fichier de déclencheur).

Tant que votre entrée est inférieure à PIPE_BUF et qu'il s'agit d'une seule opération write(), vous pouvez écrire des arguments dans le fifo et les afficher sous la forme $REPLY dans le script.

1
geocar

sans utiliser la file d'attente, vous pouvez utiliser la proc_open() comme ceci:

    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")    //here curaengine log all the info into stderror
    );
    $command = 'ping stackoverflow.com';
    $process = proc_open($command, $descriptorspec, $pipes);
0
Kris Roofe