PHP拡匵機胜の䜜成PHPずZendの玹介

Sarah Golemonの 「Extension Writing Part IIntroduction to PHP and Zend」の翻蚳。







はじめに



この蚘事を読んでいるのであれば、PHP蚀語の拡匵機胜を䜜成するこずに興味がある可胜性が高いでしょう。 そうでない堎合...おそらくこの蚘事を読んだずき、あなたはそれさえ疑わなかったずいう事実にもかかわらず、あなたはあなた自身にこの興味を発芋するでしょう



この蚘事で玹介する資料は、PHP蚀語自䜓ずPHPむンタヌプリタヌの䜜成蚀語であるCの䞡方に粟通しおいるこずを意味したす。



PHPの拡匵機胜を䜜成する理由を定矩するこずから始めたしょう。

  1. 蚀語で受け入れられる抜象化レベルのために、PHPから盎接䜜成できないラむブラリたたは特定のOS呌び出しの存圚。
  2. PHPを非暙準的な方法で動䜜させたい堎合。
  3. PHPで䜜成された゜リュヌションは既にありたすが、プロセスの高速化、コンパクト化、およびメモリ消費の削枛を実珟できるこずはご存じでしょう。
  4. 販売したい特別なコヌドがありたす。 ただし、賌入者がコヌドを実行できるこずは重芁ですが、゜ヌスを芋るこずはできたせん。


䞊蚘の理由はすべお十分ですが、拡匵機胜を䜜成するずきは、たずこれが䞻に拡匵機胜であるこずを理解する必芁がありたす。







拡匵機胜ずは䜕ですか



PHPを䜿甚する必芁がある堎合は、拡匵機胜を䜿甚したした。 いく぀かの䟋倖を陀き、PHP蚀語で䜿甚可胜なすべおの関数は特定の拡匵機胜にグルヌプ化されたす。 ほずんどの機胜400以䞊は、 暙準の拡匵機胜の䞀郚です。 PHPの゜ヌスコヌドは、それぞれ玄30個の関数を含む玄86個の拡匵機胜ずずもに配垃されたす。 カりントするず、合蚈で玄2500個の関数が埗られたす。 これだけでは䞍十分な堎合、PECLリポゞトリは100を超える远加の拡匵機胜を提䟛したすが、むンタヌネットの広倧な地域にはさらに倚くの拡匵機胜がありたす。



「たあ、これだけ倚くの機胜が拡匵機胜に組み蟌たれおいるのに、それは拡匵機胜の倖に残っおいたすか」 「拡匵機胜ずは䜕ですか」 PHPの䞭栞は䜕ですか」



PHPのコアは、2぀の独立した郚分の圢で実装されたす。 蚀語の技術的な郚分は、 Zend Engine ZEの圢匏で提瀺されたす。 ZEは、人間が読めるスクリプトをコンピュヌタヌが䜿いやすいトヌクンに倉換しおから実行したす。 さらに、ZEはメモリ管理、倉数の範囲、および関数呌び出しの凊理を担圓したす。

カヌネルの2番目の郚分は、盎接「コア」ず呌ばれるもの PHPコア です。 SAPIレむダヌサヌバヌアプリケヌションプログラミングむンタヌフェむス、PHPむンタヌフェむスず他のサヌバヌ゜フトりェア-CLI、CGI、Apacheなどずのやり取りを担圓したす。 さらに、カヌネルはsafe_modeおよびopen_basedirチェック甚の䞀般化された制埡局これらの機胜はバヌゞョン5.3以降廃止されたず宣蚀されおいたす、およびファむルおよびネットワヌクI / O操䜜をfopen、fread、fwrite関数に関連付けるストリヌム局を実装しおいたす。







ラむフサむクル



特定のSAPIが起動するずたずえば、コマンド/ usr / local / apache / bin / apachectl startを䜿甚しおApacheサヌバヌを起動する堎合、PHPはカヌネルサブシステムを起動しお䜜業を開始したす。 起動手順の終わりに向かっお、各拡匵機胜のコヌドをダりンロヌドし、 モゞュヌル初期化 MINIT関数を呌び出したす。 これにより、各拡匵機胜は、内郚倉数を初期化し、リ゜ヌスにメモリを割り圓お、リ゜ヌスハンドラヌずその機胜をZEに登録できるため、スクリプトがこの拡匵機胜の関数を呌び出すず、ZEは実行するコヌドを認識できたす。



次に、PHPはSAPIレむダヌからのペヌゞ凊理芁求を予期したす。 CGIたたはCLI SAPIの堎合、これはすぐに䞀床だけ発生したす。 SAPI Apache、IIS、たたは他の本栌的なWebサヌバヌの堎合、リモヌトナヌザヌが競合する可胜性があるペヌゞを芁求するたびに、ペヌゞ凊理の芁求が発生したす。 ただし、リク゚ストの送信方法に関係なく、その凊理は、PHPコアがZEにスクリプトを実行するための環境を構成するように芁求し 、各拡匵機胜のリク゚スト初期化 RINIT関数を呌び出すこずから始たりたす。 RINITは、特定の環境倉数を構成し、特定の芁求リ゜ヌスにメモリを割り圓お、他のタスクを実行する機胜を拡匵機胜に䞎えたす。 動䜜䞭のRINIT関数の奜䟋はセッション拡匵です。session.auto_startが有効になっおいる堎合、RINIT関数は自動的にsession_start関数を呌び出し、 $ _SESSION倉数を初期化したす。



リク゚ストが初期化されるず、ZEはPHPスクリプトをトヌクンに倉換し、次に実行可胜なオペコヌドに倉換したす。 これらのオペコヌドのいずれかが拡匵機胜からの関数呌び出しを芁求する堎合、ZEはこの関数を呌び出すための匕数を生成し、完了するたで䞀時的に制埡を転送したす。



スクリプトの実行が完了するず、PHPは拡匵機胜ごずにシャットダりン芁求 RSHUTDOWN関数を呌び出したす。これは、クリヌニングプロセスを完了するために必芁ですたずえば、セッション倉数をディスクに保存する。 次のステップであるZEはクリヌニングプロセスガベヌゞコレクションずも呌ばれたすを実行し、実行されたスクリプトで䜿甚される各倉数に察しおunsetメ゜ッドを実際に実行したすPHP 5.3以降、ガベヌゞコレクションメカニズムが倧幅に改善されたした。



リク゚ストの凊理が完了するず、PHPはSAPIから別のスクリプトを凊理するリク゚スト、たたは完了する信号を予期したす。 CGIたたはCLI SAPIの堎合、「次の芁求」は䞍可胜であるため、SAPIはPHPのシャットダりンをすぐに開始したす。 完了時に、PHPはすべおの拡匵機胜を反埩凊理し、それぞれに察しおモゞュヌルシャットダりン MSHUTDOWN関数を呌び出したす。その埌、独自のカヌネルサブシステムを完了したす。



このプロセスは最初は少し混乱しおいるように芋えるかもしれたせんが、拡匵機胜の䜜業に入るず、埐々にそれを感じたす。







メモリ割り圓お



䞍完党に蚘述された拡匵機胜でのメモリリヌクを回避するために、ZEは远加フラグに基づいお独自の内郚メモリ管理メカニズムを䜿甚しお、デヌタの有効期間を決定したす。 氞続的 氞続的 メモリ割り圓おは、単䞀ペヌゞ芁求の凊理䞭よりも倚くのメモリが割り圓おられるこずを意味したす。 メモリの非氞続的な割り圓おは、解攟関数が呌び出されたかどうかに関係なく、芁求の凊理埌にメモリを解攟するこずを意味したす。 たずえば、ナヌザヌ倉数ぞのメモリの割り圓おは本質的に䞀貫性がありたせん。リク゚ストの凊理が完了するず、ナヌザヌ倉数が䜿甚できなくなるためです。



理論䞊の拡匵では、各リク゚ストの最埌に揮発性メモリを自動的に解攟するこずをZEに任せる可胜性があるずいう事実にもかかわらず、これは掚奚されたせん。 割り圓おられたメモリは長い間芁求されないたたになり、このメモリに関連付けられたリ゜ヌスが正しく閉じられる可胜性が䜎くなり、最終的に、メモリの解攟で混乱を匕き起こすこずは悪い習慣です。 埌で芋るように、すべおのデヌタが正しくクリヌンアップされおいるこずを確認するのは簡単です。



埓来のメモリ割り圓お関数倖郚ラむブラリを䜿甚する堎合にのみ䜿甚する必芁がありたすず、PHP / ZEの定数および非氞続メモリ割り圓おの関数を簡単に比范しおみたしょう。



トラディショナル 気たぐれ åžžèš­
mallocカりント emallocカりント pemallocカりント、1 *
callocカりント、数倀 ecallocカりント、1 pecallocカりント、数倀、1
strdupstr estrdupstr pestrdupstr、1
strndupstr、len estrndupstr、len pemallocおよびmemcpy
無料ptr efreeptr pefreeptr、1
reallocptr、newsize ereallocptr、newsize pereallocptr、newsize、1
malloccount * num + extr ** safe_emalloccount、num、extr safe_pemalloccount、num、extr
* pemallocファミリヌの関数は、パラメヌタヌずしお「氞続性」フラグを受け入れたす。これにより、それらの気たぐれな関数のように動䜜できたす。

䟋emalloc1234は、pemalloc1234、0ず同じです

** safe_emallocおよびPHP 5safe_pemallocは、敎数オヌバヌフロヌの远加チェックを実装したす。









環境のセットアップず構築



PHPずZend Engineの理論に粟通したので、仕事に取り掛かり、䜕かを始めるのを埅぀こずはできたせん。 ただし、その前に、いく぀かのビルドツヌルを入手し、目的に合わせお環境を構成する必芁がありたす。



たず、PHP自䜓ず、PHPに必芁なビルドツヌルのセットが必芁です。 PHPを゜ヌスからビルドする必芁がない堎合は、 この蚘事をご芧になるこずをお勧めしたす。 PHP゜ヌスでバむナリパッケヌゞを䜿甚するのは魅力的かもしれたせんが、そのようなアセンブリには倚くの堎合、開発プロセス䞭に非垞に圹立぀2぀の重芁なプログラムパラメヌタヌ./configureがありたせん。 1぀目はenable-debugです。 このオプションは、セグメンテヌション゚ラヌsegfaultが発生したずきにカヌネルダンプを取埗し、gdbデバッガヌを䜿甚しお゚ラヌが発生した堎所ず理由を確認できるように、PHPを実行可胜ファむルの远加デバッグ情報でコンパむルしたす。

2番目のオプションの名前は、䜿甚するPHPのバヌゞョンによっお異なりたす。 PHP 4.3では、PHP 5から--enable-experimental-ztsず呌ばれ、 --enable-maintainer-ztsに名前が倉曎されたした 。 このオプションを䜿甚するず、PHPはマルチスレッド環境で動䜜するず芋なし、スレッドレス環境では芋えない䞀般的な゚ラヌをキャッチできたすが、マルチスレッド環境では拡匵機胜の動䜜が䞍安定になりたす。

远加オプションを䜿甚しおPHPをコンパむルし、開発サヌバヌたたはワヌクステヌションにむンストヌルするず、最初の拡匵機胜を䜜成するずいうアむデアを打ち砎るこずができたす。







ハロヌワヌルド



すでに必須のHello Worldプログラムの䟋がなければ、プログラミングに関するどの入門蚘事を完成させるこずができたすか この堎合、「Hello World」ずいう単語を含む文字列を返す1぀の関数をPHPに远加する拡匵機胜を䜜成するこずを怜蚎したす。 PHPでは、おそらく次のように実装したす。

<?php



function hello_world() {

return 'Hello World' ;

}



?>






次に、この関数からPHP拡匵機胜を䜜成したしょう。 たず、PHP゜ヌスディレクトリツリヌのext /ディレクトリにhelloサブディレクトリを䜜成し、そこにcdを䜜成したす。 原則ずしお、このディレクトリは゜ヌスのあるディレクトリツリヌの内郚たたは倖郚のどこにでも配眮できたすが、今埌蚘事からリンクできるようにここに配眮する必芁がありたす。 ここでは、3぀のファむルを䜜成する必芁がありたす。

  1. phpizeナヌティリティがコンパむル甚の拡匵機胜を準備するために䜿甚する構成ファむル config.m4 

    PHP_ARG_ENABLE(hello, whether to enable Hello World support,

    [ --enable-hello Enable Hello World support])



    if test "$PHP_HELLO" = "yes"; then

    AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])

    PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)

    fi







  2. 拡匵機胜をダりンロヌドするためにPHPが䜿甚する呜什を含むヘッダヌファむル php_hello.h 

    #ifndef PHP_HELLO_H

    #define PHP_HELLO_H 1



    #define PHP_HELLO_WORLD_VERSION "1.0"

    #define PHP_HELLO_WORLD_EXTNAME "hello"



    PHP_FUNCTION(hello_world);



    extern zend_module_entry hello_module_entry;

    #define phpext_hello_ptr &hello_module_entry



    #endif










  3. hello_world関数の本䜓を含む゜ヌスファむル hello.c 

    #ifdef HAVE_CONFIG_H

    #include "config.h"

    #endif



    #include "php.h"

    #include "php_hello.h"



    static function_entry hello_functions[] = {

    PHP_FE(hello_world, NULL)

    {NULL, NULL, NULL}

    };



    zend_module_entry hello_module_entry = {

    #if ZEND_MODULE_API_NO >= 20010901

    STANDARD_MODULE_HEADER,

    #endif

    PHP_HELLO_WORLD_EXTNAME,

    hello_functions,

    NULL,

    NULL,

    NULL,

    NULL,

    NULL,

    #if ZEND_MODULE_API_NO >= 20010901

    PHP_HELLO_WORLD_VERSION,

    #endif

    STANDARD_MODULE_PROPERTIES

    };



    #ifdef COMPILE_DL_HELLO

    ZEND_GET_MODULE(hello)

    #endif



    PHP_FUNCTION(hello_world)

    {

    RETURN_STRING( "Hello World" , 1);

    }










前の䟋で芋たコヌドのほずんどは、ある皮の「接着剀」です。PHPで拡匵機胜を登録し、それらの間の察話を確立するためのプロトコルです。 実際のコヌドは、 hello.cファむルの最埌の4行のみで、ナヌザヌスクリプトから呌び出すこずができるレベルでアクションを実行したす。 間違いなく、このコヌドは以前芋たPHPコヌドに非垞に䌌おおり、簡単に郚分に分割できたす。



ZEは、スクリプトの完了時にすべおの芁求されたリ゜ヌスが解攟されるこずを確認する高床なメモリ管理メカニズムを䜿甚するこずを思い出しおください。 メモリ管理の䞖界では、同じメモリブロックを再び解攟するこずは倧きなah-ahず芋なされたす。 このアクションは「ダブルリリヌス」ず呌ばれ、セグメンテヌション゚ラヌの䞀般的な原因です。これは、プログラムが、もはや所属しおいないメモリブロックにアクセスしようずするためです。

簡単に蚀えば、ZEが静的文字列この䟋では「Hello World」によっお占有されおいるメモリの解攟を開始しないように、hello_world関数は呌び出されるたびに文字列のコピヌを返す必芁がありたす。

原則ずしお、 RETURN_STRING関数は、枡された行をコピヌする必芁があるずいう事実に責任を持぀こずができるため、埌でそのメモリを安党に解攟できたす。 ただし、文字列にメモリを割り圓お、文字列をコピヌし、結果ずしおコピヌを返すこずは内郚ZE関数の暙準的な動䜜ではないため、 RETURN_STRINGを䜿甚するず、2番目の匕数を䜿甚しお倉数をコピヌするかどうかを指定できたす 。 この抂念をより芖芚的に説明するために、機胜的には前のものず同じである次のコヌドフラグメントに泚意しおください。

PHP_FUNCTION(hello_world)

{

char *str;



str = estrdup( "Hello World" );

RETURN_STRING(str, 0);

}










この䟋では、「Hello World」行に揮発性メモリを手動で割り圓おたした。これは、RETURN_STRING 関数を䜿甚しお呌び出しスクリプトに返され、2番目のパラメヌタヌずしお0を指定したす。







拡匵アセンブリ



この挔習の最埌の手順は、動的にロヌドされるモゞュヌルずしお拡匵機胜を構築するこずです。 前の䟋を正しくコピヌした堎合、ext / hello /ディレクトリから3぀のコマンドを実行するだけです。

$ phpize

$ ./configure --enable-hello

$ make







phpizeナヌティリティが正しく機胜するには、 m4およびautoconfナヌティリティが必芁であるこずに泚意しおください。

これらすべおのコマンドを実行するず、 ext / hello / modules /フォルダヌにhello.soファむルが衚瀺されたす。 これで、他のPHP拡匵機胜ず同様に、単にそのディレクトリを拡匵機胜でコピヌできたすデフォルトでは/ usr / local / lib / php / extensions /ですが、 念のため、 php.iniの蚭定を確認するか、 php-configコマンドを䜿甚したす- -extension-dir そしお、 php.iniにextension = hello.so行を远加しお、PHPの起動時に拡匵機胜のロヌドを初期化したす。 SAPIタむプのCGI / CLIの堎合、これは次に実行されるスクリプトになり、SAPIタむプのApache Webサヌバヌの堎合、これは次のサヌバヌの再起動になりたす。 コマンドラむンから関数を呌び出しおみたしょう。

$ php –r 'echo hello_world();'







すべおが正垞に実行されるず、 hello_world関数がロヌドされた拡匵機胜から行を返すため、スクリプトの結果ずしおHello World行が衚瀺され、 echoコマンドは送信されたものを衚瀺したすこの堎合、 hello_world関数の結果



この䟋では、文字列を返したしたが、他のスカラヌデヌタ型も同様の原則に埓っお返すこずができたす敎数倀の堎合はRETURN_LONG 、浮動小数点数の堎合はRETURN_DOUBLE 、TRUE / FALSEの堎合はRETURN_BOOL 、NULL倀の堎合はRETURN_NULLです。 PHP_FEマクロを含む行をfunction_entity構造に远加し、 hello.cファむル内の察応するPHP_FUNCTIONマクロを远加しお、それぞれの動䜜を芋おみたしょう。

static function_entry hello_functions[] = {

PHP_FE(hello_world, NULL)

PHP_FE(hello_long, NULL)

PHP_FE(hello_double, NULL)

PHP_FE(hello_bool, NULL)

PHP_FE(hello_null, NULL)

{NULL, NULL, NULL}

};



PHP_FUNCTION(hello_long)

{

RETURN_LONG(42);

}



PHP_FUNCTION(hello_double)

{

RETURN_DOUBLE(3.1415926535);

}



PHP_FUNCTION(hello_bool)

{

RETURN_BOOL(1);

}



PHP_FUNCTION(hello_null)

{

RETURN_NULL();

}










さらに、ビルドプロセスがスムヌズに進むように、 php_hello.hヘッダヌファむルのhello_world関数のプロトタむプの暪にこれらの関数のプロトタむプを远加する必芁がありたす。

PHP_FUNCTION(hello_world);

PHP_FUNCTION(hello_long);

PHP_FUNCTION(hello_double);

PHP_FUNCTION(hello_bool);

PHP_FUNCTION(hello_null);










config.m4ファむルに倉曎を加えおいないため、技術的にはphpizeず./configureの手順を繰り返す必芁はありたせんが、すぐにmakeコマンドの実行に進むこずができたす。 ただし、今回は、問題がないこずを確認するために、3぀すべおのアセンブリ手順を再床実行するようにお願いしたす。 さらに、最埌の手順ずしお、 makeの代わりにmake clean all コマンドを䜿甚しお、すべおの゜ヌスファむルが再構築されるようにするこずができたす。 繰り返したが、これはあなたが行った倉曎の皮類のために必芁ではないが、゚ラヌに遭遇するよりも安党にプレむする方が良い。 モゞュヌルがアセンブルされたら、それを拡匵ディレクトリにコピヌしお、叀いバヌゞョンに眮き換えるだけです。



これで、PHPむンタヌプリタヌを再床呌び出しお、远加した機胜をテストするために単玔なスクリプトを枡すこずができたす。 実際に-今すぐやっおみたせんか ここであなたを埅っおいたす...



終わった いいね echoではなくvar_dumpを䜿甚しお各関数の結果を衚瀺した堎合、 hello_boolがTRUEを返すこずに気づいたかもしれたせん。 これは、関数の匕数の1぀の匕数が等しい結果です。 PHPスクリプトの堎合のように、敎数倀0はFALSEず同等であり、その他の数倀はTRUEず同等です。 拡匵機胜の䜜成者は、倚くの堎合、この番号が1である契玄を䜿甚したす。ただし、この契玄を順守するこずが望たしいですが、必須ではありたせん。 さらに、 RETURN_TRUEおよびRETURN_FALSEマクロはより䟿利に䜿甚できたす。 以䞋は、 RETURN_TRUEマクロを䜿甚したhello_bool関数の䟋です 。

PHP_FUNCTION(hello_bool)

{

RETURN_TRUE;

}










マクロを呌び出すずきに括匧が䜿甚されおいないこずに泚意しおください。 この点で、マクロRETURN_TRUEずRETURN_FALSEは、 RETURN_ *ファミリヌの残りのマクロずは異なるため、泚意しおください。



前のすべおの䟋で、結果のコピヌを䜜成するパラメヌタヌを枡さないこずに気づいたかもしれたせん。 この理由は、このような単玔なスカラヌ量の堎合、远加のメモリは必芁ないか、解攟されないためです。



さらに3぀の戻りタむプがありたす。RESOURCE たずえば、 mysql_connect 、 fsockopenたたはftp_connectによっお返されたす、 ARRAY 別名HASH 、およびOBJECT  新しいキヌワヌドによっお返されたす。 それらに぀いおは埌で説明したす。







Ini蚭定



Zend Engineは、INIデヌタを操䜜するための2぀のアプロヌチを提䟛したす。 ここで、最も単玔なものを怜蚎し、グロヌバル倉数に慣れおから、より䞀般的なものに戻りたす。



拡匵甚のphp.iniファむルでhello.greeting蚭定を宣蚀するずしたす。これには、 hello_world関数による出力の倀が含たれたす。 これを行うには、 hello_module_entry構造倉曎の䞀郚ずしお、 hello.cおよびphp_hello.hファむルにいく぀かの倉曎を远加する必芁がありたす 。 php_hello.hファむルのナヌザヌ関数プロトタむプのリストの前に、次のプロトタむプを远加するこずから始めたす。

PHP_MINIT_FUNCTION(hello);

PHP_MSHUTDOWN_FUNCTION(hello);



PHP_FUNCTION(hello_world);

PHP_FUNCTION(hello_long);

PHP_FUNCTION(hello_double);

PHP_FUNCTION(hello_bool);

PHP_FUNCTION(hello_null);










次に、 hello.cファむルを開き、 hello_module_entryの珟圚のバヌゞョンを次のものに眮き換えたす。

zend_module_entry hello_module_entry = {

#if ZEND_MODULE_API_NO >= 20010901

STANDARD_MODULE_HEADER,

#endif

PHP_HELLO_WORLD_EXTNAME,

hello_functions,

PHP_MINIT(hello),

PHP_MSHUTDOWN(hello),

NULL,

NULL,

NULL,

#if ZEND_MODULE_API_NO >= 20010901

PHP_HELLO_WORLD_VERSION,

#endif

STANDARD_MODULE_PROPERTIES

};



PHP_INI_BEGIN()

PHP_INI_ENTRY( "hello.greeting" , "Hello World" , PHP_INI_ALL, NULL)

PHP_INI_END()



PHP_MINIT_FUNCTION(hello)

{

REGISTER_INI_ENTRIES();



return SUCCESS;

}



PHP_MSHUTDOWN_FUNCTION(hello)

{

UNREGISTER_INI_ENTRIES();



return SUCCESS;

}










ここで、 hello.cファむルの先頭に適切な#includeディレクティブを远加しお、INIファむルを操䜜するためのヘッダヌを取埗する必芁がありたす。

#ifdef HAVE_CONFIG_H

#include "config.h"

#endif



#include "php.h"

#include "php_ini.h"

#include "php_hello.h"










最埌に行う必芁があるのは、 hello_world関数を倉曎しお、INI蚭定を䜿甚するようにするこずです。

PHP_FUNCTION(hello_world)

{

RETURN_STRING(INI_STR( "hello.greeting" ), 1);

}










INI_STRから取埗した倀のコピヌを䜜成しおいるこずに泚意しおください。 その理由は、 INI_STRによっお返される文字列は静的であるためです。 ぀たり、倉曎しようずするず、PHP環境が䞍安定になったり、クラッシュしたりする可胜性がありたす。



この章で行われた倉曎の最初のセットは、 MINITおよびMSHUTDOWNずいう2぀の新しいメ゜ッドを既に知っおいるはずです 。 前述のように、これらのメ゜ッドは、SAPIレむダヌの最初の起動時ず、その䜜業を完了する過皋でそれぞれ呌び出されたす。 リク゚スト䞭たたはリク゚スト間では呌び出されたせん。 この䟋では、拡匵機胜で宣蚀されたphp.iniファむルから゚ントリを登録するために䜿甚されたす。 これらの関数リ゜ヌスにも、オブゞェクトずストリヌムハンドラを登録できたす。



hello_world関数では、 INI_STRマクロを䜿甚しお、 hello.greating゚ントリの珟圚の倀を文字列ずしお取埗したす。 以䞋にリストされおいる敎数倀、浮動小数点倀、ブヌル倀を取埗するための関数は他にもたくさんありたす。 さらに、これらの関数には、拡匵子がORIGの重耇がありたす。これにより、 php.iniファむルに曞き蟌たれた圢匏でレコヌドの倀を取埗できたす  .htaccessファむルたたはini_set関数を䜿甚しお倉曎する前。

珟圚の倀 元の倀 皮類
INI_STR名前 INI_ORIG_STR名前 Char *NULL終了
INI_INT名前 INI_ORIG_INT名前 長い眲名
INI_FLT名前 INI_ORIG_FLT名前 二重眲名
INI_BOOL名前 INI_ORIG_BOOL名前 Zend_bool


PHP_INI_ENTRY関数に移りたしょう。 最初のパラメヌタヌは、関心のあるphp.iniファむルの゚ントリの名前を含む文字列です。 php.iniの゚ントリ名の衝突を避けるために、関数に名前を付けるずきず同じ芏則を䜿甚する必芁がありたす。名前は、拡匵子の名前ず䞀臎するプレフィックスで始たる必芁がありたす。 たた、契玄では、ピリオドは.iniファむルの元の構成名から拡匵子名を分離する必芁があるず芏定されおいたす。 この堎合、蚭定名はhello.greetingのようになりたす。

2番目のパラメヌタヌは初期倀で、倀が数倀であるかどうかに関係なく、垞にchar *に蚭定されたす。 これは、ファむル自䜓がテキストであるため、INIファむルのすべおの蚭定が本質的にテキストであるずいう事実の結果です。 スクリプト内でINI_INT 、 INI_FLT、たたはINI_BOOLマクロを埌で䜿甚するず、それらのタむプが倉換されたす。

3番目のパラメヌタヌは、アクセスレベル修食子です。 これは、特定のINI蚭定をい぀どのように倉曎できるかを決定するビットマスクです。 register_globalsなどの䞀郚の蚭定では、スクリプトが解決する機䌚が䞎えられる前のリク゚スト凊理の準備䞭にのみ意味があるため、 ini_set関数を䜿甚しおスクリプト内で倀を倉曎するこずは蚱可されたせん。 allow_url_fopenなどのその他の蚭定は、ナヌザヌがini_setたたは.htaccessディレクティブを䜿甚しお倉曎するこずを蚱可しない管理蚭定です 。 このパラメヌタヌのデフォルト倀はPHP_INI_ALL倀で、蚭定倀はどこでも倉曎できるこずを瀺したす。 PHP_INI_SYSTEM | PHP_INI_PERDIRの倀も可胜です。これは、蚭定倀がphp.iniたたは.htaccessファむル内のディレクティブを介しお、ini_set関数を介しお倉曎できるこずを瀺したす。 たたは、 PHP_INI_SYSTEM倀が可胜です。぀たり、蚭定はphp.iniファむルでのみ倉曎でき 、他の堎所では倉曎できたせん。



最埌の4番目のパラメヌタヌでは、 ini_set関数を䜿甚しお蚭定が倉曎されたずきに呌び出されるコヌルバック関数を指定できたす。 これにより、拡匵機胜は、蚭定を倉曎する条件をより完党に制埡したり、新しい蚭定倀に応じお察応する機胜を呌び出すこずができたす。







グロヌバル倉数



倚くの堎合、拡匵機胜は別のリク゚ストで倉数を凊理する必芁があり、その倀を同時に凊理できる他のリク゚ストから独立しおいたす。 マルチスレッドSAPIでは、これは非垞に簡単に行えたす。゜ヌスファむルでグロヌバル倉数を宣蚀し、必芁なずきに参照するだけです。 問題は、PHPはマルチスレッドWebサヌバヌApache 2やISSなどで動䜜するように蚭蚈されおいるため、䞀方のスレッドで䜿甚されるグロヌバル倉数を他方のグロヌバル倉数ずは別に保存する必芁があるこずです。 PHPは、 ZTS Zend Thread Safetyず呌ばれるこずもあるTSRM Thread Safe Resource Manager抜象化レむダヌを䜿甚するこずにより、このタスクを倧幅に簡玠化したす。 実際、この蚘事ではTSRMの䞀郚が既に䜿甚されおいたすが、気づいおいたせんでした。 今のずころそれはあなたにずっお難しすぎるので、それらを芋぀けようずしないでください。



スレッドセヌフグロヌバル倉数を䜜成する最初の郚分は、他のグロヌバル倉数ず同様に、宣蚀するこずです。 䟋ずしお、初期倀が0になるlong型のグロヌバル倉数を1぀宣蚀したす。hello_long関数が呌び出されるたびに、グロヌバル倉数の倀を増やしおその倀を返したす。

#define PHP_HELLO_H行の盎埌に、次のコヌドスニペットをphp_hello.hに远加したす。

#ifdef ZTS

#include "TSRM.h"

#endif



ZEND_BEGIN_MODULE_GLOBALS(hello)

long counter;

ZEND_END_MODULE_GLOBALS(hello)



#ifdef ZTS

#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)

#else

#define HELLO_G(v) (hello_globals.v)

#endif










たた、 RINITメ゜ッドを䜿甚する必芁があるため、ヘッダヌでプロトタむプを宣蚀する必芁がありたす。

PHP_MINIT_FUNCTION(hello);

PHP_MSHUTDOWN_FUNCTION(hello);

PHP_RINIT_FUNCTION(hello);










hello.cファむルに移動しお、むンクルヌドブロックの埌に次のコヌドを远加したしょう。

#ifdef HAVE_CONFIG_H

#include "config.h"

#endif



#include "php.h"

#include "php_ini.h"

#include "php_hello.h"



ZEND_DECLARE_MODULE_GLOBALS(hello)








PHP_RINIThelloを远加しおhello_module_entryを倉曎したす 。

zend_module_entry hello_module_entry = {

#if ZEND_MODULE_API_NO >= 20010901

STANDARD_MODULE_HEADER,

#endif

PHP_HELLO_WORLD_EXTNAME,

hello_functions,

PHP_MINIT(hello),

PHP_MSHUTDOWN(hello),

PHP_RINIT(hello),

NULL,

NULL,

#if ZEND_MODULE_API_NO >= 20010901

PHP_HELLO_WORLD_VERSION,

#endif

STANDARD_MODULE_PROPERTIES

};










そしお、リク゚ストの開始時に初期化を凊理する関数をいく぀か远加するずずもに、 MINIT関数を倉曎したす。

static void php_hello_init_globals(zend_hello_globals *hello_globals)

{

}



PHP_RINIT_FUNCTION(hello)

{

HELLO_G(counter) = 0;



return SUCCESS;

}



PHP_MINIT_FUNCTION(hello)

{

ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);



REGISTER_INI_ENTRIES();



return SUCCESS;

}










最埌に、グロヌバル倉数を远加しおhello_long関数を倉曎できたす。

PHP_FUNCTION(hello_long)

{

HELLO_G(counter)++;



RETURN_LONG(HELLO_G(counter));

}










php_hello.hファむルに加えた倉曎では、いく぀かのマクロ-ZEND_BEGIN_MODULE_GLOBALSずZEND_END_MODULE_GLOBALSを䜿甚したした。 圌らの助けにより、 zend_hello_globals構造䜓が定矩され、long型の倉数が1぀含たれおいたす。 その埌、 HELLO_Gマクロが定矩されたした。これにより、コンパむルモヌドに応じおマルチスレッドの有無にかかわらずスレッドプヌルから倀を取埗したり、グロヌバルスコヌプから倀を取埗したりできたす。

hello.cファむルで、 ZEND_DECLARE_MODULE_GLBALSマクロを䜿甚しお、 zend_hello_globals構造䜓をグロヌバル非スレッドセヌフビルドの堎合たたはストリヌムリ゜ヌスプヌルのメンバヌずしお宣蚀したした。 拡匵機胜の䜜成者である私たちは、ZEがすべおの䜜業を凊理するため、このメカニズムに぀いお考える必芁はありたせん。 最埌に、 ZEND_INIT_MODULE_GLOBALS関数を䜿甚しお、スレッドセヌフなリ゜ヌス識別子を割り圓おたす。

お気付きかもしれたせんが、 php_hello_init_globals関数は実際には䜕もしたせん。 さらに、カりンタヌを0に初期化するず、 RINIT関数にあるこずが刀明したした。 なんで

この質問に答える鍵は、これらの関数が呌び出された瞬間にありたす。 php_hello_init_globals関数は 、新しいプロセスたたはスレッドが開始されたずきにのみ呌び出されたす。 ただし、各プロセスは耇数の芁求を凊理できるため、この関数を䜿甚しおカりンタヌを0に初期化するこずは、最初の芁求に察しおのみtrueになりたす。 同じプロセスぞの埌続のリク゚ストは、叀いカりンタ倀で匕き続き機胜するため、最初からレポヌトを開始するこずはありたせん。 リク゚ストごずにカりンタを0に初期化するために、 RINIT関数を䜿甚したした。これは、既に読んだように、各スクリプト凊理リク゚ストの前に呌び出されたす。 少なくずも、察応するパラメヌタヌZEND_INIT_MODULE_GLOBALSずしおNULLをinit関数に枡すず、スレッドサポヌトのないプラットフォヌムではセグメンテヌション゚ラヌが発生するため、 php_hello_init_globals関数をコヌドに含めたした。







INI蚭定ずグロヌバル倉数



すでにご存知のように、 PHP_INI_ENTRY関数を䜿甚しお宣蚀されたphp.iniファむルの蚭定は文字列ずしお読み取られ、必芁に応じおINI_INT 、 INI_FLTおよびINI_BOOL関数を䜿甚しお他の圢匏に倉換されたす 。 ただし、スクリプトの実行䞭にいく぀かの蚭定が頻繁に芁求され、ファむルからの䞍芁な読み取り操䜜が倧量に発生する可胜性がありたす。 幞いなこずに、特定のデヌタ型にINI蚭定を保存し、蚭定倀が倉曎されたずきにのみ型倉換を実行するようにZEに指瀺するこずができたす。 䜜業䞭にこの可胜性を確認するために、もう1぀のINI蚭定を宣蚀したす。今回はブヌル型で、珟時点でカりンタヌをデクリメントするか増やすかを決定したす。 p hp_hello.hの MODULE_GLOBALSブロックを次のように倉曎するこずから始めたす。

ZEND_BEGIN_MODULE_GLOBALS(hello)

long counter;

zend_bool direction;

ZEND_END_MODULE_GLOBALS(hello)










次のステップは、 PHP_INI_BEGINブロックを倉曎しおINI蚭定を宣蚀するこずです。

PHP_INI_BEGIN()

PHP_INI_ENTRY( "hello.greeting" , "Hello World" , PHP_INI_ALL, NULL)

STD_PHP_INI_ENTRY( "hello.direction" , "1" , PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)

PHP_INI_END()










ここで、 init_globals関数の蚭定を初期化したす。

static void php_hello_init_globals(zend_hello_globals *hello_globals)

{

hello_globals->direction = 1;

}










最埌に、 hello_long関数のINI蚭定の倀を䜿甚しお、カりンタヌの操䜜を遞択したす。

PHP_FUNCTION(hello_long)

{

if (HELLO_G(direction)) {

HELLO_G(counter)++;

} else {

HELLO_G(counter)--;

}



RETURN_LONG(HELLO_G(counter));

}










以䞊です STD_PHP_INI_ENTRYマクロの3番目のパラメヌタヌずしお枡されるOnUpdateBoolメ゜ッドメ゜ッドはZEの䞀郚を䜿甚しお、 php.ini 、 .htaccessファむルから取埗された蚭定倀のタむプが、察応するTRUE / FALSE倀にini_set関数を䜿甚しお自動的に倉換されたす。これをスクリプト内で盎接取埗できたす。 STD_PHP_INI_ENTRY関数の最埌の3぀のパラメヌタヌは、倉曎するグロヌバル倉数、拡匵機胜のグロヌバル倉数の構造、およびそれらが含たれるグロヌバル倉数のコンテナヌの名前をPHPに䌝えたす。







次は



この蚘事では、倀を返す関数を゚クスポヌトし、INI蚭定をロヌドしお凊理し、リク゚ストの凊理党䜓でそれらを保持する単玔なPHP拡匵の構造を調べたした。



次の蚘事では、PHP倉数の内郚構造、それらのストレヌゞず凊理方法に぀いお芋おいきたす。 関数が呌び出されたずきにプログラムからパラメヌタヌを取埗するために䜿甚されるzend_parse_parameters関数を䜿甚し、配列、オブゞェクト、リ゜ヌスなど、関数からより耇雑な結果を返す方法を開きたす。







PS



䞀郚の堎所では、翻蚳はかなり自由な圢匏になっおいたすが、これは翻蚳の難しさの結果であり、私はそれを完党に克服するこずはできたせんでした。さらに、翻蚳には、私に関係があるず思われる小さな远加がいく぀か含たれおいたす。

この蚘事による拡匵機胜のビルドプロセスは、PHP 5.3.2の゜ヌスコヌドでテストされたした。



で著者このテヌマに関するいく぀かの蚘事がある1、2.1、2.2、3。4番目の蚘事は、明らかに、このテヌマに焊点を圓おた「PHPの拡匵ず埋め蟌み」の著者による出版物を考慮しお、決しお光を芋るこずはないでしょう。php.netで



Zend Engineに぀いお読むこずもできたす。



All Articles