Php выполняет фоновый процесс

Мне нужно выполнить копию каталога при действии пользователя, но каталоги довольно велики, поэтому я хотел бы иметь возможность выполнить такое действие без того, чтобы пользователь знал о времени, которое требуется для завершения копирования.

Любые предложения будут высоко оценены.

Ответ 1

Предполагая, что это работает на Linux-машине, я всегда обрабатывал его следующим образом:

exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));

Это запускает команду $cmd, перенаправляет вывод команды на $outputfile и записывает идентификатор процесса в $pidfile.

Это позволяет вам легко контролировать, что делает этот процесс, и если он все еще работает.

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;
}

Ответ 2

Запишите процесс как серверный script на любом языке (php/bash/perl/etc), а затем вызовите его из функций управления процессом в вашем PHP скрипт.

Функция, вероятно, обнаруживает, что в качестве выходного потока используется стандартный io, и если он тогда будет устанавливать возвращаемое значение. Если он не заканчивается

Proc_Close (Proc_Open ("./command --foo=1 &", Array (), $foo));

Я быстро проверил это из командной строки, используя "sleep 25s" в качестве команды, и он работал как шарм.

(Ответ найден здесь)

Ответ 3

Я просто хотел бы добавить очень простой пример для тестирования этой функции в Windows:

Создайте следующие два файла и сохраните их в веб-каталоге:

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);
?>

Предоставьте разрешение IUSR для записи в каталог, в котором вы создали указанные выше файлы.

Предоставьте разрешение IUSR для ЧТЕНИЯ и ВЫПОЛНЕНИЯ C:\Windows\System32\cmd.exe

Хит foreground.php из веб-браузера

В браузере с текущими временными метками и локальным ресурсом # в выходном массиве должно быть отображено следующее:

loading page
calling run_background_process
  foreground start time = 1266003600
  foreground end time = 1266003600
output = Array
(
    [0] => 15010
)
end of page

Вы должны увидеть testoutput.php в том же каталоге, что и предыдущие файлы, и он должен быть пустым

Вы должны увидеть testprocesses.php в том же каталоге, в котором были сохранены указанные выше файлы, и он должен содержать следующий текст w/current timestamps:

foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610

Ответ 4

Возможно, вы захотите добавить это в свою команду

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

например.

shell_exec('service named reload >/dev/null 2>/dev/null &');

Ответ 5

Если вам нужно просто сделать что-то в фоновом режиме, не ожидая завершения этой страницы PHP, вы можете использовать другой (фоновый) PHP script, который "вызывается" с помощью команды wget. Этот фоновый PHP скрипт будет выполняться с привилегиями, конечно, как и любой другой PHP скрипт в вашей системе.

Вот пример использования Windows с помощью wget из пакетов gnuwin32.

Фоновый код (файл test-proc-bg.php) как exmple...

sleep(5);   // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file

На переднем плане script, вызывающий...

$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);

Вы должны использовать popen/pclose для правильной работы.

Параметры wget:

-q    keeps wget quiet.
-O -  outputs to stdout.
-b    works on background

Ответ 6

Ну, я нашел немного более быструю и легкую версию для использования

shell_exec('screen -dmS $name_of_screen $command'); 

и он работает.

Ответ 7

Вот функция запуска фонового процесса в PHP. Наконец, создан тот, который действительно работает и в Windows, после большого количества чтения и тестирования различных подходов и параметров.

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;
  }
}

Примечание 1: В окнах не используйте параметр /B, как предлагается в другом месте. Это заставляет процесс запускать то же окно консоли, что и сама команда start, в результате процесс обрабатывается синхронно. Чтобы запустить процесс в отдельном потоке (асинхронно), не используйте /B.

Примечание 2: Пустые двойные кавычки после start "" требуются, если команда является цитируемым путем. Команда start интерпретирует первый цитируемый параметр как заголовок окна.

Ответ 8

Можно ли отменить отдельный процесс, а затем запустить копию в фоновом режиме? Прошло некоторое время с тех пор, как я сделал любой PHP, но функция pcntl-fork выглядит многообещающей.

Ответ 9

Вы можете попробовать систему массового обслуживания, например Resque. Затем вы можете сгенерировать задание, которое обрабатывает информацию и довольно быстро возвращается с "обрабатывающим" изображением. При таком подходе вы не будете знать, когда это будет закончено.

Это решение предназначено для приложений большего масштаба, где вы не хотите, чтобы ваши передние машины выполняли тяжелый подъем, поэтому они могут обрабатывать запросы пользователей. Поэтому он может работать или не работать с физическими данными, такими как файлы и папки, но для обработки более сложных логических или других асинхронных задач (т.е. Новых писем с регистрацией) приятно иметь и очень масштабируемое.

Ответ 10

Используйте эту функцию для запуска вашей программы в фоновом режиме. Это кросс-платформенный и полностью настраиваемый.

<?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;
}

Обратите внимание, что после запуска команды по умолчанию эта функция закрывает stdin и stdout запущенного процесса. Вы можете перенаправить вывод процесса в некоторый файл через аргументы $ redirectStdout и $ redirectStderr.

Примечание для пользователей Windows:
Вы не можете перенаправить stdout/stderr в nul следующим образом:

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

Однако вы можете сделать это:

startBackgroundProcess('ping yandex.com >nul 2>&1');

Примечания для пользователей * nix:

1) Используйте команду exec shell, если вы хотите получить фактический PID:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2) Используйте аргумент $ stdin, если вы хотите передать некоторые данные на вкладку своей программы:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

Ответ 11

Рабочее решение для Windows и Linux. Подробнее о Моя страница 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;
                        }
                    }
            }

Ответ 12

Вместо того, чтобы инициировать фоновый процесс, как насчет создания триггерного файла и с планировщиком, например cron или autosys, периодически выполняется script, который ищет и действует на файлы триггеров? Триггеры могут содержать инструкции или даже необработанные команды (еще лучше, просто сделайте оболочку script).

Ответ 14

Я сильно использую fast_cgi_finish_request()

В сочетании с закрытием и register_shutdown_function()

$message ='job executed';
$backgroundJob = function() use ($message) {
     //do some work here
    echo $message;
}

Затем зарегистрируйте это закрытие, которое должно быть выполнено до выключения.

register_shutdown_function($backgroundJob);

Наконец, когда ответ был отправлен клиенту, вы можете закрыть соединение с клиентом и продолжить работу с процессом PHP:

fast_cgi_finish_request();

Закрытие будет выполнено после fast_cgi_finish_request.

Сообщение $не будет отображаться в любое время. И вы можете зарегистрировать столько закрытий, сколько захотите, но позаботьтесь о script времени выполнения. Это будет работать, только если PHP работает как быстрый модуль CGI (это было правильно?!)

Ответ 15

PHP-скриптинг не похож на другой язык разработки настольных приложений. В настольных языках приложений мы можем установить потоки демона для запуска фонового процесса, но в PHP процесс возникает, когда пользователь запрашивает страницу. Однако можно установить фоновое задание с использованием функции задания сервера cron, выполняемой php script.

Ответ 16

Для тех из нас, кто использует Windows, посмотрите на это:

Ссылка: http://php.net/manual/en/function.exec.php#43917

Я тоже боролся с тем, чтобы программа запускалась в фоновом режиме в Windows, пока скрипт продолжает выполняться. Этот метод, в отличие от других решений, позволяет запускать любую программу с минимальным, максимизированным или без какого-либо окна. Решение llbra @phpbrasil работает, но иногда создает нежелательное окно на рабочем столе, когда вы действительно хотите, чтобы задача выполнялась скрытой.

Запустите Notepad.exe в фоновом режиме:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

запустите команду оболочки, невидимую в фоновом режиме:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

запустите MSPaint и подождите, пока вы его закроете, прежде чем продолжить сценарий:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

Для получения дополнительной информации о методе Run() перейдите по адресу: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

Отредактированный URL:

Вместо этого перейдите на страницу https://technet.microsoft.com/en-us/library/ee156605.aspx, поскольку ссылка выше не существует.

Ответ 17

Благодаря этому ответу: идеальным инструментом для запуска фонового процесса был бы Symfony Process Component, который основан на proc_*, но его гораздо проще использовать. Смотрите документацию для получения дополнительной информации.

Ответ 18

Я знаю, что это 100-летний пост, но в любом случае подумал, что это может быть полезно кому-то. Вы можете поместить невидимое изображение где-нибудь на странице, указывающую на URL-адрес, который должен выполняться в фоновом режиме, например:

<img src="run-in-background.php" border="0" alt="" width="1" height="1" />