この記事は、Younes Rafieによってピアレビューされました。 SetePointコンテンツを最高にするためにSitePointのピアレビュアーのすべてに感謝します! Rubyコードの実行方法に関する最近の記事に触発されたこの記事では、PHPコードの実行プロセスについて説明しています。
キーテイクアウト
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>$tokens = token_get_all($code); </span></span><span> </span><span><span>foreach ($tokens as $token) { </span></span><span> <span>if (is_array($token)) { </span></span><span> <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL; </span></span><span> <span>} else { </span></span><span> <span>var_dump($token); </span></span><span> <span>} </span></span><span><span>} </span></span>
上記の出力から注目に値するポイントがいくつかあります。最初のポイントは、ソースコードのすべてのピースがトークンという名前ではないということです。代わりに、一部のシンボルは、それ自体のトークンと見なされます(=、;、:、?など)。 2番目のポイントは、Lexerが実際にトークンのストリームを単に出力するだけではないということです。また、ほとんどの場合、語彙素(トークンと一致する値)と一致したトークンのライン番号(スタックトレースのようなものに使用されます)を保存します。
パーサーも生成され、今回はBNF文法ファイルを介してバイソンを使用します。 PHPは、LALR(1)(先に見て、左から右へ)の文脈のない文法を使用します。先を見先の部分は、パーサーがnトークンを先に見ることができることを意味します(この場合、1、この場合)、解析中に遭遇する可能性のある曖昧さを解決することができます。左から右への部分は、トークンストリームを左から右へと解析することを意味します。
生成されたパーサーステージには、入力としてlexerからトークンストリームを取り、2つのジョブがあります。まず、BNF文法ファイルで定義されている文法規則のいずれかと一致させようとすることにより、トークン順序の有効性を検証します。これにより、有効な言語構造がトークンストリームのトークンによって形成されることが保証されます。パーサーの2番目のジョブは、抽象的構文ツリー(AST)を生成することです。次の段階で使用されるソースコードのツリービュー(コンパイル)。 PHP-AST拡張機能を使用してパーサーによって生成されるASTの形式を
初歩的なコードのASTを見てみましょう:
output:
Line 1: T_OPEN_TAG ('<?php ') Line 2: T_VARIABLE ('$a') Line 2: T_WHITESPACE (' ') string(1) "=" Line 2: T_WHITESPACE (' ') Line 2: T_LNUMBER ('1') string(1) ";"
種類 - ノードタイプを描写する整数値。それぞれに対応する定数があります(例:AST_STMT_LIST => 132、AST_ASSIGN => 517、AST_VAR => 256)
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>print_r(ast<span>\parse_code</span>($code, 30)); </span></span>
フラグ - 過負荷の動作を指定する整数(たとえば、ASTAST_BINARY_OPノードには、どのバイナリ操作が発生しているかを区別するフラグがあります)
コンピレーション段階はASTを消費し、ツリーを再帰的に横断することによりオペコードを放出します。この段階では、いくつかの最適化も実行されます。これらには、文字通りの引数(strlen( "abc")からint(3)などのいくつかの関数呼び出しを解決し、折り畳み一定の数学式(60 * 60 * 24からint(86400)など)。
OpCache、VLD、PHPDBGなど、さまざまな方法で、この段階でOPCODE出力を検査できます。出力がより友好的であると感じているので、これにVLDを使用します。次のfile.phpスクリプトの出力を見てみましょう。
次のコマンドを実行します:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>$tokens = token_get_all($code); </span></span><span> </span><span><span>foreach ($tokens as $token) { </span></span><span> <span>if (is_array($token)) { </span></span><span> <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL; </span></span><span> <span>} else { </span></span><span> <span>var_dump($token); </span></span><span> <span>} </span></span><span><span>} </span></span>
出力は
Line 1: T_OPEN_TAG ('<?php ') Line 2: T_VARIABLE ('$a') Line 2: T_WHITESPACE (' ') string(1) "=" Line 2: T_WHITESPACE (' ') Line 2: T_LNUMBER ('1') string(1) ";"
オプコードは、基本操作に従うのに十分な元のソースコードに似ています。 (この記事のオペコードの詳細については掘り下げるつもりはありません。それ自体がいくつかの記事全体を撮るからです。)上記のスクリプトのオペコードレベルで最適化は適用されませんでしたが、見ることができるように、コンパイルフェーズは定数条件(php_version === '7.1.0-dev')をtrueに解決することでいくつかを作成しました。
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>print_r(ast<span>\parse_code</span>($code, 30)); </span></span>
コマンド:
output:
ast\Node Object ( [kind] => 132 [flags] => 0 [lineno] => 1 [children] => Array ( [0] => ast\Node Object ( [kind] => 517 [flags] => 0 [lineno] => 2 [children] => Array ( [var] => ast\Node Object ( [kind] => 256 [flags] => 0 [lineno] => 2 [children] => Array ( [name] => a ) ) [expr] => 1 ) ) ) )
ステージ4 - 解釈
<span>if (PHP_VERSION === '7.1.0-dev') { </span> <span>echo 'Yay', PHP_EOL; </span><span>} </span>
この段階で複雑なものを掘り下げる代わりに、ここに面白い事実があります。PHPは、独自のVMを生成する際に依存関係として必要です。これは、VMがPHPスクリプトによって生成されたためです。
結論この記事が、PHPの通訳のより良い全体的な理解を提供するのに役立つことを願っています。また、Opcache拡張の重要性(キャッシュ能力と最適化能力の両方)を示しました。 PHP実行プロセスに関するよくある質問(FAQ)
PHPエンジンはどのように機能しますか? PHP実行プロセス。 PHPスクリプトを解析し、それをBytecodeにコンパイルし、ByteCodeを実行する責任があります。 PHPエンジンは、2段階のプロセスを使用してPHPスクリプトを実行します。まず、PHPスクリプトを解析し、それを抽象的構文ツリー(AST)に変換します。次に、ASTをBytecodeにコンパイルして実行します。 PHPエンジンには、実行プロセス中にメモリマネージャーとゴミコレクターも含まれています。 -lineインターフェイス(CLI)とWebサーバーインターフェイスは、PHPスクリプトを実行する2つの異なる方法です。 CLIはコマンドラインからPHPスクリプトの実行に使用され、WebサーバーインターフェイスはWeb要求に応じてPHPスクリプトを実行するために使用されます。 2つのインターフェイスの主な違いは、入力と出力を処理する方法です。 CLIでは、入力がコマンドラインから読み取り、出力がコンソールに書き込まれます。 Webサーバーインターフェイスでは、入力がHTTP要求から読み取り、出力はHTTP応答に書き込まれます。実行プロセス中にエラーを処理できるメカニズム。エラーが発生すると、PHPはエラーメッセージを生成し、エラーハンドラーに送信します。エラーハンドラーは、エラーの報告設定に応じて、エラーメッセージを表示したり、ログにしたり、無視したりできます。 PHPは例外処理もサポートしているため、より構造化された管理可能な方法でエラーを処理できます。
PHP拡張は、PHP言語に新しい機能と機能を追加するモジュールです。実行プロセス中にPHPランタイム環境にロードされ、データベースアクセスから画像処理まで、幅広いタスクを実行するために使用できます。 PHP拡張機能はCで記述され、マシンコードにコンパイルされるため、非常に高速で効率的になります。それらはPHPエコシステムの重要なコンポーネントであり、その柔軟性とパワーに貢献しています。これらの手法の1つは、PHPエンジンによって生成されたバイトコードをメモリに保存して、後続の実行で再利用できるようにすることを含むオペコードキャッシングです。これにより、PHPスクリプトが実行されるたびに解析してコンパイルする必要性がなくなり、パフォーマンスが大幅に向上します。 PHPはまた、ジャストインタイム(JIT)コンピレーションを使用します。これには、実行時にバイトコードをマシンコードにコンパイルしてパフォーマンスをさらに向上させます。 PHPには、実行プロセス中にメモリの割り当てと取引を処理するメモリマネージャーが組み込まれています。メモリマネージャーは、必要に応じて変数とデータ構造のメモリを割り当て、不要になったときにメモリを扱います。 PHPには、使用されなくなったメモリを自動的に解放するゴミコレクターもあります。これにより、メモリの漏れを防ぎ、メモリの使用量を制御し続けるのに役立ちます。プロセス。 HTTPリクエストの処理、これらの要求に応じてPHPスクリプトの実行、およびHTTP応答をクライアントに送信する責任があります。 Webサーバーは、PHPインタープリターとPHPエンジンと緊密に連携して、PHPスクリプトを実行して動的なWebページを生成します。 PHPで最も一般的に使用されるWebサーバーは、Apacheとnginxです。 mysql、postgresql、およびsqlite。データベース固有の拡張機能を使用して、実行プロセス中にこれらのデータベースと対話します。これらの拡張機能は、データベースに接続し、SQLクエリを実行し、結果を取得し、エラーを処理するために使用できる一連の機能を提供します。 PHPは、PDO(PHPデータオブジェクト)拡張機能もサポートしています。これは、データベースインタラクションのデータベース存在インターフェイスを提供します。
PHPにはセッション管理のサポートが組み込まれているため、異なるHTTP要求間で状態を維持できます。セッションが開始されると、PHPは一意のセッションIDを作成し、クライアントのブラウザのCookieに保存します。このセッションIDは、その後のリクエストごとにサーバーに送信され、PHPがクライアントを識別し、対応するセッションデータを取得できるようにします。 PHPのセッション管理機能により、ユーザー認証、ショッピングカート、その他のステートフルな機能をWebアプリケーションに簡単に実装できます。
以上がPHPの実行方法 - ソースコードからレンダリングまでの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。