ホームページ  >  記事  >  バックエンド開発  >  PHPの基本的な動作メカニズムの詳細な説明

PHPの基本的な動作メカニズムの詳細な説明

小云云
小云云オリジナル
2018-03-21 15:43:421231ブラウズ

この記事では、主に PHP の基本的な動作メカニズムについて詳しく説明します。まず、PHP の設計概念や特徴、PHP の 4 層システムなどについて説明します。助けます。

1. PHP の設計概念と特徴

  1. マルチプロセス モデル: PHP はマルチプロセス モデルであるため、1 つのリクエストの失敗が他のリクエストに影響を与えることはありません。もちろん、サービス全体としては、時代の発展に伴い、PHP はすでにマルチスレッド モデルをサポートしています。

  2. 型の弱い言語: C/C++、Java、C#、その他の言語とは異なり、PHP は型の弱い言語です。変数の型は最初は決定されず、暗黙的または明示的な型変換が行われるため、Web 開発では非常に便利で効率的です。詳細については、「変数」で説明します。に詳しく記載されています。

  3. インタープリタ型言語: PHP には C/C++、Java、C# などのコンパイル済み言語とは実行手順が異なるため、実行前に字句解析と文法解析を通じてコン​​パイル済み言語に解析する必要があります。したがって、PHPは、ブラウザユーザーにとっては0.001秒と0.1秒の差はありませんが、高性能やビッグデータ計算などの大規模なアプリケーションには適していません

  4. エンジン(Zend) + コンポーネント (ext) モードは内部結合を軽減します。

  5. 中間層 (sapi) は、Web サーバーと PHP を分離します。

  6. 構文はシンプルかつ柔軟で、あまり多くの仕様はありません。欠点があるとスタイルが混在しますが、プログラマがどんなに下手でも、突飛すぎて全体の状況を危険にさらすようなプログラムは書きません。

2. PHP の 4 層システム

PHP のコア アーキテクチャは次のとおりです:

写真からわかるように、PHP は下から上に 4 層システムです。

    Zend エンジン: Zend は完全に純粋な C で実装されており、PHP コード (字句解析や構文解析などの一連のコンパイル プロセス) を実行可能なオペコード処理に変換し、対応する処理メソッドと基本データを実装します。構造体 (ハッシュテーブル、OO など)、メモリ割り当てと管理、および外部呼び出しに対応する API メソッドを提供します。これは、すべての周辺機能が Zend を中心に実装されています。
  • 拡張機能: Zend エンジンを中心として、拡張機能はコンポーネントベースの方法でさまざまな基本サービスを提供します。ユーザーは、一般的なさまざまな組み込み関数 (配列シリーズなど)、標準ライブラリなどをすべて拡張機能を通じて実装できます。機能拡張やパフォーマンスの最適化などの目的を達成するには、独自の拡張機能を実装する必要があります (たとえば、現在 Tieba で使用されている PHP 中間層やリッチ テキスト解析は、拡張機能の代表的なアプリケーションです)。
  • Sapi: Sapi の正式名はサーバー アプリケーション プログラミング インターフェイスであり、Sapi は一連のフック関数を使用して PHP が周辺データと対話できるようにします。これは非常にエレガントで成功した設計です。 PHP. sapi を介して PHP 自体を上位層のアプリケーションから分離および分離することに成功すると、PHP は異なるアプリケーションとの互換性を考慮する必要がなくなり、アプリケーション自体も独自の特性に基づいて異なる処理方法を実装できます。
  • 上位層アプリケーション: これは、私たちが通常作成する PHP プログラムであり、Web サーバーを介して Web アプリケーションを実装したり、コマンド ラインでスクリプト モードで実行したりするなど、さまざまな SAPI メソッドを通じてさまざまなアプリケーション モードを取得できます。
  • PHPが車なら、車のフレームはPHPそのもの、Zendは車のエンジン(エンジン)、Ext以下の様々なコンポーネントは車の車輪、Sapiは道路とみなすことができます、車はさまざまな種類の道路を走ることができ、PHP プログラムの実行は車が道路上を走っていることを意味します。したがって、高性能エンジン + 適切なホイール + 適切なトラックが必要です。

3. Sapi

前述したように、Sapi を使用すると、外部アプリケーションが一連のインターフェイスを通じて PHP とデータを交換し、さまざまなアプリケーションの特性に応じて特定の処理メソッドを実装できます。

    apache2handler: これ。 WebサーバーとしてApacheを使用し、mod_PHPモードで実行する場合の処理​​方法でもあり、現在最も広く使用されています。
  • cgi: これは、有名な fastcgi プロトコルである Web サーバーと PHP の間の別の直接対話方法であり、近年、fastcgi+PHP がますます使用されており、非同期 Web サーバーでサポートされている唯一の方法でもあります。
  • cli: コマンドライン呼び出し用のアプリケーションモード
  • 4. PHP 実行プロセスとオペコード

まず、PHP コードを実行するプロセスを見てみましょう。

図からわかるように、PHP は典型的な動的言語の実行プロセスを実装しています。コードの一部を取得した後、字句解析、構文解析、その他の段階を経て、ソース プログラムが命令 (オペコード) に変換され、その後 ZEND が実行されます。仮想マシンはこれらの命令を順番に実行して操作を完了します。 PHP 自体は C で実装されているため、最終的に呼び出される関数はすべて C の関数です。実際には、PHP は C で開発されたソフトウェアと考えることができます。

PHP 実行の中核は、オペコードである翻訳された命令です。

オペコードは、PHP プログラム実行の最も基本的な単位です。オペコードは 2 つのパラメータ (op1、op2)、戻り値、および処理関数で構成されます。 PHP プログラムは最終的に、一連のオペコード処理関数の順次実行に変換されます。

いくつかの一般的な処理関数:

PHP


5. HashTable - コアデータ構造

HashTable は、zend のほぼすべての一般的な関数を実装するために使用されます。関数シンボル テーブルやグローバル変数などもハッシュテーブルに基づいて実装されます。

PHPのハッシュテーブルには以下の特徴があります:

  • 典型的なkey->valueクエリをサポート

  • 配列として使用可能

  • ノードの追加と削除はO(1)の複雑さ

  • キー混合型をサポートします: 同時に連想数値の組み合わせのインデックス配列があります

  • 値は混合型をサポートします: array ("string", 2332)

  • 線形トラバーサルをサポートします: foreach など

Zend ハッシュ テーブル典型的なハッシュ テーブルのハッシュ構造を実装します。また、二重リンク リストを追加することにより、配列の順方向および逆方向の走査機能も提供します。その構造は以下のとおりです:

ご覧のとおり、ハッシュ テーブルにはキー -> 値の形式のハッシュ構造と二重リンク リスト モードの両方があり、高速検索と線形検索をサポートするのに非常に便利です。横断。

  • ハッシュ構造: Zend のハッシュ構造は典型的なハッシュ テーブル モデルであり、リンク リストを通じて競合を解決します。 Zend のハッシュ テーブルは自己成長するデータ構造であり、ハッシュ テーブルの数がいっぱいになると、動的に 2 倍に拡張され、要素の位置が変更されることに注意してください。初期サイズは8です。さらに、キー->値の高速検索を実行する場合、zend 自体も空間を時間に交換することでプロセスを高速化するいくつかの最適化を行っています。たとえば、変数 nKeyLength は各要素で使用され、キーの長さを識別して迅速に決定します。

  • ダブルリンクリスト: Zend ハッシュテーブルは、リンクリスト構造を介した要素の線形走査を実装します。理論的には、走査には一方向リンク リストを使用するだけで十分です。二重リンク リストを使用する主な目的は、迅速に削除して走査を回避することです。 Zend ハッシュ テーブルは複合構造であり、配列として使用すると、一般的な連想配列をサポートし、連続したインデックス番号として使用したり、2 つを混合したりすることもできます。

  • PHP 連想配列: 連想配列は典型的な hash_table アプリケーションです。クエリ プロセスは次の手順を実行します (コードからわかるように、これは一般的なハッシュ クエリ プロセスに、検索を高速化するためにいくつかの素早い決定が追加されています)。

    2
3

4

1

2

3

4

5

6

ZEND_ASSIGN_SPEC_CV _CV_HANDLER: 変数の割り当て ($a=$b)

ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER: 関数呼び出し

ZEND_CONCAT_SPEC_CV_CV_HANDLER: 文字列連結 $a.$b

ZEND_ADD_SPEC_CV_CONST_HANDLER: 加算 $a+2

ZEND_IS_EQUAL_SPEC_CV_CONST: 等しいかどうか $a ==1

ZEND_IS_IDENTICAL_SPEC_CV_CONST: $a===1 に等しいと判断します

5
6

7

8

9

10

getKeyHashValueh;

index=n&nTableMask;

Bucket*p=arBuck et[index];

while(p) {

if((p->h==h)&(p->nKeyLength==nKeyLength)){
RETURNp->データ

}

p=p ->次;

}

リターンファルチャー;


  • PHP インデックス配列: インデックス配列は、添え字を通じてアクセスされる共通の配列です。たとえば、$arr[0]、Zend HashTable は内部で正規化されており、インデックス型キーにもハッシュ値と nKeyLength (0) が割り当てられています。内部メンバー変数 nNextFreeElement は現在割り当てられている最大 ID であり、プッシュするたびに自動的に 1 ずつ増加します。この正規化プロセスにより、PHP は連想データと非連想データの混合を実現できます。プッシュ操作の特殊性により、PHP 配列内のインデックス キーの順序は添え字のサイズではなく、プッシュの順序によって決まります。例えば $arr[1] = 2; $arr[2] = 3; double 型のキーの場合、Zend HashTable はそれらをインデックス キーとして扱います

  • PHP 自体は弱い型付けの言語です。変数は厳密には区別されません。 PHP では、変数を宣言するときに型を指定する必要はありません。 PHP は、プログラムの実行中に変数の型の暗黙的な変換を実行する場合があります。他の厳密に型指定された言語と同様に、プログラム内で明示的な型変換を実行することもできます。 PHP 変数は、単純型 (int、string、bool)、コレクション型 (配列リソース オブジェクト)、および定数 (const) に分類できます。上記の変数はすべて、内部では同じ構造 zval を持ちます。

Zval は、zend のもう 1 つの非常に重要なデータ構造で、PHP 変数の識別と実装に使用されます。そのデータ構造は次のとおりです:

Zval は主に 3 つの部分で構成されます:

type: Type で記述される変数を指定します。 (整数、文字列、配列など)

  • refcount&is_ref: 参照カウントの実装に使用されます (詳細は後述)

  • value: 変数

  • Zvalue の実際のデータを格納するコア部分used 変数の実データを保存します。複数の型を格納する必要があるため、zvalue は共用体であり、弱い型付けが実装されています。

  • PHP 変数の型と実際のストレージの対応は次のとおりです。

IS_DOUBLE -> ; dvalue

IS_ARRAY -> ht

IS_STRING -> 左辺値

参照カウントは、メモリのリサイクル、文字列操作などで広く使用されています。 PHP の変数は、参照カウントの典型的なアプリケーションです。 Zval の参照カウントは、メンバー変数 is_ref および ref_count によって実装されます。参照カウントを通じて、複数の変数が同じデータを共有できます。頻繁なコピーによる大量の消費を避けてください。

代入操作を実行するとき、zend は変数を同じ zval と ref_count++ に指し、設定解除操作中は対応する ref_count-1 を指します。破棄操作は、ref_count が 0 に減少した場合にのみ実行されます。参照割り当ての場合、zend は is_ref を 1 に変更します。

PHP 変数は参照カウントを通じて変数共有データを実現しますが、変数の 1 つの値を変更した場合はどうなるでしょうか?変数を書き込もうとするときに、その変数が指す zval が複数の変数で共有されていることがわかると、Zend は ref_count が 1 の zval をコピーし、元の zval の refcount をデクリメントします。このプロセスは「zval 分離」と呼ばれます。 」。 zend は書き込み操作が発生したときにのみコピー操作を実行するため、参照変数の場合、要件は非参照型とは逆であり、参照によって割り当てられる変数はバンドルされている必要があります。 1 つの変数を変更すると、バンドルされたすべての変数が変更されます。

整数と浮動小数点数は、PHP の基本的な型の 1 つであり、単純な変数でもあります。整数と浮動小数点数の場合、対応する値は zvalue に直接格納されます。タイプはそれぞれlongとdoubleです。

整数型の場合、C などの厳密に型指定された言語とは異なり、PHP では int、unsigned int、long、long long などの型が 1 つだけであることがわかります。整数型です。このことから、PHP では、整数の値の範囲はコンパイラのビット数によって決まり、固定されていないことがわかります。

浮動小数点数の場合、整数と同様に、float と double は区別されず、double のみが区別されます。

PHP では、整数の範囲が範囲外になった場合はどうすればよいですか?この場合、自動的に double 型に変換されてしまいますので、これが原因で起こるトリックが多いので注意が必要です。

整数と同様、文字変数も PHP の基本型であり単純な変数です。 zvalue 構造から、PHP の文字列は、C++ の文字列と同様に、実際のデータへのポインターと長さ構造で構成されていることがわかります。 c とは異なり、長さは実際の変数で表されるため、その文字列はバイナリ データ (両端を含む) にすることができます。同時に、PHP では、文字列の長さ strlen を見つけるのは O(1) 操作です。

文字列操作を追加、変更、または追加すると、PHP はメモリを再割り当てして新しい文字列を生成します。最後に、セキュリティ上の理由から、PHP は文字列生成時に

共通の文字列結合メソッドと速度比較を最後に追加します:

次の 4 つの変数があるとします。 $strA='123'; $strB = ' 456'; ; $intA=123; intB=456;

次に、次の文字列の結合方法を比較して説明します。

7

8

$res=$strA.$strB および $res="$strA$strB"

この場合、zend はメモリの一部を再度割り当てて、それに応じて処理します。その速度は一般的に

$strA=$strA.$strB

これが最速です。zend はコピーの繰り返しを避けるために、現在の strA に基づいて直接再割り当てします

$res=$intA.$intB

これは暗黙的な形式であるため、速度は遅くなります実際のプログラミングでは、

$strA=sprintf(“%s%s”,$strA.$strB);

これは最も遅い方法です。 PHP のような言語構造ではなく、形式の特定と処理に時間がかかります。また、仕組み自体が malloc です。ただし、sprintf メソッドは最も可読性が高く、実際には特定の状況に応じて柔軟に選択できます。


PHP 配列は、当然 Zend HashTable を通じて実装されます。

foreach 操作を実装するにはどうすればよいですか?配列の Foreach は、ハッシュテーブル内の二重リンク リストを走査することによって完了します。インデックス配列の場合、foreach を介した走査は for よりもはるかに効率的であり、キー→値を検索する必要がなくなります。 count オペレーションは、HashTable->NumOfElements, O(1) オペレーションを直接呼び出します。 「123」のような文字列の場合、zend はそれを整数形式に変換します。 $arr[‘123’] と $arr[123] は同等です

リソースタイプ変数は、PHP で最も複雑な変数であり、複合構造でもあります。

PHP の zval は幅広いデータ型を表すことができますが、カスタム データ型を完全に記述することは困難です。これらの複合構造を表現する効率的な方法がないため、それらに対して従来の演算子を使用する方法はありません。この問題を解決するには、リソースと呼ばれる本質的に任意の識別子 (ラベル) を介してポインターを参照するだけで済みます。

zval では、リソースに対して、lval がリソースのアドレスを直接指すポインターとして使用されます。リソースは任意の複合構造にすることができます。よく知られている mysqli、fsock、memcached などはすべてリソースです。

リソースの使用方法:

  • 登録: カスタム データ型の場合、それをリソースとして使用します。まず、これを登録する必要があります。そうすれば、zend はそれにグローバルに一意の識別子を割り当てます。

  • リソース変数を取得する: リソースに関して、zend は実際のデータの id->hash_tale を維持します。リソースの場合、その ID のみが zval に記録されます。フェッチするときは、id を通じて hash_table 内の特定の値を検索し、それを返します。

  • リソースの破壊: リソースのデータ型は多様です。 Zend 自体にはそれを破壊する方法はありません。したがって、ユーザーはリソースを登録する際に破棄機能を提供する必要があります。リソースの設定が解除されると、zend は対応する関数を呼び出して破棄を完了します。グローバルリソーステーブルからも削除してください。

リソースは、それを参照しているすべての変数がスコープ外になった後だけでなく、リクエストが終了して新しいリクエストが生成された後でも、長期間存続することができます。これらのリソースは、特に破棄されない限り、SAPI のライフサイクル全体にわたって存続するため、永続リソースと呼ばれます。多くの場合、永続リソースによりパフォーマンスがある程度向上します。たとえば、一般的な mysql_pconnect では、永続リソースは pemalloc を通じてメモリを割り当て、リクエストが終了しても解放されないようにします。
zend の場合、2 つの区別はありません。

PHP ではローカル変数とグローバル変数はどのように実装されますか?リクエストの場合、PHP はいつでも 2 つのシンボル テーブル (symbol_table と active_symbol_table) を参照でき、前者はグローバル変数を維持するために使用されます。後者は、現在アクティブな変数シンボル テーブルを指すポインターです。プログラムが関数に入ると、zend はシンボル テーブル x をそれに割り当て、active_symbol_table を a に指します。このようにして、グローバル変数とローカル変数の区別が行われます。

変数値の取得: PHP のシンボル テーブルは hash_table を通じて実装され、取得時に、対応する zval が識別子に従ってテーブルから検索され、返されます。

関数でグローバル変数を使用する: 関数では、明示的に global を宣言することでグローバル変数を使用できます。 active_symbol_tableのsymbol_tableに同じ名前の変数への参照を作成します。symbol_tableに同じ名前の変数が存在しない場合は、それが最初に作成されます。

PHP 実行メカニズムのこのプロセス:

1. PHP 関連のプロセスを手動で開始したことはありません。Apache の起動時に実行されます。2. PHP は mod_php5.so モジュールを通じて Apache に接続されます (サーバー アプリケーション プログラミング インターフェイス);

3. PHP には、カーネル、Zend エンジン、拡張層の 3 つのモジュールがあります。

4. PHP カーネルは、リクエスト、ファイル ストリーム、その他の関連操作を処理するために使用されます。 5. Zend Engine (ZE) は、ソース ファイルをマシン語に変換し、仮想マシン上で実行するために使用されます。

6. 拡張レイヤーは、PHP が特定の操作を実行するために使用する関数、クラス ライブラリ、およびストリームのセットです。 。たとえば、MySQL データベースに接続するには mysql 拡張機能が必要です。

7. ZE がプログラムを実行するとき、ZE は制御を拡張機能に渡し、その後それを返します。特定のタスクを処理します。

8. 最後に、ZE はプログラムの実行結果を PHP カーネルに返し、PHP カーネルはその結果を SAPI 層に送信し、最終的にブラウザに出力します。

PHP の動作メカニズムについての詳細な説明

PHP の動作メカニズム - PHP 起動の最初のステップ

最初のステップと 2 番目のステップが何かわからないですか?心配しないでください。これについては次に詳しく説明します。

最初の最も重要なステップを見てみましょう。覚えておくべきことは、操作の最初のステップはリクエストが到着する前に行われるということです。 Apache の起動後、PHP インタープリタも起動し、PHP は各拡張機能の MINIT メソッドを呼び出し、これらの拡張機能を使用可能な状態に切り替えます。 php.ini ファイルでどのような拡張子が開かれているかを確認してください。MINIT は「モジュールの初期化」を意味します。各モジュールは、他のリクエストを処理するための関数、クラス ライブラリなどのセットを定義します。

典型的な MINIT メソッドは次のとおりです:

PHP_MINIT_FUNCTION(extension_name){ /* 関数、クラスなどを初期化します */ }

PHP の動作メカニズム: PHP 起動の 2 番目のステップ

ページリクエストが発生すると、SAPI 層は制御を PHP 層に渡します。したがって、PHP は、このリクエストに応答するために必要な環境変数を設定します。同時に、実行中に生成された変数名と値を保存する変数テーブルも作成します。 PHP は各モジュールの RINIT メソッド、つまり「リクエストの初期化」を呼び出します。典型的な例はセッション モジュールの RINIT です。セッション モジュールが php.ini で有効になっている場合、$_SESSION 変数は初期化され、モジュールの RINIT メソッドが呼び出されたときに関連するコンテンツが読み込まれます。準備プロセスはプログラムの実行の間に自動的に開始されます。

典型的な RINIT メソッドは次のとおりです:

PHP_RINIT_FUNCTION(extension_name) { /* セッション変数の初期化、変数の事前設定、グローバル変数の再定義など */ }

PHP 操作メカニズムにおける PHP シャットダウンの最初のステップ

はPHP の起動と同様に、PHP は 2 つの手順でシャットダウンします。ページが実行されると (ファイルの最後に到達するか、exit または die 関数で終了するかに関係なく)、PHP はクリーンアップ プログラムを開始します。各モジュールの RSHUTDOWN メソッドを順番に呼び出します。 RSHUTDOWN は、プログラムの実行中に生成されたシンボル テーブルをクリアする、つまり各変数の unset 関数を呼び出すために使用されます。

典型的な RSHUTDOWN メソッドは次のとおりです:

PHP_RSHUTDOWN_FUNCTION(extension_name) { /* メモリ管理を行い、最後の PHP 呼び出しで使用されたすべての変数の設定を解除します。など */ }

PHP 動作メカニズムの PHP シャットダウンの 2 番目のステップ

最後に、すべてのリクエストが処理され、SAPI を閉じる準備が整い、PHP は 2 番目のステップの実行を開始します。PHP は各拡張機能の MSHUTDOWN メソッドを呼び出します。これは、各モジュールがメモリを解放する最後の機会です。

典型的な RSHUTDOWN メソッドは次のとおりです:

PHP_MSHUTDOWN_FUNCTION(extension_name) { /* ハンドラーや永続メモリなどを解放します */ }

このようにして、PHP のライフサイクル全体が終了します。なお、「第1ステップの開始」と「第2ステップの終了」はサーバからのリクエストがない場合のみ実行されます。

関連する推奨事項:

JavaScript クロージャの基礎となる動作メカニズム コードの詳細な説明

JavaScript クロージャの基礎となる動作メカニズム

PHP_PHP チュートリアルの基礎となる動作メカニズムを探索する

以上がPHPの基本的な動作メカニズムの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。