8
votes

Shell_Exec () Timeout Management & Exec ()

Je gère un script tiers en utilisant une classe wrapper j'ai écrit quels appels shell_exec () et des tuyaux dans un fichier i analysez plus tard à l'aide du code PHP. Je devrais mentionner que cela fonctionne, mais j'essaie d'améliorer la fonctionnalité, après avoir rencontré un cas d'utilisation que je n'avais pas pensé.

Comment est préférable de gérer le délai d'attente sur shell_exec ()? Je pensais à l'envelopper dans un essayer () () mais je ne suis pas sûr de la meilleure façon de gérer le composant temporel.

J'ai lu quelques questions ici relatif à shell_exec () et exécuté () et il semble que, en passant des paramètres de sortie sur exécuté () Vous pouvez obtenir un retour, Mais cela s'appuie sur le script terminant avec un statut de retour. De plus, dans ma page de test Mini, je ne peux pas sembler l'obtenir de renvoyer une sortie!

L'autre option que j'ai pensée à utiliser une boîte de dialogue modale, avec un spinner de style Ajax tandis que le script fonctionnait, et définir un délai d'attente manuel dans JavaScript. Qui a ensuite donné à l'utilisateur un message de dialogue de modèle à l'abour de l'échec / du délai d'attente et de la fin.

y a-t-il des méthodes acceptées pour ce cas d'utilisation?

mon mini test, consistait en ce qui suit, xxx

Utilisation de cette question comme REF, Impossible d'exécuter le script php à l'aide de php Exec < p> http://www.php.net/manual/fr/function. EXEC.PHP


0 commentaires

4 Réponses :


8
votes

Je vous suggérerais de regarder à l'aide de proc_open . Vous pouvez la configurer pour renvoyer une ressource de flux, conserver manuellement une minuterie et si la minuterie expire avant la fin du processus, vous pouvez le terminer avec proc_terminate . Si cela complète avant l'expiration de la minuterie, vous pouvez utiliser proc_close puis stream_get_contents pour saisir les données qui auraient été écrites autrement.

voir http://www.php.net/manual/fr /function.proc-open.php


2 commentaires

Merci je vais essayer ça aujourd'hui et vois comment je vais :)


J'ai aussi trouvé cette question très pratique, Stackoverflow.com/Questtions/2603912/...



19
votes

J'écris un peu de code de travail pour une telle tâche. Fonction renvoie le code de sortie (0 - OK,> 0 - Erreur) et écrit stdout, stardr sur les variables de référence.

/*execute program and write all output to $out
terminate program if it runs more than 30 seconds */
execute("program --option", null, $out, $out, 30);
echo $out;

function execute($cmd, $stdin=null, &$stdout, &$stderr, $timeout=false)
{
    $pipes = array();
    $process = proc_open(
        $cmd,
        array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
        $pipes
    );
    $start = time();
    $stdout = '';
    $stderr = '';

    if(is_resource($process))
    {
        stream_set_blocking($pipes[0], 0);
        stream_set_blocking($pipes[1], 0);
        stream_set_blocking($pipes[2], 0);
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }

    while(is_resource($process))
    {
        //echo ".";
        $stdout .= stream_get_contents($pipes[1]);
        $stderr .= stream_get_contents($pipes[2]);

        if($timeout !== false && time() - $start > $timeout)
        {
            proc_terminate($process, 9);
            return 1;
        }

        $status = proc_get_status($process);
        if(!$status['running'])
        {
            fclose($pipes[1]);
            fclose($pipes[2]);
            proc_close($process);
            return $status['exitcode'];
        }

        usleep(100000);
    }

    return 1;
}


0 commentaires

0
votes

J'ai essayé avec popen () code>, mais il n'y a aucun moyen de mettre fin à la poncse après. En outre, stream_get_contents () code> bloque le flux même lorsque vous utilisez stream_set_blocking sous Windows, je devais donc utiliser Fread à la place. De plus, proc_terminate ne fonctionne pas correctement sous Windows. J'ai donc dû utiliser une fonction de tuer alternative.

Je suis venu avec cela, il devrait fonctionner à la fois sur Windows et Linux maintenant: P>

function execute($command, $timeout = 5) {
    $handle = proc_open($command, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipe);

    $startTime = microtime(true);

    /* Read the command output and kill it if the proccess surpassed the timeout */
    while(!feof($pipe[1])) {
        $read .= fread($pipe[1], 8192);
        if($startTime + $timeout < microtime(true)) break;
    }

    kill(proc_get_status($handle)['pid']);
    proc_close($handle);

    return $read;
}

/* The proc_terminate() function doesn't end proccess properly on Windows */
function kill($pid) {
    return strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}


0 commentaires

0
votes

Si vous êtes sous Linux (ou WSL dans Windows 10), l'utilisation de la commande Timeout semble être la meilleure solution. Voir cette réponse: Exec () avec Timeout


0 commentaires