ホームページ  >  記事  >  バックエンド開発  >  PHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分です

PHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分です

王林
王林転載
2019-08-24 09:51:012668ブラウズ

PHP は簡単だと言われていますが、使いこなすのは簡単ではありません。それを使用できることに加えて、その根本的な動作原理を知る必要もあります。

PHP は、Web 開発に適した動的言語です。具体的には、C言語を使用して多数のコンポーネントを実装するソフトウェアフレームワークです。より狭い意味で見ると、強力な UI フレームワークと考えることができます。

PHP の基礎となる実装を理解する目的は何ですか?動的言語を使いこなすには、まず動的言語を理解する必要があります。メモリ管理とフレームワーク モデルは参考になります。拡張開発を通じて、より強力な機能を実現し、プログラムのパフォーマンスを最適化できます。

1. PHP の設計思想と特徴

マルチプロセスモデル: PHP はマルチプロセスモデルであるため、異なるリクエストが互いに干渉することはなく、これにより、1 つのリクエストがハングすることが保証され、損失がサービス全体に影響を与えることはありません。もちろん、時代の発展に伴い、PHP はすでにマルチスレッド モデルをサポートしています。型付けが弱い言語: C/C、Java、C#、その他の言語とは異なり、PHP は型付けが弱い言語です。変数の型は最初に決定されるのではなく、動作中に決定され、暗黙的または明示的な型変換が発生する可能性があります。この仕組みの柔軟性は、Web 開発において非常に便利で効率的です。詳細は PHP で説明します。変数に詳しく記載されています。エンジン (Zend) コンポーネント (ext) パターンにより、内部カップリングが軽減されます。中間層 (sapi) は Web サーバーと PHP を分離します。構文はシンプルで柔軟であり、あまり多くの仕様はありません。欠点があるとスタイルが混在しますが、プログラマがどんなに下手でも、突飛すぎて全体の状況を危険にさらすようなプログラムは書きません。

2. PHP の 4 層システム

PHP のコア アーキテクチャは次のとおりです。写真から、PHP は下から上まで 4 層のシステムであることがわかります:

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

PHP が車だとすると、車のフレームは PHP そのもの、Zend は車のエンジン (エンジン)、Ext 以下のさまざまなコンポーネントは車の車輪に相当します。車はさまざまな種類の道路を走行でき、PHP プログラムを実行することで車が道路を走行できます。したがって、必要なのは、優れたエンジン、適切なホイール、適切なトラックです。

3. Sapi

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

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

4. PHP 実行プロセス&opcode

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

PHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分です

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

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

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

いくつかの共通処理関数:

PHP

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. HashTable - コア データ構造

HashTable は zend のコア データ構造です。PHP のほぼすべての一般的な関数を実装するために使用されます。私たちが知っている PHP 配列は、その構造です。さらに、zend 内の関数シンボル テーブル、グローバル変数などの典型的なアプリケーションもハッシュ テーブルに基づいて実装されます。

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

1. 一般的なキー->値クエリをサポート

2. 配列として使用可能

3 、ノードの追加と削除は O(1) の複雑さです。

4. キーは混合型をサポートします: 連想配列とインデックス配列の両方があります。

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

6. 線形トラバーサルのサポート: たとえば、foreach

Zend ハッシュ テーブルは、典型的なハッシュ テーブルのハッシュ構造を実装し、同時に二重にアタッチすることで順方向と逆方向を提供します。リンク リスト 配列を走査する関数。その構造は以下のとおりです。

PHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分です

ハッシュ テーブルには、キー -> 値の形式と二重のハッシュ構造の両方が存在することがわかります。リンク リスト モードにより、高速検索と線形トラバースを非常に便利にサポートできます。

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

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

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

PHP

getKeyHashValue h;
index = n & nTableMask;
Bucket *p = arBucket[index];
while (p) {
if ((p->h == h) & (p->nKeyLength == nKeyLength)) {
RETURN p->data;  
}
p=p->next;
}
RETURN FALTURE;

PHP インデックス配列:

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

6. PHP 変数

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

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

PHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分です

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

type: 変数のタイプ (整数、文字列、配列など) を指定します。

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

value: 変数の実際のデータを保存するコア部分

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

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

1, IS_LONG -> lvalue

2, IS_DOUBLE -> dvalue

3、IS_ARRAY -> ht

4、IS_STRING -> str

5 , IS_RESOURCE -> lvalue

参照カウントは、メモリのリサイクル、文字列操作などで広く使用されています。 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 つの型はlongです。このことから、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;

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

#PHP

$res = $strA.$strB および $res = “$strA$strB”

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

$strA = $strA.$strB

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

$res = $ intA.$intB

暗黙的な形式変換が必要なため、これは遅くなります。実際のプログラミングでは、

$strA = sprintf ( "%s%s",$ を避けることにも注意する必要があります) strA.$strB);

sprintf は 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 中国語 Web サイトをご覧ください: PHP 実践ビデオ チュートリアル

以上がPHP の基礎となる動作メカニズムと原理を理解するにはどうすればよいでしょうか?この記事を理解すれば十分ですの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。