Comment puis-je lancer un script PHP en arrière-plan après avoir soumis un formulaire?

problème

J'ai un formulaire qui, une fois soumis, exécutera le code de base pour traiter l'information soumise et l'insérer dans une base de données pour affichage sur un site de notification. De plus, j'ai une liste de personnes qui se sont inscrites pour recevoir ces notifications par courriel et SMS. Cette liste est triviale comme le moment (seulement poussant environ 150), mais il est suffisant pour cause il prend plus d'une minute pour faire le cycle à travers l'ensemble table des abonnés et envoyer plus de 150 emails. (Les e-mails sont envoyés individuellement comme demandé par les administrateurs système de notre serveur de messagerie en raison des politiques de messagerie de masse.)

pendant ce temps, l'individu qui a affiché l'alerte s'assiéra sur la dernière page du formulaire pendant près d'une minute sans aucun signe positif que son avis est affiché. Cela conduit à d'autres problèmes potentiels, tout ce qui ont des solutions possibles qui je pense sont moins que idéal.

  1. tout d'abord, l'affiche peut penser que le serveur est en retard et cliquez à nouveau sur le bouton "Submit", ce qui provoque le script à recommencer ou exécuter deux fois. Je pourrais résoudre cela en utilisant JavaScript pour désactiver le bouton et remplacer le texte pour dire quelque chose comme " traitement...toutefois ce n'est pas l'idéal, car l'utilisateur sera toujours bloqué sur la page de la durée de l'exécution du script. (De plus, si JavaScript est désactivé, ce problème persiste exister.)

  2. Deuxièmement, l'affiche pourrait fermer l'onglet ou le navigateur prématurément après avoir soumis le formulaire. Le script va continuer à fonctionner sur le serveur jusqu'à ce qu'il essaie de revenir au navigateur, mais si l'utilisateur navigue vers n'importe quelle page de notre domaine (pendant que le script est encore en cours d'exécution), le navigateur suspend le chargement de la page jusqu'à ce que le script soit terminé. (Cela se produit seulement quand un onglet ou une fenêtre du navigateur est fermé et non pas la totalité application de navigateur.) Toujours, ce n'est pas l'idéal.

(Possible) Solution

J'ai décidé de faire apparaître la partie "email" du script dans un fichier séparé que je peux appeler après que la notification a été postée. J'ai d'abord pensé mettre ceci sur la page de confirmation après que la notification ait été postée avec succès. Toutefois, l'utilisateur ne saura pas ce script est en cours d'exécution et toute les anomalies ne seront pas visibles pour eux; ce script ne peut pas échouer.

mais, que faire si je peux exécuter ce script comme un processus de fond? Alors, ma question Est la suivante: Comment puis-je exécuter un script PHP pour déclencher en tant que service d'arrière-plan et l'exécuter complètement indépendamment de ce que l'Utilisateur a fait au niveau du formulaire?

EDIT: Ce ne peut pas cron ed. Il doit s'exécuter dès que le formulaire est soumis. Ce sont notifications prioritaires. De plus, les administrateurs du système qui utilisent nos serveurs interdisent aux crons de fonctionner plus souvent que 5 minutes.

87
demandé sur Michael Irigoyen 2011-01-07 18:05:20

15 réponses

faire quelques expériences avec exec et shell_exec j'ai découvert une solution qui a fonctionné parfaitement! Je choisis d'utiliser shell_exec pour pouvoir enregistrer chaque processus de notification qui se produit (ou ne se produit pas). ( shell_exec retourne comme une chaîne de caractères et c'était plus facile que d'utiliser exec , en assignant la sortie à une variable et en ouvrant un fichier pour écrire.)

j'utilise la ligne suivante pour invoquer le script email:

shell_exec("/path/to/php /path/to/send_notifications.php '".$post_id."' 'alert' >> /path/to/alert_log/paging.log &");

il est important de noter le & à la fin de la commande (comme le souligne @netcoder). Cette commande UNIX exécute un processus en arrière-plan.

les variables supplémentaires entourées de guillemets simples après le chemin du script sont définies comme des variables $_SERVER['argv'] que je peux appeler dans mon script.

le script e-mail sort alors dans mon fichier log en utilisant le >> et va sortir quelque chose comme ceci:

[2011-01-07 11:01:26] Alert Notifications Sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 38.71 seconds)
[2011-01-07 11:01:34] CRITICAL ERROR: Alert Notifications NOT sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 23.12 seconds)
75
répondu Michael Irigoyen 2013-09-24 20:47:17

sur les serveurs Linux / Unix, vous pouvez exécuter un travail en arrière-plan en utilisant proc_open :

$descriptorspec = array(
   array('pipe', 'r'),               // stdin
   array('file', 'myfile.txt', 'a'), // stdout
   array('pipe', 'w'),               // stderr
);

$proc = proc_open('php email_script.php &', $descriptorspec, $pipes);

le & étant le point important ici. Le script continuera même si le script original est terminé.

16
répondu netcoder 2011-01-07 17:19:50

PHP exec("php script.php") peut le faire.

du manuel :

si un programme est lancé avec ceci fonction dans l'ordre pour continuer cours d'exécution en arrière-plan, la sortie le programme doit être redirigé vers une fichier ou d'un autre flux de sortie. Défaillant pour ce faire, PHP sera suspendu jusqu'à ce que l'exécution du programme se termine.

donc si vous redirigez le sortie vers un fichier journal (ce qui est une bonne idée de toute façon), votre script d'appel ne sera pas hang et votre script e-mail fonctionnera en bg.

6
répondu Simon 2011-01-07 15:13:13

et pourquoi ne pas faire une requête HTTP sur le script et ignorer la réponse ?

http://php.net/manual/en/function.httprequest-send.php

Si vous faites votre demande sur le script, vous devez appeler votre serveur va exécuter en arrière-plan et vous pouvez (dans votre script principal) affiche un message indiquant à l'utilisateur que le script est en cours d'exécution.

5
répondu Twister 2011-01-07 16:32:53

et ça?

  1. votre script PHP qui contient le formulaire sauve un drapeau ou une valeur dans une base de données ou un fichier.
  2. un second script PHP recherche cette valeur périodiquement et si elle a été définie, elle déclenche le script Email de manière synchrone.

ce second script PHP doit être configuré pour fonctionner comme un cron.

4
répondu adarshr 2011-01-07 15:09:33

comme je sais que vous ne pouvez pas le faire de manière facile (voir fork exec etc (ne fonctionne pas sous windows)), peut-être que vous pouvez inverser l'approche, utiliser l'arrière-plan du navigateur affichant le formulaire en ajax, donc si le post fonctionne toujours, vous n'avez pas de temps d'attente.

Cela peut aider, même si vous devez faire une longue élaboration.



À propos de l'envoi de courrier il est toujours conseillé d'utiliser un spooler, peut-être un serveur smtp local et rapide qui accepte votre les requêtes et le spool them à la MTA réelle ou mettre tout dans un DB, que d'utiliser une cron qui spool la file d'attente.

Le cron peut être sur une autre machine appelant le spooler comme url externe:

* * * * * wget -O /dev/null http://www.example.com/spooler.php
4
répondu Ivan Buttinoni 2011-01-07 15:25:08

la façon la plus simple d'exécuter un script PHP en arrière-plan est

php script.php >/dev/null &

le script s'exécute en arrière-plan et la page atteint également la page d'action plus rapidement.

4
répondu Sandeep Dhanda 2018-02-19 20:16:07

tâche de Fond sonne comme une bonne idée de ce.

vous aurez besoin d'un accès ssh à la machine pour exécuter le script comme un cron.

$ php scriptname.php pour l'exécuter.

3
répondu Ian 2011-01-07 15:08:29

si vous pouvez accéder au serveur sur ssh et exécuter vos propres scripts, vous pouvez faire un serveur simple fifo en utilisant php (bien que vous devrez recompiler php avec posix support pour fork ).

le serveur peut être écrit dans n'importe quoi vraiment, vous pouvez probablement le faire facilement en python.

ou la solution la plus simple serait d'envoyer une HttpRequest et de ne pas lire les données de retour mais le serveur pourrait détruisez le script avant qu'il ne finisse le traitement.

exemple de serveur:

<?php
define('FIFO_PATH', '/home/user/input.queue');
define('FORK_COUNT', 10);

if(file_exists(FIFO_PATH)) {
    die(FIFO_PATH . ' exists, please delete it and try again.' . "\n");
}

if(!file_exists(FIFO_PATH) && !posix_mkfifo(FIFO_PATH, 0666)){
    die('Couldn\'t create the listening fifo.' . "\n");
}

$pids = array();
$fp = fopen(FIFO_PATH, 'r+');
for($i = 0; $i < FORK_COUNT; ++$i) {
    $pids[$i] = pcntl_fork();
    if(!$pids[$i]) {
        echo "process(" . posix_getpid() . ", id=$i)\n";
        while(true) {
            $line = chop(fgets($fp));
            if($line == 'quit' || $line === false) break;
            echo "processing (" . posix_getpid() . ", id=$i) :: $line\n";
        //  $data = json_decode($line);
        //  processData($data);
        }
        exit();
    }
}
fclose($fp);
foreach($pids as $pid){
    pcntl_waitpid($pid, $status);
}
unlink(FIFO_PATH);
?>

exemple de client:

<?php
define('FIFO_PATH', '/home/user/input.queue');
if(!file_exists(FIFO_PATH)) {
    die(FIFO_PATH . ' doesn\'t exist, please make sure the fifo server is running.' . "\n");
}

function postToQueue($data) {
    $fp = fopen(FIFO_PATH, 'w+');
    stream_set_blocking($fp, false); //don't block
    $data = json_encode($data) . "\n";
    if(fwrite($fp, $data) != strlen($data)) {
        echo "Couldn't the server might be dead or there's a bug somewhere\n";
    }
    fclose($fp);
}
$i = 1000;
while(--$i) {
    postToQueue(array('xx'=>21, 'yy' => array(1,2,3)));
}
?>
3
répondu OneOfOne 2011-01-07 16:26:13

si vous utilisez une plate-forme *nix, utilisez cron et l'exécutable php .

EDIT:

il y a un certain nombre de questions demandant de" lancer php sans cron " déjà. En voici un:

la Planification des scripts sans l'aide de CRON

cela dit, la méthode exec() de la réponse ci-dessus semble très prometteur :)

2
répondu Spiny Norman 2017-05-23 12:17:39

si vous êtes sur Windows, cherchez proc_open ou popen ...

mais si nous sommes sur le même serveur "Linux" avec cpanel, alors c'est la bonne approche:

#!/usr/bin/php 
<?php
$pid = shell_exec("nohup nice php -f            
'path/to/your/script.php' /dev/null 2>&1 & echo $!");
While(exec("ps $pid"))
{ //you can also have a streamer here like fprintf,        
 // or fgets
}
?>

Ne pas utiliser fork() ou curl si vous avez un doute, vous pouvez les manipuler, c'est juste comme d'abuser de votre serveur

enfin, sur le fichier script.php qui est appelé ci-dessus, prenez note de ceci assurez-vous d'avoir écrit:

<?php
ignore_user_abort(TRUE);
set_time_limit(0);
ob_start();
// <-- really optional but this is pure php

//Code to be tested on background

ob_flush(); flush(); 
//this two do the output process if you need some.        
//then to make all the logic possible


str_repeat(" ",1500); 
//.for progress bars or loading images

sleep(2); //standard limit

?>
2
répondu i am ArbZ 2014-05-09 20:16:32

de toutes les réponses, aucune n'a considéré la fonction ridiculement facile fastcgi_finish_request , qui, lorsqu'elle est appelée, renvoie toutes les sorties restantes au navigateur et ferme la session Fastcgi et la connexion HTTP, tout en laissant le script s'exécuter en arrière-plan.

un exemple:

<?php
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
fastcgi_finish_request(); // The user is now disconnected from the script

// do stuff with received data,
2
répondu Danogentili 2017-06-01 11:02:04

dans mon cas, j'ai trois params, l'un d'eux est string (mensaje):

exec("C:\wamp\bin\php\php5.5.12\php.exe C:/test/N/trunk/api/v1/Process.php $idTest2 $idTest3 \"$mensaje\" >> c:/log.log &");

dans mon processus.php j'ai ce code:

if (!isset($argv[1]) || !isset($argv[2]) || !isset($argv[3]))
{   
    die("Error.");
} 

$idCurso = $argv[1];
$idDestino = $argv[2];
$mensaje = $argv[3];
0
répondu Hernaldo Gonzalez 2015-06-23 18:58:30

pour le fond travailleur, je pense que vous devriez essayer cette technique, il aidera à appeler autant de pages que vous le souhaitez toutes les pages s'exécuter à la fois de manière indépendante, sans attente pour chaque page de réponse asynchrone.

form_action_page.php

     <?php

    post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue");
    //post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue2");
    //post_async("http://localhost/projectname/otherpage.php", "Keywordname=anyValue");
    //call as many as pages you like all pages will run at once //independently without waiting for each page response as asynchronous.

  //your form db insertion or other code goes here do what ever you want //above code will work as background job this line will direct hit before //above lines response     
                ?>
                <?php

                /*
                 * Executes a PHP page asynchronously so the current page does not have to wait for it to     finish running.
                 *  
                 */
                function post_async($url,$params)
                {

                    $post_string = $params;

                    $parts=parse_url($url);

                    $fp = fsockopen($parts['host'],
                        isset($parts['port'])?$parts['port']:80,
                        $errno, $errstr, 30);

                    $out = "GET ".$parts['path']."?$post_string"." HTTP/1.1\r\n";//you can use POST instead of GET if you like
                    $out.= "Host: ".$parts['host']."\r\n";
                    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
                    $out.= "Content-Length: ".strlen($post_string)."\r\n";
                    $out.= "Connection: Close\r\n\r\n";
                    fwrite($fp, $out);
                    fclose($fp);
                }
                ?>

page test.php

    <?
    echo $_REQUEST["Keywordname"];//case1 Output > testValue
//here do your background operations it will not halt main page
    ?>

PS:Si vous voulez envoyer des paramètres d'url en boucle, alors suivez cette réponse : https://stackoverflow.com/a/41225209/6295712

0
répondu Hassan Saeed 2017-05-23 12:02:13

ça marche pour moi. ce

exec(“php asyn.php”.” > /dev/null 2>/dev/null &“);
0
répondu Raj 2018-08-25 10:22:04