sirve - php ejecuta un proceso en segundo plano
php tutorial (17)
Necesito ejecutar una copia de directorio en una acción de usuario, pero los directorios son bastante grandes, por lo que me gustaría poder realizar dicha acción sin que el usuario se dé cuenta del tiempo que tarda la copia en completarse.
Cualquier sugerencia sería muy apreciada.
¿Puede organizar un proceso separado y luego ejecutar su copia en segundo plano? Ha pasado un tiempo desde que hice PHP, pero la función pcntl-fork parece prometedora.
Aquí hay una función para iniciar un proceso en segundo plano en PHP. Finalmente, creó uno que también funciona en Windows, después de leer y probar diferentes enfoques y parámetros.
function LaunchBackgroundProcess($command){
// Run command Asynchroniously (in a separate thread)
if(PHP_OS==''WINNT'' || PHP_OS==''WIN32'' || PHP_OS==''Windows''){
// Windows
$command = ''start "" ''. $command;
} else {
// Linux/UNIX
$command = $command .'' /dev/null &'';
}
$handle = popen($command, ''r'');
if($handle!==false){
pclose($handle);
return true;
} else {
return false;
}
}
Nota 1: En Windows, no use el parámetro /B
como se sugiere en otro lugar. Obliga al proceso a ejecutar la misma ventana de consola que el comando de start
sí, lo que hace que el proceso se procese de forma síncrona. Para ejecutar el proceso en un subproceso separado (asincrónicamente), no use /B
Nota 2: Las comillas dobles vacías después del start ""
son necesarias si el comando es una ruta entre comillas. start
comando de start
interpreta el primer parámetro entre comillas como título de ventana.
Bueno, encontré una versión un poco más rápida y más fácil de usar
shell_exec(''screen -dmS $name_of_screen $command'');
y funciona.
En lugar de iniciar un proceso en segundo plano, ¿qué hay de crear un archivo de activación y tener un programador como cron o autosys que ejecuta periódicamente un script que busca y actúa sobre los archivos de activación? Los activadores pueden contener instrucciones o incluso comandos sin procesar (mejor aún, simplemente convertirlo en un script de shell).
Es posible que desee intentar adjuntar esto a su comando
>/dev/null 2>/dev/null &
p.ej.
shell_exec(''service named reload >/dev/null 2>/dev/null &'');
Escriba el proceso como una secuencia de comandos del lado del servidor en cualquier idioma (php / bash / perl / etc) y luego llámelo desde las funciones de control de procesos en su secuencia de comandos php.
La función probablemente detecta si se utiliza io estándar como el flujo de salida y si es así, se establecerá el valor de retorno ... si no termina
Proc_Close (Proc_Open ("./command --foo=1 &", Array (), $foo));
Probé esto rápidamente desde la línea de comandos usando "sleep 25s" como comando y funcionó a la perfección.
Estoy usando mucho fast_cgi_finish_request()
En combinación con un cierre y register_shutdown_function ()
$message =''job executed'';
$backgroundJob = function() use ($message) {
//do some work here
echo $message;
}
Luego registre este cierre para ser ejecutado antes del cierre.
register_shutdown_function($backgroundJob);
Finalmente, cuando la respuesta se envió al cliente, puede cerrar la conexión al cliente y continuar trabajando con el proceso de PHP:
fast_cgi_finish_request();
El cierre se ejecutará después de fast_cgi_finish_request.
El mensaje $ no será visible en ningún momento. Y puede registrar tantos cierres como desee, pero tenga cuidado con el tiempo de ejecución del script. Esto solo funcionará si PHP se está ejecutando como un módulo de CGI rápido (¡¿era eso correcto ?!)
PHP scripting no es como otro lenguaje de desarrollo de aplicaciones de escritorio. En los lenguajes de aplicaciones de escritorio, podemos establecer hilos de daemon para ejecutar un proceso en segundo plano, pero en PHP ocurre un proceso cuando el usuario solicita una página. Sin embargo, es posible establecer un trabajo en segundo plano utilizando la funcionalidad de trabajo cron del servidor que ejecuta el script php.
Para aquellos de nosotros que usamos Windows , miren esto:
Referencia: http://php.net/manual/en/function.exec.php#43917
Yo también luché con conseguir que un programa se ejecutara en segundo plano en Windows mientras el script continúa ejecutándose. Este método, a diferencia de otras soluciones, le permite iniciar cualquier programa minimizado, maximizado o sin ninguna ventana. La solución de llbra @ phpbrasil funciona pero a veces produce una ventana no deseada en el escritorio cuando realmente desea que la tarea se oculte.
iniciar Notepad.exe minimizado en el fondo:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>
iniciar un comando de shell invisible en el fondo:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>
inicie MSPaint maximizado y espere a que lo cierre antes de continuar con el script:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe", 3, true);
?>
Para obtener más información sobre el método Run (), vaya a: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp
URL editada:
Vaya a https://technet.microsoft.com/en-us/library/ee156605.aspx ya que el enlace anterior ya no existe.
Puede intentar un sistema de colas como Resque . A continuación, puede generar un trabajo, que procesa la información y un retorno bastante rápido con la imagen de "procesamiento". Sin embargo, con este enfoque no sabrás cuándo está terminado.
Esta solución está diseñada para aplicaciones de mayor escala, donde no desea que sus máquinas frontales hagan el trabajo pesado, por lo que pueden procesar las solicitudes de los usuarios. Por lo tanto, podría o no funcionar con datos físicos como archivos y carpetas, pero para procesar lógica más complicada u otras tareas asíncronas (es decir, nuevos correos de registros) es bueno tenerlo y es muy escalable.
Sé que es una publicación de 100 años, pero de todos modos, pensé que podría ser útil para alguien. Puede colocar una imagen invisible en algún lugar de la página que apunta a la URL que debe ejecutarse en segundo plano, como esto:
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />
Si solo necesita hacer algo en segundo plano sin que la página de PHP espere a que se complete, puede usar otro script PHP (de fondo) que se "invoque" con el comando wget. Este script PHP de fondo se ejecutará con privilegios, por supuesto, como cualquier otro script PHP en su sistema.
Aquí hay un ejemplo en Windows usando wget de los paquetes gnuwin32.
El código de fondo (archivo test-proc-bg.php) como un ejemplo ...
sleep(5); // some delay
file_put_contents(''test.txt'', date(''Y-m-d/H:i:s.u'')); // writes time in a file
El guión en primer plano, el que invoca ...
$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);
Debe usar el popen / pclose para que esto funcione correctamente.
Las opciones de wget:
-q keeps wget quiet.
-O - outputs to stdout.
-b works on background
Si usa PHP, hay una forma mucho más fácil de hacerlo usando pcntl_fork:
Solo me gustaría agregar un ejemplo muy simple para probar esta funcionalidad en Windows:
Cree los dos archivos siguientes y guárdelos en un directorio web:
foreground.php
<?php
ini_set("display_errors",1);
error_reporting(E_ALL);
echo "<pre>loading page</pre>";
function run_background_process()
{
file_put_contents("testprocesses.php","foreground start time = " . time() . "/n");
echo "<pre> foreground start time = " . time() . "</pre>";
// output from the command must be redirected to a file or another output stream
// http://ca.php.net/manual/en/function.exec.php
exec("php background.php > testoutput.php 2>&1 & echo $!", $output);
echo "<pre> foreground end time = " . time() . "</pre>";
file_put_contents("testprocesses.php","foreground end time = " . time() . "/n", FILE_APPEND);
return $output;
}
echo "<pre>calling run_background_process</pre>";
$output = run_background_process();
echo "<pre>output = "; print_r($output); echo "</pre>";
echo "<pre>end of page</pre>";
?>
background.php:
<?
file_put_contents("testprocesses.php","background start time = " . time() . "/n", FILE_APPEND);
sleep(10);
file_put_contents("testprocesses.php","background end time = " . time() . "/n", FILE_APPEND);
?>
Otorgue permiso a IUSR para escribir en el directorio en el que creó los archivos anteriores
Otorgue permiso IUSR para LEER y EJECUTAR C: / Windows / System32 / cmd.exe
Hit foreground.php desde un navegador web
Se debe representar lo siguiente en el navegador con las marcas de tiempo actuales y el número de recurso local en la matriz de salida:
loading page
calling run_background_process
foreground start time = 1266003600
foreground end time = 1266003600
output = Array
(
[0] => 15010
)
end of page
Debería ver testoutput.php en el mismo directorio en el que se guardaron los archivos anteriores y debería estar vacío
Debería ver testprocesses.php en el mismo directorio en el que se guardaron los archivos anteriores, y debería contener el siguiente texto con las marcas de tiempo actuales:
foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610
Suponiendo que esto se ejecuta en una máquina Linux, siempre lo he manejado así:
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
Esto inicia el comando $cmd
, redirige la salida del comando a $outputfile
y escribe la identificación del proceso en $pidfile
.
Eso le permite controlar fácilmente lo que está haciendo el proceso y si aún se está ejecutando.
function isRunning($pid){
try{
$result = shell_exec(sprintf("ps %d", $pid));
if( count(preg_split("//n/", $result)) > 2){
return true;
}
}catch(Exception $e){}
return false;
}
Una solución de trabajo para Windows y Linux. Encuentre más en mi página de github .
function run_process($cmd,$outputFile = ''/dev/null'', $append = false){
$pid=0;
if (strtoupper(substr(PHP_OS, 0, 3)) === ''WIN'') {//''This is a server using Windows!'';
$cmd = ''wmic process call create "''.$cmd.''" | find "ProcessId"'';
$handle = popen("start /B ". $cmd, "r");
$read = fread($handle, 200); //Read the output
$pid=substr($read,strpos($read,''='')+1);
$pid=substr($pid,0,strpos($pid,'';'') );
$pid = (int)$pid;
pclose($handle); //Close
}else{
$pid = (int)shell_exec(sprintf(''%s %s %s 2>&1 & echo $!'', $cmd, ($append) ? ''>>'' : ''>'', $outputFile));
}
return $pid;
}
function is_process_running($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === ''WIN'') {//''This is a server using Windows!'';
//tasklist /FI "PID eq 6480"
$result = shell_exec(''tasklist /FI "PID eq ''.$pid.''"'' );
if (count(preg_split("//n/", $result)) > 0 && !preg_match(''/No tasks/'', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf(''ps %d 2>&1'', $pid));
if (count(preg_split("//n/", $result)) > 2 && !preg_match(''/ERROR: Process ID out of range/'', $result)) {
return true;
}
}
return false;
}
function stop_process($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === ''WIN'') {//''This is a server using Windows!'';
$result = shell_exec(''taskkill /PID ''.$pid );
if (count(preg_split("//n/", $result)) > 0 && !preg_match(''/No tasks/'', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf(''kill %d 2>&1'', $pid));
if (!preg_match(''/No such process/'', $result)) {
return true;
}
}
}
Utilice esta función para ejecutar su programa en segundo plano. Es multiplataforma y totalmente personalizable.
<?php
function startBackgroundProcess(
$command,
$stdin = null,
$redirectStdout = null,
$redirectStderr = null,
$cwd = null,
$env = null,
$other_options = null
) {
$descriptorspec = array(
1 => is_string($redirectStdout) ? array(''file'', $redirectStdout, ''w'') : array(''pipe'', ''w''),
2 => is_string($redirectStderr) ? array(''file'', $redirectStderr, ''w'') : array(''pipe'', ''w''),
);
if (is_string($stdin)) {
$descriptorspec[0] = array(''pipe'', ''r'');
}
$proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
if (!is_resource($proc)) {
throw new /Exception("Failed to start background process by command: $command");
}
if (is_string($stdin)) {
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
}
if (!is_string($redirectStdout)) {
fclose($pipes[1]);
}
if (!is_string($redirectStderr)) {
fclose($pipes[2]);
}
return $proc;
}
Tenga en cuenta que después de iniciar el comando, esta función cierra de forma predeterminada la entrada estándar y la salida estándar del proceso en ejecución. Puede redirigir la salida del proceso a algún archivo a través de los argumentos $ redirectStdout y $ redirectStderr.
Nota para los usuarios de Windows: no hay forma de redireccionar stdout / stderr a nul
device. Desafortunadamente no puedes hacerlo:
startBackgroundProcess(''ping yandex.com'', null, ''nul'', ''nul'');
Por lo tanto, debe redirigir stderr / stdin a un archivo válido o su comando debe esperar que no sea un descriptor stderr / stdout.
Notas para usuarios de * nix:
1) Utilice el comando exec shell si desea obtener un PID real:
$proc = startBackgroundProcess(''exec ping yandex.com -c 15'', null, ''/dev/null'', ''/dev/null'');
print_r(proc_get_status($proc));
2) Use el argumento $ stdin si desea pasar algunos datos a la entrada de su programa:
startBackgroundProcess(''cat > input.txt'', "Hello world!/n");