ホームページ  >  記事  >  バックエンド開発  >  プログラミング初心者が PHP の基本的な動作メカニズムと原理を学習する際に注意すべき点は何ですか?

プログラミング初心者が PHP の基本的な動作メカニズムと原理を学習する際に注意すべき点は何ですか?

WBOY
WBOYオリジナル
2016-07-25 08:46:00904ブラウズ

LESSEJIA今日は、PHPの基本的な動作メカニズムと原則を学ぶ方法を共有します。PHPはWeb開発に適した動的言語です。具体的には、C言語を使用して多数のコンポーネントを実装するソフトウェアフレームワークです。より狭義には、強力な UI フレームワークと考えることができます。

AD:

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 エンジンを中心として、拡張機能はコンポーネントベースの方法でさまざまな基本サービスを提供します。ユーザーは、一般的なさまざまな組み込み関数 (配列シリーズなど)、標準ライブラリなどをすべて拡張機能を通じて実装できます。機能拡張やパフォーマンスの最適化などの目的を達成するには、独自の拡張機能を実装する必要があります (たとえば、PHP 中間層や Tieba で使用されるリッチ テキスト解析は、拡張機能の代表的なアプリケーションです)。

Sapi: Sapi の正式名は Server Application Programming Interface で、Sapi は一連のフック関数を使用して PHP が周辺データと対話できるようにします。これは PHP の非常にエレガントで成功した設計です。 sapi を通じて成功 PHP 自体を上位層のアプリケーションから切り離して分離することにより、PHP は異なるアプリケーションとの互換性を考慮する必要がなくなり、アプリケーション自体も独自の特性に基づいて異なる処理方法を実装できるようになります。

上位層アプリケーション: これは、私たちが通常作成する PHP プログラムであり、Web サーバーを介して Web アプリケーションを実装したり、コマンドラインでスクリプト モードで実行したりするなど、さまざまな SAPI メソッドを通じてさまざまなアプリケーション モードを取得します。

PHP が車だとすると、車のフレームは PHP そのもの、Zend は車のエンジン、Sapi の下にあるさまざまなコンポーネントは車の車輪とみなすことができます。車はさまざまな種類の道路上を走行でき、PHP プログラムの実行は車が道路上を走行していることを意味します。したがって、高性能エンジン + 適切なホイール + 適切なトラックが必要です。

3. Sapi

前に述べたように、Sapi を使用すると、外部アプリケーションが一連のインターフェイスを通じて PHP とデータを交換し、さまざまなアプリケーションの特性に応じて特定の処理メソッドを実装できるようになります。 Apache2handler: これは、Apache を Web サーバーとして使用し、mod_PHP モードで実行する場合の処理​​方法でもあり、現在最も広く使用されています。

cgi: これは、有名な fastcgi プロトコルである Web サーバーと PHP の間のもう 1 つの直接対話方法であり、近年、fastcgi+PHP がますます使用されており、非同期 Web サーバーでサポートされている唯一の方法でもあります。

cli: コマンドライン呼び出し用のアプリケーションモード

4. PHP実行プロセスとopcode

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

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

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

オペコードは

PHP

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

1ZEND_ASSIGN_SPEC_CV_CV_HANDLER: 変数割り当て ($a=$b)

2ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER: 関数呼び出し

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

4ZEND_ADD_SPEC_CV_CONST_HANDLER : 加算演算 $a+2

5ZEND_IS_EQUAL_SPEC_CV_CONST:$a==1と等しい判定

6ZEND_IS_IDENTICAL_SPEC_CV_CONST:$a===1と等しい判定

5. HashTable — コアデータ構造

HashTable は zend の中核となるデータ構造です。 PHP ほとんどすべての一般的な関数を実装するために使用されます。また、zend 内では、関数シンボル テーブル、グローバル変数などもハッシュ テーブルに基づいて実装されます。

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

一般的な key->value クエリをサポート

配列として使用可能

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

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

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

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

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

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

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

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

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

03Bucket *p = arBucket[index];

04while (p) {

05 if ((p->h == h) && (p->gt; nKeyLength == nKeyLength)) {

06 RETURN p->data;

07 }

08 p=p->next;

10RETURN FAL TURE ;

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 変数の識別と実装に使用されます。そのデータ構造は次のとおりです:

Zval は主に 3 つの部分で構成されます。 type : 変数の型 (整数、文字列、配列など) を指定します

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

値: コア部分は変数の実際のデータを格納します

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

PHP変数の型と実際のストレージの対応関係は以下の通りです。こんにちは

4IS_STRING -> str

5IS_RESOURCE -> lvalue

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

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

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

整数と浮動小数点数は、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 とは異なり、長さは実際の変数で表されるため、その文字列はバイナリ データ (\0 を含む) にすることができます。同時に、PHP では、文字列の長さ strlen を見つけるのは O(1) 操作です。

文字列操作を追加、変更、または追加すると、PHP はメモリを再割り当てして新しい文字列を生成します。最後に、セキュリティ上の理由から、PHP は文字列を生成するときに最後に \0 を追加します。一般的な文字列の結合方法と速度の比較:

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

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

1$res = $strA.$ strB と $res = “$strA$ strB”

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

3$strA = $strA.$strB

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

5$res = $intA.$intB

6 この種の速度は、暗黙的な形式変換を行うとき、必要があるため遅くなります。実際にプログラムを書く際には避けるように注意してください

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

8 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 がテーブルから検索され、識別子に従って返されます。

レッスンマスターは、PHPの基礎となる動作メカニズムと原理を学習している関数でグローバル変数を使用することを皆さんに思い出させます: 関数では、明示的にglobalを宣言することでグローバル変数を使用できます。 active_symbol_tableのsymbol_tableに同じ名前の変数への参照を作成します。symbol_tableに同じ名前の変数が存在しない場合は、それが最初に作成されます。




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