php melakukan proses latar belakang


259

Saya perlu menjalankan salinan direktori pada tindakan pengguna, tetapi direktori cukup besar, jadi saya ingin dapat melakukan tindakan seperti itu tanpa pengguna menyadari waktu yang dibutuhkan untuk menyalin selesai.

Setiap saran akan sangat dihargai.


3
Ini stackoverflow.com/questions/5367261/... menjelaskan bagaimana melakukan ini di bawah jendela
Sealtiel

Jawaban:


365

Dengan asumsi ini berjalan pada mesin Linux, saya selalu menanganinya seperti ini:

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

Ini meluncurkan perintah $cmd, mengarahkan output perintah ke $outputfile, dan menulis id proses $pidfile.

Itu memungkinkan Anda memantau dengan mudah apa yang sedang dilakukan dan apakah prosesnya masih berjalan.

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
Apakah pengaturan sudo berjalan tanpa diminta kata sandi? Perintah apa pun yang membutuhkan input pengguna tidak akan berfungsi.
Mark Biek

17
Ini stackoverflow.com/questions/5367261/... menjelaskan bagaimana melakukan jendela bawah yang sama
Sealtiel

13
Saya menggunakan ini, tetapi memperbaruinya sedikit jadi saya tidak perlu menulis PID ke file. Jadi saya menggunakan format ini: <code> exec (sprintf ("$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </code> Proses PID yang dihasilkan adalah dalam $ pidArr [0]
Kaiesh

3
@ Kaiesh: seharusnya "echo $!"
brunobg

8
Kaiesh membuat salah ketik anoter, '$' bukannya '%'. Versi yang diperbaiki: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)
Yuri Gor

23

Tulis proses sebagai skrip sisi-server dalam bahasa apa pun (php / bash / perl / dll) yang berguna dan kemudian panggil dari fungsi kontrol proses di skrip php Anda.

Fungsi ini mungkin mendeteksi jika standar io digunakan sebagai aliran output dan jika itu kemudian yang akan menetapkan nilai kembali .. jika tidak maka itu berakhir

proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );

Saya menguji ini dengan cepat dari baris perintah menggunakan "sleep 25s" sebagai perintah dan itu bekerja seperti pesona.

( Jawaban ditemukan di sini )


3
Ini adalah satu-satunya cara saya bisa membuatnya bekerja pada centos. Terima kasih!
Deac Karns

Terima kasih! Ada ketidakcocokan PHP dan BASH. Jika Anda menjalankan sistem yang lebih baru, ia akan gagal diluncurkan di latar belakang menggunakan system () atau exec (). Deskriptor file tampaknya macet bahkan jika Anda mengarahkan ulang. Metode Anda berhasil. alternatif lain adalah dengan menggunakan biner bash lama untuk PHP, tapi itu gila
John

22

Anda mungkin ingin mencoba menambahkan ini ke perintah Anda

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

misalnya.

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

Maksud saya> / dev / null 2> / dev / null &
wlf

Ini bekerja untuk saya, dan saya akan menggunakan makefile melakukan sesuatu di situs server, terima kasih! (mis. make, make restart)
Chu-Siang Lai

Ini jauh lebih sederhana daripada jawaban yang diterima! (Selama Anda tidak perlu hal yang lebih rumit, seperti memeriksa apakah prosesnya masih berjalan.)
Sean the Bean

18

Saya hanya ingin menambahkan contoh yang sangat sederhana untuk menguji fungsi ini pada Windows:

Buat dua file berikut dan simpan ke direktori 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);
?>

Berikan izin IUSR untuk menulis ke direktori tempat Anda membuat file di atas

Berikan izin IUSR untuk MEMBACA dan LAKUKAN C: \ Windows \ System32 \ cmd.exe

Tekan foreground.php dari peramban web

Berikut ini harus diterjemahkan ke browser dengan cap waktu saat ini dan sumber daya lokal # dalam array keluaran:

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

Anda akan melihat testoutput.php di direktori yang sama dengan file di atas disimpan, dan itu harus kosong

Anda akan melihat testprocesses.php di direktori yang sama dengan file di atas disimpan, dan itu harus berisi teks berikut dengan cap waktu saat ini:

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

10

Jika Anda hanya perlu melakukan sesuatu di latar belakang tanpa halaman PHP menunggu untuk menyelesaikannya, Anda dapat menggunakan skrip PHP lain (latar belakang) yang "dipanggil" dengan perintah wget. Skrip PHP latar belakang ini akan dieksekusi dengan hak istimewa, tentu saja, seperti skrip PHP lainnya di sistem Anda.

Berikut adalah contoh di Windows menggunakan wget dari paket gnuwin32.

Kode latar belakang (file test-proc-bg.php) sebagai contoh ...

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

Skrip latar depan, yang memohon ...

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

Anda harus menggunakan popen / pclose agar ini berfungsi dengan baik.

Opsi wget:

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

7

Berikut adalah fungsi untuk meluncurkan proses latar belakang di PHP. Akhirnya dibuat satu yang benar-benar berfungsi pada Windows juga, setelah banyak membaca dan menguji berbagai pendekatan dan parameter.

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

Catatan 1: Di windows, jangan gunakan /Bparameter seperti yang disarankan di tempat lain. Ini memaksa proses untuk menjalankan jendela konsol yang sama dengan startperintah itu sendiri, sehingga proses diproses secara serempak. Untuk menjalankan proses di utas terpisah (asinkron), jangan gunakan /B.

Catatan 2: Kutipan ganda kosong setelah start ""diperlukan jika perintahnya adalah path yang dikutip. startperintah menafsirkan parameter yang dikutip pertama sebagai judul jendela.


6

Yah saya menemukan versi yang sedikit lebih cepat dan lebih mudah digunakan

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

dan itu berhasil.


@ Alpha.Dev Saya mengujinya di linux hanya saya tidak tahu apakah itu akan bekerja pada sistem lain.
Morfidon

4

Bisakah Anda mengatur untuk memotong proses yang terpisah, dan kemudian menjalankan salinan Anda di latar belakang? Sudah lama sejak saya melakukan PHP, tetapi fungsi pcntl-fork terlihat menjanjikan.


Ini tidak memberikan jawaban untuk pertanyaan itu. Untuk mengkritik atau meminta klarifikasi dari penulis, tinggalkan komentar di bawah posting mereka.
Rizier123

13
Terima kasih - Komentar tidak ada di '08, tapi saya akan mengingatnya :)
Matt Sheppard

4

Gunakan fungsi ini untuk menjalankan program Anda di latar belakang. Ini lintas platform dan sepenuhnya dapat disesuaikan.

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

Perhatikan bahwa setelah perintah dimulai, secara default fungsi ini menutup stdin dan stdout dari proses yang berjalan. Anda dapat mengarahkan proses output ke beberapa file melalui argumen $ redirectStdout dan $ redirectStderr.

Catatan untuk pengguna windows:
Anda tidak dapat mengalihkan stdout / stderr ke nuldengan cara berikut:

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

Namun, Anda dapat melakukan ini:

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

Catatan untuk * nix pengguna:

1) Gunakan perintah exec shell jika Anda ingin mendapatkan PID aktual:

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

2) Gunakan argumen $ stdin jika Anda ingin meneruskan beberapa data ke input program Anda:

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

3

Anda dapat mencoba sistem antrian seperti Resque . Anda kemudian dapat menghasilkan pekerjaan, yang memproses informasi dan kembali dengan cepat dengan gambar "pemrosesan". Dengan pendekatan ini Anda tidak akan tahu kapan itu selesai.

Solusi ini ditujukan untuk aplikasi berskala lebih besar, di mana Anda tidak ingin alat berat depan Anda melakukan pengangkatan berat, sehingga mereka dapat memproses permintaan pengguna. Karena itu mungkin atau mungkin tidak bekerja dengan data fisik seperti file dan folder, tetapi untuk memproses logika yang lebih rumit atau tugas asinkron lainnya (yaitu email pendaftaran baru) itu bagus untuk dimiliki dan sangat scalable.


2

Solusi yang berfungsi untuk Windows dan Linux. Temukan lebih banyak di halaman github saya .

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


1

Alih-alih memulai proses latar belakang, bagaimana dengan membuat file pemicu dan memiliki penjadwal seperti cron atau autosys secara berkala menjalankan skrip yang mencari dan bertindak pada file pemicu? Pemicu bisa berisi instruksi atau bahkan perintah mentah (lebih baik lagi, buat saja skrip shell).



1

Saya banyak menggunakan fast_cgi_finish_request()

Dalam kombinasi dengan penutupan dan register_shutdown_function ()

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

Kemudian daftarkan penutupan ini untuk dieksekusi sebelum shutdown.

register_shutdown_function($backgroundJob);

Akhirnya ketika respons dikirim ke klien Anda dapat menutup koneksi ke klien dan terus bekerja dengan proses PHP:

fast_cgi_finish_request();

Penutupan akan dieksekusi setelah fast_cgi_finish_request.

$ Message tidak akan terlihat kapan saja. Dan Anda dapat mendaftarkan penutupan sebanyak yang Anda inginkan, tetapi berhati-hatilah dengan waktu eksekusi skrip. Ini hanya akan berfungsi jika PHP dijalankan sebagai modul Fast CGI (benarkah itu ?!)


0

Scripting PHP tidak seperti bahasa berkembang aplikasi desktop lainnya. Dalam bahasa aplikasi desktop kita dapat mengatur thread daemon untuk menjalankan proses latar belakang tetapi dalam PHP proses terjadi ketika pengguna meminta halaman. Namun dimungkinkan untuk mengatur pekerjaan latar belakang menggunakan fungsionalitas tugas cron server yang dijalankan oleh skrip php.


0

Bagi kita yang menggunakan Windows , lihat ini:

Referensi: http://php.net/manual/en/function.exec.php#43917

Saya juga bergulat dengan mendapatkan program untuk berjalan di latar belakang di Windows sementara skrip terus dijalankan. Metode ini tidak seperti solusi lain yang memungkinkan Anda untuk memulai program yang diminimalkan, dimaksimalkan, atau tanpa jendela sama sekali. Solusi llbra @ phpbrasil bekerja tetapi terkadang menghasilkan jendela yang tidak diinginkan di desktop ketika Anda benar-benar ingin tugas tersebut dijalankan secara tersembunyi.

mulai Notepad.exe diminimalkan di latar belakang:

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

mulai perintah shell tidak terlihat di latar belakang:

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

mulailah MSPaint dimaksimalkan dan tunggu sampai Anda menutupnya sebelum melanjutkan skrip:

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

Untuk info lebih lanjut tentang metode Run (), buka: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

URL yang diedit:

Buka https://technet.microsoft.com/en-us/library/ee156605.aspx sebagai gantinya karena tautan di atas tidak ada lagi.


1
Apa yang perlu dilakukan untuk menjalankan kode ini? Saya mendapatkanFatal error: Class 'COM' not found
Alph.Dev

PS Tautan ke MSDN rusak
Alph.Dev

@ Alph.Dev: Dok msdn tampaknya telah dihapus sementara. Itu berisi lebih banyak contoh dan penjelasan tentang cara menggunakan WScript.Shell.
Jimmy Ilenloa

@ Alph.Dev: Untuk COM kelas tidak ditemukan, itu hal lain sama sekali. Lihat itu di google. Anda juga dapat memeriksa php.net/manual/en/com.installation.php
Jimmy Ilenloa

@ Alph.Dev silahkan cek technet.microsoft.com/en-us/library/ee156605.aspx untuk informasi terbaru pada fungsi Run ()
Jimmy Ilenloa

-12

Saya tahu ini adalah postingan berusia 100 tahun, tetapi bagaimanapun, saya pikir itu mungkin berguna bagi seseorang. Anda dapat meletakkan gambar yang tidak terlihat di suatu halaman di halaman yang menunjuk ke url yang perlu dijalankan di latar belakang, seperti ini:

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


12
Solusi yang sangat buruk. Jika pengguna akan meninggalkan halaman maka proses akan terganggu.
Sergey P. alias azure

3
"gambar tak terlihat" dan tautannya ditampilkan dalam kode sumber halaman.
tony gil
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.