PHPはバックグラウンドプロセスを実行します


259

ユーザーのアクションでディレクトリのコピーを実行する必要がありますが、ディレクトリが非常に大きいため、ユーザーがコピーの完了にかかる時間を意識することなく、そのようなアクションを実行できるようにしたいと考えています。

任意の提案をいただければ幸いです。


3
このstackoverflow.com/questions/5367261/…は、windowsでこれを行う方法を説明しています
shealtiel

回答:


365

これがLinuxマシンで実行されていると仮定すると、私は常に次のように処理しました。

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

これにより、コマンドが起動し$cmd、コマンド出力がにリダイレクトされ$outputfile、プロセスIDがに書き込まれ$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
sudoセットアップはパスワードの入力を求められずに実行されますか?ユーザー入力を必要とするコマンドは機能しません。
Mark Biek

17
このstackoverflow.com/questions/5367261/…は、Windowsで同じことを行う方法を説明しています
シェルティエル

13
これを使用しましたが、少し更新したため、PIDをファイルに書き込む必要はありませんでした。したがって、次の形式を使用します。<code> exec(sprintf( "$ s> $ s 2>&1&echo $ 1"、$ cmd、$ outputfile)、$ pidArr); </ code>結果のプロセスPIDは$ pidArr [0]にあります
Kaiesh

3
@Kaiesh:「エコー$!」
brunobg 2013年

8
Kaieshは「%」ではなく「$」という注釈のタイプミスをしました。修正バージョン:exec(sprintf( "%s>%s 2>&1&echo $!"、$ cmd、$ outputfile)、$ pidArr)
Yuri Gor

23

プロセスをサーバーサイドスクリプトとして任意の言語(php / bash / perl / etc)で記述し、phpスクリプトのプロセス制御関数から呼び出します。

関数はおそらく、標準ioが出力ストリームとして使用されているかどうかを検出し、それが使用されている場合は戻り値を設定します。そうでない場合は終了します

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

私はコマンドラインから「sleep 25s」をコマンドとして使用してこれをすばやくテストしましたが、それは魅力のように機能しました。

ここで答えが見つかります


3
これは私がcentosで動作させる唯一の方法です。ありがとうございました!
Deac Karns、

よろしくお願いします!PHPとBASHには互換性がありません。新しいシステムを実行すると、system()またはexec()を使用したバックグラウンドでの起動に失敗します。リダイレクトしてもファイル記述子が動かなくなるようです。あなたの方法はうまくいきました。別の代替手段は、PHPに古いbashバイナリを使用することですが、それ
John

22

これをコマンドに追加してみてください

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

例えば。

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

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

それは私にとってはうまくいき、makefileを使用してサーバーサイトで何かをします、ありがとう!(例:make、make restart)
Chu-Siang Lai

これは受け入れられた答えよりもはるかに簡単です!(プロセスがまだ実行されているかどうかの確認など、より複雑なものを必要としない限り)
Beanをショーンする

18

Windowsでこの機能をテストするための非常に単純な例を追加したいと思います。

次の2つのファイルを作成し、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);
?>

上記のファイルを作成したディレクトリへの書き込み権限をIUSRに付与します

IUSRにC:\ Windows \ System32 \ cmd.exeの読み取りと実行を許可します。

Webブラウザーから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が表示され、次のテキストと現在のタイムスタンプが含まれているはずです。

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

10

PHPページが完了するのを待たずにバックグラウンドで何かを行う必要がある場合は、wgetコマンドで「呼び出される」別の(バックグラウンド)PHPスクリプトを使用できます。もちろん、このバックグラウンドPHPスクリプトは、システム上の他のPHPスクリプトと同様に、特権を使用して実行されます。

以下は、gnuwin32パッケージのwgetを使用したWindowsの例です。

例としてのバックグラウンドコード(ファイルtest-proc-bg.php)...

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

フォアグラウンドスクリプト、呼び出し...

$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

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:Windowsでは、/B他の場所で提案されているパラメーターを使用しないでください。これstartは、プロセスにコマンド自体と同じコンソールウィンドウを実行させるため、プロセスは同期的に処理されます。別のスレッドで(非同期に)プロセスを実行するには、を使用しないでください/B

注2:start ""コマンドが引用符で囲まれたパスの場合、後に空の二重引用符が必要です。startコマンドは、最初に引用されたパラメーターをウィンドウタイトルとして解釈します。


6

さて、私は使用する少し速くて簡単なバージョンを見つけました

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

そしてそれは動作します。


@ Alpha.Dev私はそれをLinuxでのみテストしました。別のシステムで動作するかどうかはわかりません。
モルフィドン、2016年

4

別のプロセスを分岐して、バックグラウンドでコピーを実行するように手配できますか?PHPを実行してからしばらく経ちますが、関数pcntl-forkは有望に見えます。


これは質問に対する答えを提供しません。批評したり、著者に説明を求めるには、投稿の下にコメントを残してください。
Rizier123 2015

13
ありがとう-コメントは'08には存在しませんでしたが、それを覚えておきます:)
Matt Sheppard

4

この関数を使用して、プログラムをバックグラウンドで実行します。クロスプラットフォームで完全にカスタマイズ可能です。

<?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)実際のPIDを取得する場合は、execシェルコマンドを使用します。

$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");

3

Resqueのようなキューイングシステムを試すこともできます。次に、情報を処理するジョブを生成し、「処理中」のイメージで非常に高速に戻ることができます。このアプローチでは、いつ終了するかはわかりません。

このソリューションは、フロントマシンがユーザーの要求を処理できるように、大規模な処理を行わないようにする大規模なアプリケーションを対象としています。したがって、ファイルやフォルダーなどの物理データを処理する場合としない場合がありますが、より複雑なロジックやその他の非同期タスク(つまり、新規登録メール)を処理する場合は、非常にスケーラブルであり、便利です。


2

WindowsとLinuxの両方の実用的なソリューション。詳細はMy github pageをご覧ください

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

バックグラウンドプロセスを開始する代わりに、トリガーファイルを作成し、cronやautosysなどのスケジューラーに、トリガーファイルを探して操作するスクリプトを定期的に実行させるのはどうでしょうか。トリガーには、命令または生のコマンドを含めることができます(なお、シェルスクリプトにするだけです)。



1

私は頻繁に使用しています 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の後に実行されます。

$ messageはいつでも表示されません。また、必要な数のクロージャを登録できますが、スクリプトの実行時間に注意してください。これは、PHPがFast CGIモジュールとして実行されている場合にのみ機能します(そうでしたか?)


0

PHPスクリプトは、他のデスクトップアプリケーション開発言語とは異なります。デスクトップアプリケーション言語では、バックグラウンドプロセスを実行するようにデーモンスレッドを設定できますが、PHPでは、ユーザーがページをリクエストするとプロセスが発生します。ただし、phpスクリプトが実行するサーバーのcronジョブ機能を使用してバックグラウンドジョブを設定することは可能です。


0

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を最大化して起動し、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にアクセスしてください


1
このコードを実行するには何をする必要がありますか?取得Fatal error: Class 'COM' not found
Alph.Dev

PsのMSDNへのリンクが壊れている
Alph.Dev

@ Alph.Dev:msdnドキュメントが一時的に削除されたようです。WScript.Shellの使用例と説明が含まれています。
ジミーイレンロア

@ Alph.Dev:COMクラスが見つからない場合、それはまったく別のことです。Googleで確認してください。また、このphp.net/manual/en/com.installation.phpを
Jimmy Ilenloa '17

@ Alph.Dev で、run()関数の最新情報については、technet.microsoft.com / en-us / library / ee156605.aspxを確認してください
Jimmy Ilenloa

-12

私はそれが100年前の投稿であることを知っていますが、とにかく、誰かに役立つかもしれないと思いました。次のように、バックグラウンドで実行する必要のあるURLを指す非表示の画像をページのどこかに配置できます。

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


12
本当に悪い解決策。ユーザーがページを離れると、プロセスが中断されます。
Sergey P. aka azure

3
「見えない画像」とそのリンクは、ページのソースコードで公開されています。
トニーギル2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.