元のリンク: http://www.orlion.ga/1237/
クラスのメンバー変数は基本的に PHP の変数ですが、これらの変数はすべて特定のクラスに属しており、これらの変数にはアクセス制御があります。
クラスのメンバメソッドは本来PHPでは関数ですが、この関数はクラスメソッドとしても存在し、インスタンスメソッドとしても存在し、これらのメソッドにはクラスアクセス制御が付加されています。クラスのメンバー メソッドは、現実世界のエンティティの動作を抽象化したもので、クラスの動作を実装するために使用できます。
1. メンバー変数
メンバー変数はコンパイル時にクラス構造に登録されています。 zend_do_begin_class_declaration 関数は、コンパイル時にクラス宣言がコンパイルされるときに呼び出されます。この関数は、クラスのメンバー変数などのクラスの基本情報を初期化するために使用されます。呼び出しシーケンスは [zend_do_begin_class_declaration]–>[zend_initialize_class_data]–>[zend_hash_init_ex] です
リーリー
クラスのメンバー変数は HashTable に格納されるため、データは zend_hash_init_ex 関数を使用して初期化されます。
クラスを宣言すると、クラスのメンバー変数が配置されている HashTable が初期化されます。その後、新しいメンバー変数の属性宣言がある場合は、コンパイル時に zend_do_declare_property が使用されます。関数は最初に、メンバー変数で許可されていないいくつかの条件をチェックします:
インターフェイスではメンバー変数は使用できません
メンバー変数は抽象プロパティを持つことはできません
メンバー変数をfinalとして宣言できません
属性を繰り返し宣言することはできません
クラス内でプロパティを Final として宣言する場合:
リーリーエラーが報告されます: 致命的なエラー: プロパティを宣言できません...このエラーは zend_do_declare_property 関数によってスローされます:
リーリー定義チェックが正しい後、関数はメンバー変数を初期化します。
リーリー初期化プロセス中に、プログラムはまずメモリを割り当てます。このメンバー変数に初期化されたデータがある場合、そのデータは属性に直接割り当てられます。それ以外の場合は、ZVAL が初期化され、その型が IS_NULL に設定されます。初期化プロセスが完了した後、プログラムは zend_declare_property_ex 関数を呼び出して、このメンバー変数を指定されたクラス構造に追加します。
通常のメンバー変数は、最終的にクラスのdefault_propertilesフィールドに登録されます。私たちの日常業務では、上記のプロセスを使用しない場合もありますが、 get_class_vars() 関数を使用してクラスのメンバー変数を表示する場合があります。この関数は、クラスのデフォルトのプロパティで構成される連想配列を返します。この配列の要素は、varname=>value の形式で存在します。その実装のコアコードは次のとおりです:
リーリーまず、zend_lookup_class 関数を呼び出して class_name という名前のクラスを見つけ、それを pce 変数にコピーします。この検索プロセスの中心となるのは、EG (class_table) を検索する HashTable 検索関数 zend_hash_quick_find です。クラスが存在するかどうかを確認し、存在する場合はそれを直接返します。存在しない場合は、自動的にロードできるかどうかを判断する必要があります。自動的にロードできる場合は、クラスをロードして戻ります。クラスが見つからない場合は、FALSE を返します。クラスが見つかった場合は、返された配列を初期化し、クラスの静的メンバー変数を更新し、クラスのメンバー変数を返された配列に追加します。クラスの静的メンバー変数には更新プロセスがあります。このプロセスについては、以下の静的変数に関するセクションで説明します。
2. 静的メンバー変数
クラスの静的メンバー変数はすべてのインスタンスに共通であり、このクラスに属するため、クラス変数とも呼ばれます。 PHP のクラス構造では、クラス自体の静的変数はクラス構造のdefault_static_memebersフィールドに存在します。
通常のメンバー変数とは異なり、クラス変数はクラス名を通じて直接呼び出すことができます。これは、クラス変数と呼ばれることの特殊性も反映しています。 PHP の例:
リーリーVLD 拡張機能によって生成された中間コードを表示します:
リーリーこの中間コードは、Tipi::$var 呼び出しにのみ対応します。前のクラス定義とはほとんど関係がありません。 VLD によって生成されたコンテンツによれば、PHP コード: Tipi::$var を知ることができ、生成された中間コードには ZEND_FETCH_CLASS と FETCH_R が含まれます。これは単なる静的変数の呼び出しですが、2 つの中間コードが生成されます。理由: クラスの静的変数を呼び出したいのですが、当然、最初にクラスを見つけてから、このクラスの変数を取得する必要があります。 PHP ソースコードから判断すると、これはコンパイル中に zend_do_fetch_static_member 関数が呼び出され、その関数内で zend_do_fetch_class 関数が呼び出され、ZEND_FETCH_CLASS 中間コードが生成されるためです。対応する実行関数は ZEND_FETCH_CLASS_SPEC_CONST_HANDLER です。この関数は、zend_fetch_class 関数 (Zend/zend_execute_API.c) を呼び出します。 zend_fetch_class 関数は、最終的に zend_lookup_class_ex 関数を呼び出してクラスを検索します。
找到了类接着应该就是查找类的静态成员变量,其最终调用的函数为:zend_std_get_static_property。这里由于第二个参数的类型为ZEND_FETCH_STATIC_MEMBER。这个函数最后是从static_members字段中查找对应的值返回。而在查找前会和前面一样,执行zend_update_class_constant函数,从而更新此类的所有静态成员变量,静态变量更新流程图:
三、成员方法
成员方法从本质上来将也是一种函数,所以其存储结构也和常规函数一样,存储在zend_function结构体中。对于一个类的多个成员方法,它是以HashTable的数据结构存储了多个zend_function结构体。和前面的成员变量一样,在类声明时成员方法也通过调用zend_initalize_class_data方法,初始化了整个方法列表所在的HashTable。
除去访问控制关键字,一个成员方法和常规函数是一样的,从语法解析中调用的函数一样(都是zend_do_begin_function_declaration函数),但是其调用的参数有一些不同,第三个参数is_method,成员方法的赋值为1,表示它作为成员方法的属性。在这个函数中会有一系统的编译判断,比如在接口中不能声明私有的成员方法。
在此程序判断后,程序将方法直接添加到类结构的function_table字段,在此之后,又是若干的编译检测。比如接口的一些魔术方法不能设置为非公有,不能被设置为static,如__call()、__callStatic()、__get()等。
与成员变量一样,成员方法也有一个返回所有成员方法的函数–get_class_methods()。此函数返回由指定的类中定义的方法名所组成的数组。
四、静态成员方法
类的静态成员方法通常也叫做类方法。与静态成员变量不同,静态成员方法与成员方法都存储在类结构的function_table字段。
class Tipi{ public static function t() { echo 1; } } Tipi::t();
以上的代码在VLD扩展下生成的部分中间代码:
number of ops: 8 compiled vars: none line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > EXT_STMT 1 NOP 8 2 EXT_STMT 3 ZEND_INIT_STATIC_METHOD_CALL 'Tipi','t' 4 EXT_FCALL_BEGIN 5 DO_FCALL_BY_NAME 0 6 EXT_FCALL_END 9 7 > RETURN 1 branch: # 0; line: 2- 9; sop: 0; eop: 7 path #1: 0, Class Tipi: Function t: Finding entry points Branch analysis from position: 0
从以上的内容可以看出整个静态成员方法的调用是一个先查找方法再调用的过程。而对于调用操作,对应的中间代码为ZEND_INIT_STATIC_METHOD_CALL。由于类名和方法名都是常量,于是我们可以知道中间代码对应的函数是ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER。在这个函数中,它会首先调用zend_fetch_class函数,通过类名在EG(class_table)中查找类,然后再执行静态方法的获取方法。
if (ce->get_static_method) { EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); } else { EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); }
如果类结构中的get_static_method方法存在,则调用此方法,如果不存在,则调用zend_std_get_static_method。在PHP的源码中get_static_method方法一般都是NULL,这里我们重点查看zend_std_get_static_method函数。此函数会查找ce->function_table列表,在查找到方法后检查方法的访问控制权限,如果不允许访问,则报错,否则返回函数结构体。