PHP は簡単だと言われていますが、使いこなすのは簡単ではありません。それを使用できることに加えて、その根本的な動作原理を知る必要もあります。
PHP は Web 開発に適した動的言語です。具体的には、C言語を使用して多数のコンポーネントを実装するソフトウェアフレームワークです。より狭い意味で見ると、強力な UI フレームワークと考えることができます。
PHP の基礎となる実装を理解する目的は何ですか?動的言語をうまく使用するには、まずメモリ管理とフレームワーク モデルを理解する必要があります。拡張された開発を通じて、より強力な機能を実現し、プログラムのパフォーマンスを最適化できます。
1. PHP の設計概念と特徴
マルチプロセス モデル: PHP はマルチプロセス モデルであるため、異なるリクエストは相互に干渉せず、1 つのリクエストの失敗が影響を与えることはありません。もちろん、時代の発展に伴い、PHP はすでにマルチスレッド モデルをサポートしています。
弱い型指定言語: C/C++、Java、C#、その他の言語とは異なり、PHP は弱い型指定言語です。変数の型は最初は決定されず、暗黙的または明示的な型変換が行われるため、Web 開発では非常に便利で効率的です。詳細については、「変数」で説明します。に詳しく記載されています。
エンジン (Zend) + コンポーネント (ext) モードは、内部結合を軽減します。
中間層 (sapi) は Web サーバーと PHP を分離します。
構文はシンプルで柔軟であり、あまり多くの仕様はありません。欠点があるとスタイルが混在しますが、プログラマがどんなに下手でも、突飛すぎて全体の状況を危険にさらすようなプログラムは書きません。
2. PHP の 4 層システム
PHP のコア アーキテクチャは次のとおりです:
写真からわかるように、PHP は下から上に 4 層システムです:
Zend エンジン: Zend は完全に純粋な C で実装されており、PHP コード (字句解析、構文解析、その他のコンパイル プロセス) を実行可能なオペコード処理に変換し、対応する処理メソッドと基本的なデータ構造を実装します。ハッシュテーブル、oo)、メモリの割り当てと管理、外部呼び出しに対応する API メソッドの提供など、すべての周辺機能が Zend を中心に実装されています。
拡張機能: Zend エンジンを中心に、拡張機能はコンポーネントベースの方法でさまざまな基本サービスを提供します。ユーザーは、一般的なさまざまな組み込み関数 (配列シリーズなど)、標準ライブラリなどをすべて拡張機能を通じて実装できます。機能拡張やパフォーマンスの最適化などの目的を達成するには、独自の拡張機能を実装する必要があります (たとえば、現在 Tieba で使用されている PHP 中間層やリッチ テキスト解析は、拡張機能の代表的なアプリケーションです)。
Sapi: Sapi の正式名はサーバー アプリケーション プログラミング インターフェイスです。これは、PHP が一連のフック関数を通じて周辺データと対話できるようにする、非常にエレガントで成功した PHP の設計です。 . sapi を通じて PHP 自体を上位層のアプリケーションから切り離し、分離することに成功すると、PHP は異なるアプリケーションとの互換性を考慮する必要がなくなり、アプリケーション自体も独自の特性に基づいて異なる処理方法を実装できます。
上位層アプリケーション: これは、私たちが通常作成する PHP プログラムであり、Web サーバーを介して Web アプリケーションを実装したり、コマンド ラインでスクリプト モードで実行したりするなど、さまざまな SAPI メソッドを通じてさまざまなアプリケーション モードを取得します。
PHP が車だとすると、車のフレームは PHP そのもの、Zend は車のエンジン (エンジン)、Ext の下にあるさまざまなコンポーネントは車の車輪、Sapi は道路とみなすことができます、車はさまざまな種類の道路を走ることができ、PHP プログラムを実行すると車が道路を走ります。したがって、高性能エンジン + 適切なホイール + 適切なトラックが必要です。
3. Sapi
前述したように、Sapi を使用すると、外部アプリケーションが一連のインターフェイスを通じて PHP とデータを交換し、さまざまなアプリケーションの特性に応じて特定の処理メソッドを実装できます。 : これは、Apache を Web サーバーとして使用し、mod_PHP モードで実行する場合の処理方法であり、現在最も広く使用されています。
cgi: これは、有名な fastcgi プロトコルである Web サーバーと PHP の間の別の直接対話方法であり、近年、fastcgi+PHP がますます使用されており、非同期 Web サーバーでサポートされている唯一の方法でもあります。
cli: コマンドライン呼び出し用のアプリケーションモード
まず、PHP コードを実行するプロセスを見てみましょう。
図からわかるように、PHP は典型的な動的言語実行プロセスを実装しています。コードの一部を取得した後、字句解析、構文解析、その他の段階を経て、ソース プログラムが命令 (オペコード) に変換されます。その後、ZEND 仮想マシンはこれらの命令を順番に実行して操作を完了します。 PHP 自体は C で実装されているため、最終的に呼び出される関数はすべて C の関数です。実際には、PHP は C で開発されたソフトウェアと考えることができます。
PHP 実行の中核は、オペコードである翻訳された命令です。
オペコードは、PHP プログラム実行の最も基本的な単位です。オペコードは 2 つのパラメータ (op1、op2)、戻り値、および処理関数で構成されます。 PHP プログラムは最終的に、一連のオペコード処理関数の順次実行に変換されます。
いくつかの一般的な処理関数:
1 | ZEND_ASSIGN_SPEC_CV_CV_HANDLER: 変数割り当て ($a=$b) |
2 |
ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER: 関数呼び出し |
3 | ZEND_CONCAT_SPEC_CV_CV_HANDLER: 文字列連結 $a.$b |
4 | ZEND_ADD_SPEC_CV_CONST_HANDLER: 加算 $a+2 |
5 | ZEND_IS_EQUAL_SPEC_CV_CONST: $a==1 に等しいと判定します |
6 |
ZEND_IS_IDENTICAL_SPEC_CV_CONST: $a ===1 に等しいと判定します |
5. ハッシュテーブル — コアデータ構造
HashTable は、zend のコアとなるデータ構造であり、関数シンボル テーブル、グローバル変数など、PHP の一般的な関数の実装に使用されます。ハッシュテーブルに基づいて実装されます。
PHP のハッシュ テーブルには次の機能があります:
一般的な key->value クエリをサポート
配列として使用可能
ノードの追加と削除の複雑さは O(1) です
キーは混合型をサポートします: 同時に連想数値の組み合わせのインデックス配列があります
値は混合型をサポートします: array ("string", 2332)
線形トラバーサルをサポートします: foreach
Zend ハッシュなどtable は典型的なハッシュ テーブルを実装します。ハッシュ テーブルのハッシュ構造は、二重リンク リストを追加することによって、配列の順方向および逆方向の走査機能も提供します。その構造は以下のとおりです:
ご覧のとおり、ハッシュ テーブルにはキー -> 値の形式のハッシュ構造と二重リンク リスト モードの両方があり、高速検索と線形トラバーサルをサポートするのに非常に便利です。 。
ハッシュ構造: Zend のハッシュ構造は典型的なハッシュ テーブル モデルであり、リンク リストを通じて競合を解決します。 Zend のハッシュ テーブルは自己成長するデータ構造であり、ハッシュ テーブルの数がいっぱいになると、動的に 2 倍に拡張され、要素の位置が変更されることに注意してください。初期サイズは8です。さらに、キー->値の高速検索を実行する場合、zend 自体も空間を時間に交換することでプロセスを高速化するいくつかの最適化を行っています。たとえば、変数 nKeyLength は各要素で使用され、キーの長さを識別して迅速に決定します。
二重リンク リスト: Zend ハッシュ テーブルは、リンク リスト構造による要素の線形走査を実装します。理論的には、走査には一方向のリンク リストを使用するだけで十分です。双方向のリンク リストを使用する主な目的は、迅速に削除して走査を回避することです。 Zend ハッシュ テーブルは複合構造であり、配列として使用すると、一般的な連想配列をサポートし、連続したインデックス番号として使用したり、2 つを混合したりすることもできます。
PHP 連想配列: 連想配列は典型的な hash_table アプリケーションです。クエリ プロセスは次の手順を実行します (コードからわかるように、これは一般的なハッシュ クエリ プロセスに、検索を高速化するためにいくつかの素早い判断が追加されています)。 ;
| 02
| index = n & nTableMask;
| 03
| バケット *p = arBucket[index];
| 04while(p){ |
05 | if((p-> h == h)&&(p-> nkeylength == nkeylength){ |
06 |
| p=p->next;
|
| }
|
| リターンフォールチャー;
|
3 | IS_ARRAY -> |
参照カウントは、メモリのリサイクル、文字列操作などで広く使用されています。 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 配列は、当然 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 などはすべてリソースです。 リソースの使用方法:
リソースは、それを参照しているすべての変数がスコープ外になった後だけでなく、リクエストが終了して新しいリクエストが生成された後でも、長期間存続することができます。これらのリソースは、特に破棄されない限り、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に同じ名前の変数が存在しない場合は、それが最初に作成されます。 |