C ++でPHPモゞュヌルを開発しおいたす。 パヌト1Zend Engine 2の䞖界ぞの遠足

こんにちは、Harbasコミュニティの皆さん



猫の䞋には、さたざたな゜ヌスほずんどが英語から入手したC ++を䜿甚しおPHPの拡匵機胜を䜜成し、自分のニヌズに合わせお1぀のモゞュヌルを開発しながらZend Engine 2の゜ヌスを遞択する情報がいく぀か蚘茉されおいたす。 そのボリュヌムが十分に倧きいので、簡朔にしようずしたした。



この郚分では



しかし、この郚分ではC ++には到達したせん... =



小さな免責事項蚘事の内容は、最初は真実ではなく、公匏文曞に基づいおいるわけではありたせんそれはありたすかそしお、ZE 2に察する䞻芳的な芋解でもありたす。開発の初期段階で時間を節玄するため。







Zend Engine 2の内郚䞖界



基本的なデヌタ型


Zend Engine 2はCで蚘述されおいたす。これは、内郚゚コシステムに倧きな圱響を䞎えおいたす。 クラスオブゞェクトのパラダむムが存圚しない堎合、グロヌバル倉数、自由関数、およびナヌザヌ定矩デヌタ型構造䜓ずの類䌌性がZE 2内に発生したした。 すべおの堎合においお、単玔デヌタ型ず耇合デヌタ型の組み合わせ、およびそれらを凊理するための手順がありたす。



最も䞀般的な構造はzval zend-valueです。 構造は、 ナヌザヌスペヌスの裏偎のPHP倉数の衚珟ですここおよび今埌、ナヌザヌスペヌスはPHPで蚘述されたコヌドを意味し、ZEが凊理するため、 ナヌザヌスペヌスの裏偎はCコヌドです。

PHPは、匱い動的型付けず自動メモリ管理を備えた蚀語です。この蚀語の倉数は、ラむフサむクル党䜓で型を倉曎でき、必芁がなくなった埌にプログラマが明瀺的に削陀する必芁はありたせんガベヌゞコレクタがこれを凊理したす。 これらの気たぐれに぀いおは、郚分的に膚らたせおzvalする必芁がありたす。 珟圚 PHP 5.3.3 この構造は次のように定矩されおいたすzend.h



typedef struct _zval_struct zval; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; };
      
      





ここで䜕が芋えたすか 驚くべきこずに、Zend倀zvalは盎接倉数の倀ではありたせん。 倉数の倀は倀フィヌルドに栌玍され、そのタむプはzvalue_value 䞋です。 valueに栌玍される倀のタむプは、タむプフィヌルドによっお決定され、次のいずれかzend.hになりたす。



  #define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9
      
      





ええ、ここにありたす-これらは非垞に8皮類のPHPデヌタで、倚くの堎合、人々はむンタビュヌにリストできたせん さらに、2぀の個別の倀、IS_CONSTANTおよびIS_CONSTANT_ARRAY。



構造には、この倉数ぞの参照の数に関する情報refcount__gcず、この倉数が参照であるかどうかを決定するフラグis_ref__gcも含たれたす。 この情報は、ZE内のメモリを䜿甚しお効果的な䜜業を敎理するために必芁です。 たずえば、ナヌザヌ空間での次の状況



 <?php $foo = 5; // 1 $bar = $foo; // 2
      
      





タむプIS_LONGのzval valオブゞェクトの䜜成ナヌザヌ空間のすべおの敎数はナヌザヌ空間の反察偎のC longに察応を䜜成し、そのis_ref_gcを0に、refcount_gcを1最初の行に蚭定し、文字「foo」 valを倀ずしお持぀珟圚のプロセス文字テヌブル実際には倉数名ずその倀の連想配列 2行目では、zvalの新しいむンスタンスは䜜成されたせん。 シンボルテヌブルに远加されたばかりのValは1参照カりント増加し、バヌシンボルは同じvalでシンボルテヌブルに登録されたす。 これにより、新しい倉数を䜜成するために必芁なメモリ割り圓おの数が削枛されたす。

むンタヌプリタヌがコヌドに遭遇するず



 ... $bar = '42';
      
      





圌は、シンボル「bar」に察応するプロセスzvalの文字の珟圚のテヌブルから抜出し、たず、この倀ぞのリンクの数をチェックしたす。 それが1より倧きく、zvalが参照is_ref_gc == 0でない堎合、むンタヌプリタヌは$ barの珟圚の倀のコピヌを䜜成し、それでアクションを実行しこの堎合、文字列倀を '42'ず同等にしたす、シンボルテヌブルに「bar」を入れたす新しい䟡倀。 refcoun_gc == 1たたはis_ref_gc == 1の堎合、アクションはシンボルテヌブルから取埗した倀に察しお盎接実行されたす。 したがっお、次のような状況むしろ人為的であるが、生呜に察する暩利があるの状況では



 <?php $foo = 100500; //1 $bar = $foo; //2 echo $bar; unset($bar); $foo = '42'; //3
      
      





むンタヌプリタヌのコストはzvalを1぀だけですが、それに察応する2文字です。 これは、コメント1の行でzval valが1に等しいリンクの数で䜜成されるため可胜です。コメント2の行では、倀valの新しいシンボル「バヌ」が登録されたす。そのため、refcoun_gcはすでに2に等しいコメント3では、unset$ barを呌び出した埌、valぞのリンクの数が再び1に枛少するため、新しいzvalは䜜成されたせん。

ナヌザヌ空間で$ b =$ aの圢匏の構造が怜出された堎合、is_ref_gcが1に等しくなるず掚枬するこずは難しくありたせん。

䞊蚘のアプロヌチは、「曞き蟌み時に分離」ず呌ばれたす。



次に、タむプを芋おみたしょう zvalue_valuezend.h



 typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value;
      
      





これが組合であるこずに気付くのは難しくない。 これは、zvalue_value型の倉数のアドレスのメモリにあるデヌタは、開発段階ず実行段階の䞡方で、ナニオンを構成するデヌタ型のいずれかずしおプログラマヌにずっお䟿利な方法で解釈できるこずを意味したす。 このzvalue_valueの機胜により、PHPナヌザヌ空間倉数のタむプを䞀生簡単に倉曎できるようになりたすzval倉数の珟圚のタむプは、そのタむプフィヌルドを参照するこずで芋぀けるこずができたす。

ただし、ここには5぀の䞀意のフィヌルドずデヌタ型8のみが衚瀺されおいるず蚀えたす。PHP型のzvalue_valueナニオンぞのマッピングは次のずおりです。



zvalue_valueの構造を怜蚎するこずから䜕が圹立぀でしょうか

  1. リ゜ヌスタむプは敎数の識別子でのみ衚されるため、ナヌザヌスペヌスではダヌクホヌスのように芋えたす。 ナヌザヌ空間の反察偎では、そのような識別子を開いおいるファむル蚘述子、TCP゜ケット、デヌタベヌス接続オブゞェクトなどに関連付けるこずができたす。 マッピングは、リ゜ヌスの特別なストレヌゞを介しお行われたすもちろん、C偎。
  2. PHPの実際のものはすべお倍粟床で、8バむトを占有したす。
  3. 原則ずしお、文字列はバむナリセヌフにするこずができたす。 内郚でヌル文字を䜿甚したす。 これは、文字列の長さが、この文字列が眮かれおいるメモリぞのポむンタずずもに栌玍されるずいう事実により実珟されたす。 strlen操䜜はナヌザヌ空間で高速です。 行末のヌル文字はオプションです。 実際、倚くの拡匵機胜は正確にnullで終了する文字列を䜿甚し、文字列strlenを軜芖したせん。
  4. 配列は、内郚HashTable型で衚されたす。 HashTableは別の構造ですが、それを怜蚎し、それを操䜜する原則はこの蚘事の範囲倖です。
  5. PHPのオブゞェクトは、 zend_object_value構造で衚されたす。 拡匵機胜の開発は、独自のデヌタ型の䜜成ず関連しおいるため、埌で説明したす。
  6. PHPで倉数を䜜成するず、倉数のタむプに関係なく、32ビットアヌキテクチャで少なくずも16バむトが消費されたすナニオンのサむズがその䞭のフィヌルドの最倧サむズに等しい堎合、zvalフィヌルドのサむズを合蚈したす。




それでオブゞェクトに到達したした。 zend_object_value構造䜓は、オブゞェクトを含むナヌザヌ空間倉数を衚すこずを目的ずしおいたす。 そしお、クラスオブゞェクトパラダむムのオブゞェクトずは䜕ですか オブゞェクトずは、デヌタずその凊理方法の共生です。 それでは、構造を芋おみたしょう zend_object_value zend_type.h



 typedef unsigned int zend_object_handle; typedef struct _zend_object_handlers zend_object_handlers; typedef struct _zend_object_value { zend_object_handle handle; zend_object_handlers *handlers; } zend_object_value;
      
      





構造は、敎数識別子ハンドルず別の構造zend_object_handlers *ハンドラヌの組み合わせであり、オブゞェクトに関連付けられた特定のむベントが発生したずきにZE 2゚ンゞンによっお呌び出される関数ぞのポむンタヌが含たれたす。 そのようなむベントには、新しい倉数をオブゞェクトを含む倉数の倀ず同等にするzend_object_add_ref_t add_ref、スコヌプから出る、別の倀で初期化する、たたはオブゞェクトを含む倉数の蚭定解陀を呌び出すzend_object_del_ref_t del_ref、__ clonezend_object_cloneを呌び出すこずによりオブゞェクトを耇補するが含たれたすclone_obj、オブゞェクトプロパティぞのアクセスzend_object_read_property_t read_property、オブゞェクトプロパティぞの曞き蟌みzend_object_write_property_t write_propertyなど zend_object_handlers構造䜓自䜓は次のずおりですzend_object_handlers.h



 struct _zend_object_handlers { /* general object functions */ zend_object_add_ref_t add_ref; zend_object_del_ref_t del_ref; zend_object_clone_obj_t clone_obj; /* individual object functions */ zend_object_read_property_t read_property; zend_object_write_property_t write_property; zend_object_read_dimension_t read_dimension; zend_object_write_dimension_t write_dimension; zend_object_get_property_ptr_ptr_t get_property_ptr_ptr; zend_object_get_t get; zend_object_set_t set; zend_object_has_property_t has_property; zend_object_unset_property_t unset_property; zend_object_has_dimension_t has_dimension; zend_object_unset_dimension_t unset_dimension; zend_object_get_properties_t get_properties; zend_object_get_method_t get_method; zend_object_call_method_t call_method; zend_object_get_constructor_t get_constructor; zend_object_get_class_entry_t get_class_entry; zend_object_get_class_name_t get_class_name; zend_object_compare_t compare_objects; zend_object_cast_t cast_object; zend_object_count_elements_t count_elements; zend_object_get_debug_info_t get_debug_info; zend_object_get_closure_t get_closure; };
      
      





そしお、 ここで詳现を読んでください 。



気を取られお、zend_object_valueに戻りたす。 それで、むベントの関数ハンドラぞのポむンタ以倖に䜕が含たれおいたすか しかし、䜕も _zend_object_handlers内のオブゞェクトの動䜜を決定しようずする詊みの類䌌性を芋た堎合、䜕らかの奇劙な識別子ハンドルに加えお、特定のむンスタンスに固有のデヌタは芳察されたせん。 しかし、識別子自䜓真空䞭の球䜓は意味がありたせん。 したがっお、同皮の゚ンティティの䜕らかの皮類のリポゞトリが存圚する必芁がありたす。この識別子は、ある゚ンティティを別の゚ンティティから区別したす。

ZEのこのようなストレヌゞはZend Object Storageです。 その䞭のキヌはオブゞェクト蚘述子zend_object_value.handleず倀です...はい、はい、おそらくあなたはそれを掚枬したした-別の皮類の構造はzend_object zend.hです



 typedef struct _zend_object { zend_class_entry *ce; HashTable *properties; HashTable *guards; /* protects from __get/__set ... recursion */ } zend_object;
      
      





ここでは、別々のピヌスから、 油絵がすでに圢になり始めおいたす。 HashTable *プロパティ-特定のむンスタンスに固有のデヌタを保持できる堎所です。 プロパティフィヌルドはZEの連想配列暙準であり、そのキヌは珟圚のオブゞェクトのクラスのフィヌルド名でなければならず、倀はこのオブゞェクトのフィヌルドプロパティの珟圚の倀です。



そのため、珟時点ではオブゞェクトを操䜜するための次のオプションがありたす-特定の状況でオブゞェクトの暙準動䜜を再定矩できzend_object_handlersの察応する関数をオヌバヌラむドするこずにより、HashTable *に関連付けられたプロパティをむンスタンスフィヌルドに曞き蟌むこずでデヌタを栌玍できたす珟圚のオブゞェクト。 䜕かが欠けおいたす...ああ、そうですが、どのようにオブゞェクトにカスタム動䜜を远加したすか新しいメ゜ッドを䜜成したす メ゜ッドは1぀のクラスのすべおのオブゞェクトに共通するものであるため、クラスのすべおのオブゞェクトに察する共有アクセスぞの共有アクセスを䜕らかの構造に配眮するこずは論理的です。 そのような構造は、 zend_class_entry zend.hです。



 struct _zend_class_entry { char type; char *name; zend_uint name_length; struct _zend_class_entry *parent; int refcount; zend_bool constants_updated; zend_uint ce_flags; HashTable function_table; HashTable default_properties; HashTable properties_info; HashTable default_static_members; HashTable *static_members; HashTable constants_table; const struct _zend_function_entry *builtin_functions; //  union _zend_function *constructor; //,    union _zend_function *destructor; //    union _zend_function *clone; union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function *__isset; union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; union _zend_function *serialize_func; union _zend_function *unserialize_func; zend_class_iterator_funcs iterator_funcs; /* handlers */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* a class implements this interface */ union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC); /* serializer callbacks */ int (*serialize)(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); int (*unserialize)(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); zend_class_entry **interfaces; zend_uint num_interfaces; char *filename; zend_uint line_start; zend_uint line_end; char *doc_comment; zend_uint doc_comment_len; struct _zend_module_entry *module; };
      
      





zend_class_entry構造䜓の目的は、同じクラスのすべおのオブゞェクトの䞀般的な偎面を衚すこずです。 zend_class_entryは、実際にはクラスそのものです。 ご芧のずおり、構造は小さくなく、各フィヌルドの目的を考慮するこずはこの蚘事のタスクではありたせん。 コメントでマヌクしたフィヌルドに泚目したしょう。



const struct _zend_function_entry * builtin_functions-_zend_function_entry構造の配列ぞのポむンタヌ。 これらが将来のクラスのメ゜ッドであるず掚枬するこずは難しくありたせん。 const修食子でマヌクされおいるため、この配列の芁玠を倉曎する぀たり、むンスタンス化されたクラスオブゞェクトのメ゜ッドをオヌバヌラむドするこずはできたせんzend_object_value.handlersずは異なりたす。



コンストラクタヌからunserialize_funcたでのフィヌルドはPHPのマゞックメ゜ッドです unserialize_funcが__wakeupであり、serialize_funcが__sleepであるず掚枬するのは難しくありたせん。残りのメ゜ッドも同様のニヌモニックを持っおいたす。



独自の拡匵機胜を䜜成する過皋で、builtin_functionsに゚ントリを远加したり、将来のクラスのマゞックメ゜ッドを再定矩したりできたす。



最埌になりたしたが、このZE構造の魅力的な䞖界ぞの遠足のヒヌロヌは、拡匵モゞュヌル自䜓の衚珟に関䞎する構造です-zend_module_entry zend_modules.h



 struct _zend_module_entry { unsigned short size; unsigned int zend_api; unsigned char zend_debug; unsigned char zts; const struct _zend_ini_entry *ini_entry; const struct _zend_module_dep *deps; const char *name; const struct _zend_function_entry *functions; int (*module_startup_func)(INIT_FUNC_ARGS); int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); int (*request_startup_func)(INIT_FUNC_ARGS); int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); const char *version; size_t globals_size; #ifdef ZTS ts_rsrc_id* globals_id_ptr; #else void* globals_ptr; #endif void (*globals_ctor)(void *global TSRMLS_DC); void (*globals_dtor)(void *global TSRMLS_DC); int (*post_deactivate_func)(void); int module_started; unsigned char type; void *handle; int module_number; char *build_id; };
      
      





䞀般的な堎合、拡匵機胜は1぀ではなく耇数のクラスを同時に実装できたす-゚クスポヌト関数のみラむトはOOPに収束したせんでした、䞊蚘の構造の蚭蚈にどのように圱響するかを考慮しおください



  1. const struct _zend_function_entry * functions-拡匵機胜によっお゚クスポヌトされた関数の配列ぞのポむンタヌ。 zend_class_entry.builtin_functionsず同様です。
  2. int* module_startup_funcINIT_FUNC_ARGS-拡匵機胜が接続されたずきに呌び出される関数ぞのポむンタヌ。 特に、拡匵機胜がクラスを゚クスポヌトする堎合、この関数でクラスをZE 2の内郚クラスレゞストリに登録する必芁がありたす。
  3. int* module_shutdown_funcSHUTDOWN_FUNC_ARGS-拡匵機胜がアンロヌドされるずきに呌び出される関数ぞのポむンタヌ。 ここで自分自身を拭かなければなりたせん。




デヌタ型の階局





前の段萜では、通垞のzvalから䞀般的なzend_module_entryぞのパスを調べたした。 これらの型を巧みに凊理しお、独自のPHP拡匵機胜を䜜成し、オブゞェクトを生成するための適切に調敎されたファクトリを線成できたす。 実際、PHP拡匵モゞュヌルはナヌザヌスペヌスのトレヌニングスクヌルのようなものです。 最初に、それをビルドPHP_MINIT_FUNCTIONを呌び出しし、特定の焊点を持぀人材掟遣䌚瀟ずしお劎働取匕所PHP_MINIT_FUNCTIONで゚クスポヌトされたクラスたたは関数を宣蚀に登録し、次に、埓業員クラスの新しいむンスタンスの最初の芁求時に、戊闘機を準備するサむクルを開始する䜜成する必芁がありたすオブゞェクト。 準備は、䜜成されたオブゞェクトにメモリを割り圓お、特定のむベントハンドラヌzend_object_handlersおよび独自のクラスzend_class_entryに関連付けたす。これには、未来のオブゞェクトのメ゜ッドが含たれたす。 この準備は通垞、関数extension_name_objects_newに配眮され、フィヌルドzend_class_entry.create_objectに関連付けられたす。



抂略的に、展開構造は次のように衚すこずができたす。







たた、拡匵機胜のデヌタ型の階局をより明確に想像するために、次の図を瀺したす。







おわりに



この蚘事には倚くのテキストず非垞に少ないコヌドが含たれおいたしたが、ZE 2デヌタ型の䞖界ぞの遠足がなければ、特定の関数の呌び出しの目的を理解しようずするこずは非垞に困難です。 次のパヌトでは、PHP拡匵モゞュヌルを䜜成するために必芁な最初の手順に぀いお説明したすが、その前にzvalの操䜜ずメモリ割り圓おの管理のトピックに觊れたす。



PS興味のある人のための蚘事の玠晎らしい遞択はこちらです。



All Articles