ホームページ  >  記事  >  バックエンド開発  >  PHPのオートロード機構の実装解析_PHPチュートリアル

PHPのオートロード機構の実装解析_PHPチュートリアル

WBOY
WBOYオリジナル
2016-07-21 15:16:11653ブラウズ

1. オートロード機構の概要

PHP の OO モードを使用してシステムを開発する場合、通常は各クラスの実装を別のファイルに保存するのが一般的であり、これによりクラスの再利用も容易になります。将来のメンテナンスに便利です。これは、OO デザインの基本的な考え方の 1 つでもあります。 PHP5 より前では、クラスを使用する必要がある場合、include/require を使用してクラスを直接インクルードするだけで済みました。
以下は実際的な例です:

コードをコピーします コードは次のとおりです:

/* Person.class.php */
class Person {
var $name, $age;
function __construct ($name, $age)
{
$this->name = $name;
$this->age = $age;
}
?> php */
require_once ("person.class.php");
$person ("Altair", 6); この例では、いいえ-autoload。php ファイルは、require_once を使用してそれを含める Person クラスを使用する必要があります。その後、その Person クラスを直接使用してオブジェクトをインスタンス化できます。
しかし、プロジェクトの規模が拡大し続けると、この方法を使用するといくつかの隠れた問題が発生します。PHP ファイルが他の多くのクラスを使用する必要がある場合、多くの require/include ステートメントが必要になり、省略や不要なインクルードが発生する可能性があります。クラスファイル。多数のファイルで他のクラスの使用が必要な場合、各ファイルに正しいクラス ファイルが含まれていることを確認するのは悪夢のような作業になる可能性があります。
PHP5 は、クラスの自動読み込み (オートロード) メカニズムというこの問題の解決策を提供します。オートロード メカニズムにより、PHP プログラムは最初にすべてのクラス ファイルを含めるのではなく、クラスが使用される場合にのみクラス ファイルを自動的に含めることができます。このメカニズムは遅延ロードとも呼ばれます。

以下は、自動ロードメカニズムを使用して Person クラスをロードする例です:




コードをコピーします

コードは次のとおりです:

/* autoload.php */ function __autoload($classname) { require_once ($classname . "class.php") }
$person("Altair", 6);

;

通常、PHP5 がクラスを使用するときに、クラスがロードされていないことが判明すると、自動的に __autoload() 関数が実行され、使用する必要があるクラスをロードできます。この簡単な例では、拡張子「.class.php」を付けたクラス名を直接追加してクラス ファイル名を形成し、require_once を使用してそれをロードします。この例から、autoload は少なくとも 3 つのことを行う必要があることがわかります。1 つ目はクラス名に基づいてクラス ファイル名を決定すること、2 つ目はクラス ファイルが配置されているディスク パスを決定することです。私たちの場合、それが最も単純です。クラスがそれらを呼び出す PHP プログラム ファイルと同じフォルダーにある場合、3 番目のことは、ディスク ファイルからシステムにクラスをロードすることです。 3 番目のステップは最も単純で、include/require を使用するだけです。第 1 ステップと第 2 ステップの機能を実現するには、クラス名とディスク ファイル間のマッピング方法を開発時に合意する必要があります。この方法でのみ、クラス名に基づいて対応するディスク ファイルを見つけることができます。

したがって、含めるクラス ファイルが多数ある場合は、対応するルールを決定し、__autoload() 関数でクラス名と実際のディスク ファイルを一致させるだけで、遅延読み込み効果を実現できます。ここから、 __autoload() 関数の実装で最も重要なことは、クラス名と実際のディスク ファイルの間のマッピング ルールの実装であることもわかります。

しかし、ここで問題が発生します。システムの実装で他の多くのクラス ライブラリを使用する必要がある場合、これらのクラス ライブラリは異なる開発者によって作成され、クラス名と実際のディスク ファイル間のマッピング ルールがすべて異なる可能性があります。同じ。現時点で、クラス ライブラリ ファイルの自動ロードを実装したい場合は、すべてのマッピング ルールを __autoload() 関数に実装する必要があります。この場合、__autoload() 関数は非常に複雑になるか、実装が不可能になる可能性があります。最終的に、__autoload() 関数は、たとえ実装できたとしても非常に肥大化する可能性があり、将来のメンテナンスとシステム効率に大きな悪影響を及ぼします。この場合、もっとシンプルで明確な解決策はないのでしょうか?答えはもちろん「いいえ」です! さらなる解決策を検討する前に、まず PHP の自動ロード メカニズムがどのように実装されているかを見てみましょう。

2. PHP の自動ロードメカニズムの実装

PHP ファイルの実行は 2 つの独立したプロセスに分割されることがわかっています。最初のステップは、PHP ファイルを一般に OPCODE と呼ばれるバイトコード シーケンスにコンパイルすることです (実際の最初のステップはそれを zend_op_array というバイト配列にコンパイルします)、そして 2 番目のステップは、仮想マシンによってこれらの OPCODE を実行することです。 PHP のすべての動作は、これらの OPCODE によって実装されます。したがって、PHP でのオートロードの実装メカニズムを調べるために、autoload.php ファイルをオペコードにコンパイルし、これらの OPCODE を使用してプロセスで PHP が何を行うかを調べます:
コードをコピーします コードは次のとおりです。以下のように:

/* autoload.php コンパイルされた OPCODE リストは、作者が開発した OPDUMP ツール
* を使用して生成されます。ソフトウェアは Web サイト http://www.phpinternals.com/ からダウンロードできます。
*/
// require_once ("person.php");

function __autoload ($classname) {
0 NOP
0 RECV 1
if (!class_exists($classname)) {
1 SEND_VAR !0
2 DO_FCALL 'class_exists' [extval:1]
3 BOOL_NOT $0 =>RES[~1]
4 JMPZ ~1, ->8
require_once ($classname. ".class.php"); 5 CONCAT !0, '.class.php' =>RES[~2]
6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE
}
7 JMP ->8
}
8 RETURN null

$p = new Person('フレッド', 35);
1 FETCH_CLASS '人' =>RES[:0]
2 NEW :0 =>RES[$1]
3 SEND_VAL 'フレッド'
4 SEND_VAL 35
5 DO_FCALL_BY_NAME [extval:2]
6 ASSIGN !0, $1

var_dump ($p);
7 SEND_VAR !0
8 DO_FCALL 'var_dump' [extval:1]


autoload.php の 10 行目には、クラス Person に対してインスタンス化されます。したがって、自動ロード メカニズムは、この行のコンパイルされたオペコードに確実に反映されます。上記のコードの 10 行目で生成された OPCODE から、オブジェクト Person をインスタンス化するときに、最初に FETCH_CLASS 命令を実行する必要があることがわかります。 PHP による FETCH_CLASS 命令の処理から探索の旅を始めましょう。

PHP のソース コードを確認すると (私は PHP 5.3alpha2 バージョンを使用しています)、次の呼び出しシーケンスを見つけることができます:


コードをコピーします コードは次のとおりです:
ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS) , ...) (zend_vm_def.h 1864 行目)
=> zend_fetch_class (zend_execute_API.c 1434 行目)
=>zend_lookup_class_ex (zend_execute_API.c 964 行目)
=> ) (zend_execute_API .c 1040 行目)


呼び出しの最後のステップの前に、呼び出し時の主要なパラメーターを見てみましょう:


コードをコピーします コードは次のとおりです:

/* autoload_function 変数の値を "__autoload" に設定します */
fcall_info.function_name = &autoload_function; // おっと、ついに "__autoload" が見つかりました
...
fcall_cache.function_handler = EG(autoload_func)
!

zend_call_function は、Zend Engine の最も重要な関数の 1 つであり、その主な機能は、PHP プログラム内のユーザー定義関数または PHP 独自のライブラリ関数を実行することです。 zend_call_function には 2 つの重要なポインター パラメーター fcall_info と fcall_cache があり、それぞれ 2 つの重要な構造体 (1 つは zend_fcall_info 、もう 1 つは zend_fcall_info_cache) を指します。 zend_call_function の主なワークフローは次のとおりです。 fcall_cache.function_handler ポインターが NULL の場合は、fcall_info.function_name という名前の関数を検索し、存在する場合はそれを実行します。fcall_cache.function_handler が NULL でない場合は、指定された関数を直接実行します。 fcall_cache.function_handler 関数による。

これで、PHP がオブジェクトをインスタンス化するとき (実際にはインターフェイスを実装するとき、クラスでクラス定数または静的変数を使用するとき、クラスで静的メソッドを呼び出すとき)、まずシステム (またはインターフェイス) でクラスを検索することが明確にわかりました。 ) が存在する場合は、自動ロード メカニズムを使用してクラスをロードしてみてください。オートロードメカニズムの主な実行プロセスは次のとおりです:

(1) エグゼキューターのグローバル変数関数ポインター autoload_func が NULL かどうかを確認します。
(2) autoload_func==NULL の場合、__autoload() 関数がシステムに定義されているかどうかを確認し、定義されていない場合は、エラーを報告して終了します。
(3) __autoload() 関数が定義されている場合は、__autoload() を実行してクラスのロードを試み、ロード結果を返します。
(4) autoload_func が NULL でない場合、autoload_func ポインタが指す関数を直接実行してクラスをロードします。この時点では、__autoload() 関数が定義されているかどうかはチェックされないことに注意してください。
真実がついに明らかになりました。PHP には、自動ロード メカニズムを実装するための 2 つの方法が用意されています。1 つは、前述したように、通常は PHP ソース プログラムに実装されるユーザー定義の __autoload() 関数を使用する方法です。これは、関数を設計し、その関数を指す autoload_func ポインターを指定することです。これは通常、C 言語を使用して PHP 拡張機能で実装されます。 __autoload() 関数と autoload_func の両方が実装されている場合 (autoload_func が特定の PHP 関数を指す)、autoload_func 関数のみが実行されます。

3. SPLオートロード機構の実装
SPLとはStandard PHP Libraryの略称です。これは、PHP5 で導入された拡張ライブラリであり、その主な機能には、オートロード メカニズムとさまざまな Iterator インターフェイスまたはクラスの実装が含まれます。 SPL オートロード メカニズムは、関数ポインタ autoload_func をオートロード機能を備えた自己実装関数にポイントすることによって実装されます。 SPL には 2 つの異なる関数 spl_autoload と spl_autoload_call があり、これら 2 つの異なる関数アドレスを autoload_func に指定することで、異なる自動ロード メカニズムが実装されます。
spl_autoload は SPL によって実装されるデフォルトの自動ロード関数であり、その機能は比較的単純です。 2 つのパラメータを受け取ることができます。最初のパラメータはクラス名を表し、2 番目のパラメータ $file_extensions はオプションであり、展開を保護するために $file_extensions に複数の拡張子を指定できます。セミコロンを使用して名前を区切ります。指定しない場合は、デフォルトの拡張子 .inc または .php が使用されます。 spl_autoload は、まず $class_name を小文字に変更し、次にすべてのインクルード パスで $class_name.inc または $class_name.php ファイルを検索し ($file_extensions パラメーターが指定されていない場合)、見つかった場合はクラス ファイルをロードします。 spl_autoload("person", ".class.php") を手動で使用して、Person クラスをロードできます。実際、複数の拡張子を指定できる点を除けば、require/include に似ています。

spl_autoload を自動的に動作させるには、つまり autoload_func を spl_autoload にポイントする方法は?答えは、spl_autoload_register 関数を使用することです。パラメータを使用せずに PHP スクリプトで初めて spl_autoload_register() を呼び出すと、autoload_func を spl_autoload に指定できます。

上記の説明を通じて、spl_autoload の関数は比較的単純であり、SPL 拡張機能で実装されており、その機能を拡張できないことがわかりました。独自のより柔軟な自動読み込みメカニズムを実装したい場合はどうすればよいでしょうか?この時点で、spl_autoload_call 関数がデビューします。

まず、spl_autoload_call の実装の素晴らしい機能を見てみましょう。 SPL モジュール内には、本質的に HashTable であるグローバル変数 autoload_functions がありますが、リンク リスト内の各要素は、次の機能を持つ関数を指す関数ポインターであると単純に考えることができます。クラスの自動ロード機能。 spl_autoload_call の実装自体は非常に単純で、リンクされたリスト内の各関数を順番に実行し、各関数の実行後に必要なクラスがロードされたかどうかを判断し、ロードが成功した場合はそのままリターンします。リンクされたリストの他の機能を実行し続けます。このリンクされたリスト内のすべての関数が実行された後でクラスがロードされていない場合、spl_autoload_call はユーザーにエラーを報告せずに直接終了します。したがって、オートロード メカニズムを使用しても、クラスが自動的に正しくロードされることは保証されません。キーはオートロード関数の実装方法に依存します。

では、自動ロード関数リスト autoload_functions は誰が管理しているのでしょうか?先ほど述べた spl_autoload_register 関数です。ユーザー定義のオートロード関数をこのリンク リストに登録し、autoload_func 関数ポインターを spl_autoload_call 関数にポイントすることができます (例外があり、具体的な状況については全員が考えることができることに注意してください)。 spl_autoload_unregister 関数を使用して、登録された関数を autoload_functions リンク リストから削除することもできます。前のセクションで述べたように、autoload_func ポインタが null でない場合、__autoload() 関数は自動的に実行されません。autoload_func は spl_autoload_call を指しています。仕事?もちろん、引き続き spl_autoload_register(__autoload) 呼び出しを使用して、autoload_functions リンク リストに登録します。

最初のセクションの最後の質問に戻りますが、解決策はあります。各クラス ライブラリの異なる命名メカニズムに従って独自の自動ロード関数を実装し、spl_autoload_register を使用してそれらを SPL 自動ロード関数に登録します。列。 。この方法では、非常に複雑な __autoload 関数を維持する必要がありません。


4. オートロード効率の問題と対策

オートロードメカニズムを使用するとき、多くの人は最初に、オートロードを使用するとシステムの効率が低下するという反応をします。オートロード実装の原理を理解すると、オートロード メカニズム自体がシステム効率に影響を与える理由ではないことがわかります。これにより、不要なクラスがシステムにロードされなくなるため、システム効率が向上する可能性さえあります。
では、なぜ多くの人は自動ロードを使用するとシステム効率が低下するという印象を持っているのでしょうか?実際、オートロード メカニズムの効率に影響を与えるのは、まさにユーザー設計のオートロード機能です。クラス名を実際のディスク ファイルと効率的に一致させることができない場合 (これはファイル名だけでなく実際のディスク ファイルを指すことに注意してください)、システムは多くのファイル存在検証を実行する必要があります (各インクルード パスでの検証が必要です)。 ) に含まれるパスを検索し、ファイルが存在するかどうかを判断するにはディスク I/O 操作が必要です。ご存知のとおり、ディスク I/O 操作の効率は非常に低いため、これが効率を低下させる原因となります。

したがって、システムを設計するときは、クラス名を実際のディスク ファイルにマッピングするための明確なメカニズムを定義する必要があります。このルールが単純かつ明確であればあるほど、自動ロード メカニズムはより効率的になります。自動ロード メカニズムは本質的に非効率的ではありません。効率の低下につながるのは、自動ロード機能の乱用と不適切な設計のみです。

http://www.bkjia.com/PHPjc/325988.html

tru​​ehttp://www.bkjia.com/PHPjc/325988.html技術記事 1. オートロードメカニズムの概要 PHP の OO モデルを使用してシステムを開発する場合、通常は各クラスの実装を別のファイルに保存するのが通例です。これにより、クラスの実装が容易になります...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。