今日の午後、segmentfault.com で質問のタイトルは「PHP をサービスとして実装する方法」というもので、PHP は Web 経由でのみ呼び出すことができるかどうかを尋ねるものでした。実際、多くの人が PHP の使用シナリオについて誤解しています。PHP は Web スクリプトを作成するためにのみ使用できると考えています。実際、PHP 4 以降、PHP の使用シナリオは Web リクエストの処理に限定されなくなりました。
PHP のアーキテクチャ システムの観点から見ると、PHP は、sapi、php コア、zend エンジンの 3 つのレベルに分かれています。 PHP コア自体は Web と連携していません。たとえば、mod_php は、Apache 用に作成された sapi 実装であり、これらの sapi と組み合わせて使用されます。 Web サーバーは Web リクエストを処理します。ただし、Web と関係のない Sapi も多数あります。たとえば、cli sapi はコマンドライン環境で php を直接実行でき、embed sapi は他の言語 (Lua など) に php を埋め込むことができます。ここでは、PHP のアーキテクチャ システムと SAPI について詳しく説明するつもりはありません。アーキテクチャ システムの観点から、PHP はすでにさまざまな環境をサポートするように設計されており、Web に固有のものではないことを説明したいと思います。
アーキテクチャ システムのサポートに加えて、PHP の豊富な拡張モジュールは、さまざまな環境で PHP が機能するためのサポートも提供します。たとえば、この記事で説明した pcntl モジュールと POSIX モジュールは、基本的なプロセス管理、信号処理、その他のオペレーティング システムを実現できます。 -level 関数とソケット モジュールにより、PHP はソケット通信機能を使用できるようになります。したがって、PHP を使用すると、シェルや Perl で一般的に使用されるものと同様のツール スクリプト、さらにはサーバーの性質を持つデーモン プロセスを作成することができます。
PHP でデーモン サーバーを作成する方法を示すために、PHP で単純な http サーバーを作成しました。このサーバーはデーモン プロセスとして実行されます。もちろん、PHP を使用してデーモンを作成する方法に重点を置くため、この http サーバーには特定のビジネス ロジックは実装しませんでしたが、指定されたポートをリッスンし、http リクエストを受け入れ、クライアントに固定テキストを返すことができます。プロセス全体はソケットを通じて実装されます。
このプログラムの完全なコードは次のとおりです:
//http クライアントリクエストを受け入れ、レスポンスコンテンツを生成します
//デモとして、この関数は「PHP HTTP Server」をクライアントに送信するだけです。
関数 handle_http_request($address, $port)
{
$max_backlog = 16;
$res_content = "HTTP/1.1 200 OK
コンテンツの長さ: 15
コンテンツタイプ: text/plain; charset=UTF-8
PHP HTTP サーバー";
$res_len = strlen($res_content);
//ソケットを作成、バインド、リッスンします
If(($socket =socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === FALSE)
{
echo "ソケットの作成に失敗しました!n";
終了します;
}
If((socket_bind($socket, $address, $port)) === FALSE)
{
echo "ソケットのバインドに失敗しました!n";
終了します;
}
If((socket_listen($socket, $max_backlog)) === FALSE)
{
echo "ソケットのリッスンに失敗しました!n";
終了します;
}
//ループ
その間(TRUE)
{
If(($accept_socket =ソケット_accept($socket)) === FALSE)
{
続ける;
}
その他
{
socket_write($accept_socket, $res_content, $res_len);
socket_close($accept_socket);
}
}
}
//デーモンプロセスとして実行します
関数 run()
{
If(($pid1 = pcntl_fork()) === 0)
//最初の子プロセス
{
posix_setsid() //最初の子プロセスをセッションリーダーとして設定します
;
If(($pid2 = pcntl_fork()) === 0)
//デーモンとして実行される 2 番目の子プロセス
{
//独自のドメインまたはアドレスに置き換えられます。
handle_http_request('www.codinglabs.org', 9999);
}
その他
{
//最初の子プロセスが終了します;
終了します;
}
}
それ以外
{
//最初の子プロセスが終了するまで待ちます;
pcntl_wait($status);
}
}
//エントリーポイント
run();
?>
ここでは、誰もが Unix 環境のプログラミングに精通していることを前提としているので、あまり詳しく説明せず、簡単に整理します。簡単に言うと、このプログラムは主に 2 つの部分で構成されています。handle_http_request 関数は、C で記述された tcp サーバーと同様に、ソケットを作成し、バインドし、リッスンし、各接続を処理します。クライアント側では、接続が受け入れられると、固定テキスト「PHP HTTP Server」が出力されます (もちろん、ここでは多重化と非ブロッキングは考慮されていません)。同期ブロッキング TCP サーバー。
run 関数は、プログラム全体をデーモン プロセスに変換する役割を果たします。このメソッドは、Unix 環境の C メソッドと非常によく似ており、最初のフォークの後に setid が呼び出され、子プロセス 1 がセッション リーダーに変換されます。そのため、子プロセス 2 は、祖先の切り離しとは異なり、祖先プロセスが終了しても (init プロセスに任せて) 実行を継続します。詳細については説明しません。Unix プロセスに詳しくない人は、『UNIX 環境における高度なプログラミング』という本を参照してください。
ここで、pcntl_fork は Unix の fork に対応し、pcntl_wait は wait に対応し、posix_setsid は setid に対応することに注意してください。その他の関数については、PHP マニュアルの pcntl および fork モジュールを参照してください。
コマンドラインからこのスクリプトを開始します:
php httpserver.php
ps コマンドを使用すると、デーモン プロセスが開始されたことがわかります:
|
1
|
|
ここでバインドするのはブログのドメイン名 www.codinglabs.org で、ポートは 9999 です。これは必要に応じて変更できます。
次に、curl コマンドを使用して、http サーバーが正常に実行されているかどうかを確認します。
問題ないようです。ブラウザで見てみましょう:
もちろん、このプログラムは実際の http サーバーではありません。デーモン プロセスとしても、実行ディレクトリの変更 (PHP の chroot を通じて実現できます)、シグナル バインディング、ログ機能など、多くの必要な機能が不完全です。利用できませんが、デモとしては、PHP が動的な Web ページ処理スクリプトを作成できるだけではないことを示すには十分です。興味のある友人がいる場合は、php を使用して、上で説明した機能をこの http サーバーに追加できます。
もう 1 つの注意点は、pcntl モジュールとソケット モジュールがデフォルトではインストールされないことです。PHP のインストール時にパラメータでインストールを指定しない場合は、これら 2 つの拡張モジュールを個別にインストールする必要があります。
codinglabs.orgからの抜粋