PHP 拡張機能の書き方
PHP 拡張機能の書き方
2010 年 6 月 14 日
PHP 拡張機能の書き方
翻訳: taft@wjl.cn
Ver 0.1
最終更新日 2006/1/19
WJL Studio @ wjl.cn 2006
ディレクトリ入門クイックスタート
メモリ管理
PHP 関数からの戻り値
self_concat() の完成
サンプルの概要
折り返し 3 番目-party 拡張機能
リソースを利用する最初の PHP 関数の作成
グローバル変数
カスタム INI ディレクティブの追加
スレッドセーフなリソース管理マクロ
概要
用語集 はじめにPHP の成功の 1 つは、膨大な数の拡張機能を利用できることです。 Web 開発者がどのようなニーズを持っているかに関係なく、このニーズは PHP 配布パッケージで見つかる可能性が最も高くなります。 PHP 配布パッケージには、さまざまなデータベース、グラフィックス ファイル形式、圧縮、および XML テクノロジ拡張機能をサポートする多くの拡張機能が含まれています。
拡張 API の導入により、PHP3 は大幅に進歩しました。拡張 API メカニズムにより、PHP 開発コミュニティは多数の拡張機能を簡単に開発できるようになりました。 2 つのバージョンを経た現在でも、API は PHP3 と非常によく似ています。拡張機能の主なアイデアは、PHP の内部メカニズムとスクリプト エンジン自体を拡張機能作成者から可能な限り隠し、開発者が API に精通していることのみを要求することです。
独自の PHP 拡張機能を作成する理由は 2 つあります。 1 つ目の理由は、PHP がまだサポートしていないテクノロジーをサポートする必要があるということです。これには通常、既製の C ライブラリをラップして PHP インターフェイスを提供することが含まれます。たとえば、FooBase というデータベースが市場に投入される場合、PHP から FooBase の C 関数ライブラリを呼び出すのに役立つ PHP 拡張機能を作成する必要があります。この作業は 1 人だけで行っても、(そうするなら) PHP コミュニティ全体で共有することもできます。 2 番目の、あまり一般的ではない理由は、パフォーマンスまたは機能上の理由から、ビジネス ロジックを作成する必要があることです。
上記 2 つの理由があなたには関係がなく、自分には冒険心がないと感じる場合は、この章を飛ばしていただいても構いません。
この章では、いくつかの拡張 API 関数を使用して、比較的単純な PHP 拡張を作成する方法を説明します。カスタム PHP 拡張機能の開発を検討しているほとんどの開発者にとって十分な情報が含まれています。プログラミング コースを学習する最良の方法の 1 つは、この章のヒントとなるいくつかの非常に単純な例に取り組むことです。基本を理解したら、ドキュメントやソース コードを読んだり、メーリング リストのニュースグループのディスカッションに参加したりして、インターネット上で知識を深めていくことができます。したがって、この章では入門に重点を置きます。 UNIX では、ext_skel と呼ばれるスクリプトを使用して拡張機能のスケルトンが作成されます。スケルトン情報は、拡張機能のインターフェイスを記述する定義ファイルから取得されます。したがって、スケルトンを構築するには UNIX を使用する必要があります。 Windows 開発者は、ext_skel の代わりに Windows ext_skel_win32.php を使用できます。
ただし、この章で開発した拡張機能を使用して PHP をコンパイルする手順は、UNIX コンパイル システムのみを対象としています。この章の API の説明はすべて、UNIX および Windows 用に開発された拡張機能に関連しています。
この章を読み終えると、シンプルなビジネス ロジック拡張を構築する方法がわかります。 C 関数ライブラリ、特に fopen() などのいくつかの標準的な C ファイル操作関数のパッケージ拡張をお勧めします
クイック スタート
このセクションでは、スクリプト エンジンの基本構造に関する知識を紹介するのではなく、直接説明します。拡張機能のコーディングについて説明されているため、拡張機能の全体像をすぐに理解できなくても心配する必要はありません。 Web サイトを開発していて、文字列を n 回繰り返す関数が必要だとします。以下は PHP で書かれた例です:
Function self_concat($string, $n)
{
$result = "";
for ($i = 0; $i function、および非常に長い文字列と大きな値の n を関数に渡すということは、スクリプト内でかなりの量の文字列の連結とメモリの再割り当てが行われることを意味し、割り当て可能な関数がある場合、スクリプトの実行が大幅に遅くなる可能性があります。結果の文字列を保存し、$string を n 回繰り返すには大量のメモリがあれば十分であるため、ループの繰り返しごとにメモリを割り当てる必要はありません。
拡張機能の関数を作成する最初のステップは次のとおりです。この例では、関数定義ファイルには関数プロトタイプ self_concat():
string self_concat(string str, int n) が 1 行だけ含まれています。
関数定義ファイルの一般的な形式は、関数 1 行です。オプションのパラメーターを定義し、bool、float、int、array などの多数の PHP 型を使用できます。
として保存します。 PHP オリジナル コード ディレクトリ ツリー内の myfunctions.def ファイル。スケルトン コンストラクターが関数定義ファイルを実行します。コンストラクター スクリプトは ext_skel と呼ばれ、PHP ソース コード ディレクトリ (README.EXT_SKEL) の ext/ ディレクトリに配置されます。 PHP ソース コードのメイン ディレクトリに詳細情報があります))。関数定義を myfunctions.def というファイルに保存し、拡張機能に myfunctions という名前を付けたいとします。次のコマンドを実行して拡張機能スケルトン
を作成します。 ext_skel --extname=myfunctions --proto =myfunctions.def
このコマンドは、ext/ ディレクトリの下に myfunctions/ ディレクトリを作成します。最初に行うことは、実際の C コードを作成してテストできるようにスケルトンをコンパイルすることです。拡張機能をコンパイルするには 2 つの方法があります:
ロード可能なモジュールまたは DSO (Dynamic Shared Object) として PHP への静的コンパイル
2 番目の方法の方が簡単に開始できるため、この章では静的コンパイルを使用します。ロード可能な拡張モジュールのコンパイルに興味がある場合は、PHP ソース コードのルート ディレクトリにある README.SELF-CONTAINED_EXTENSIONS ファイルを読むことができます。拡張機能をコンパイルするには、拡張機能ディレクトリ ext/myfunctions/ 内の config.m4 ファイルを変更する必要があります。この拡張機能は外部 C ライブラリをラップしません。 --enable-myfunctions 構成スイッチのサポートを PHP ビルド システムに追加する必要があります (with-extension スイッチは、ユーザーが関連するライブラリへのパスを指定する必要がある拡張機能に使用されます) C ライブラリ)。この構成を有効にするには、次の 2 行で自動生成されたコメントを削除します。
PHP_ARG_ENABLE(myfunctions, myfunctions サポートを有効にするかどうか,
[ --enable-myfunctions Include myfunctions support])
あとは、PHP オリジナル コードのルート ディレクトリで ./buildconf を実行するだけです。このコマンドは新しい構成スクリプトを生成します。 ./configure --help 出力を表示することで、新しい構成オプションが構成ファイルに含まれているかどうかを確認できます。ここで、好みの構成オプションと --enable-myfunctions をオンにして、PHP を再構成します。 最後に重要なことですが、make を使用して PHP を再コンパイルします。
ext_skel は、拡張スケルトンに 2 つの PHP 関数を追加する必要があります。実装する self_concat() 関数と、myfunctions が PHP にコンパイルされているかどうかを確認するconfirm_myfunctions_compiled() 関数です。 PHP の拡張機能の開発が完了したら、後者は削除できます。
このスクリプトを実行すると、次のような出力が生成されます:
「おめでとうございます! ext/myfunctions
config.m4 が正常に変更されました。モジュール myfunctions が PHP にコンパイルされました。」さらに、ext_skel スクリプトは myfunctions.php というスクリプトを生成します。これを使用して、拡張機能が PHP に正常にコンパイルされたことを確認することもできます。この拡張機能でサポートされているすべての機能がリストされています。
拡張機能をコンパイルする方法がわかったので、次は self_concat() 関数を実際に学習します。
以下は、ext_skel スクリプトによって生成されるスケルトン構造です。
/* {{{ proto string self_concat(string str, int n) */ PHP_FUNCTION(self_concat) } char *str = int argc = ZEND_NUM_ARGS; (); int str_len; long n; if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE) return; } */ 自動生成された PHP 関数の周囲にいくつかのコメントが含まれています。これらの関数は、vi や Emacs などのエディターでコード ドキュメントとコードの折りたたみを自動的に生成するために使用されます。関数自体はマクロ PHP_FUNCTION() を使用して定義されており、Zend エンジンに適した関数プロトタイプを生成できます。ロジック自体は、呼び出し関数のパラメーターとロジック自体のセマンティック部分に分割されます。
関数によって渡されたパラメーターを取得するには、zend_parse_parameters() API 関数を使用できます。以下は、この関数のプロトタイプです:
zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);
最初のパラメーターは、関数に渡されるパラメーターの数です。通常のアプローチは、これを ZEND_NUM_ARGS() に渡すことです。これは、関数に渡される引数の合計数を表すマクロです。 2 番目のパラメータはスレッド セーフ用であり、後で説明する TSRMLS_CC マクロを常に渡します。 3 番目のパラメーターは、関数で予期されるパラメーターのタイプを指定する文字列で、その後にパラメーター値で更新する必要がある変数のリストが続きます。 PHP は緩い変数定義と動的な型判定を使用するため、異なる型のパラメーターを期待される型に変換できます。たとえば、ユーザーが整数変数を渡しても、関数が浮動小数点数を必要とする場合、zend_parse_parameters() は整数を対応する浮動小数点数に自動的に変換します。実際の値を期待される型に変換できない場合 (整数から配列への変換など)、警告がトリガーされます。
次の表に、指定できるタイプを示します。完全を期すために説明されていないいくつかのタイプもリストします。
型指定子に対応する C 型の説明 l long シンボリック整数 d double 浮動小数点数 s char *、int バイナリ文字列、長さ b zend_bool 論理型 (1 または 0) r zval * リソース (ファイル ポインタ、データベース接続など) .) a zval * 連想配列 o zval * 任意の型のオブジェクト O zval * 指定された型のオブジェクト。ターゲット オブジェクトのクラス タイプ z zval * zval を何も操作せずに指定する必要があります。最後のいくつかのオプションの意味を簡単に理解するには、zval が Zend エンジンの値コンテナであることを知っておく必要があります [1]。変数がブール型、文字列、その他の型のいずれであっても、その情報は常に zval 共用体に含まれます。この章では、zval に直接アクセスせず、追加のマクロを介して操作します。以下は、後続のコードをよりよく理解できるように、C での zval を多少なりとも示しています。
typedef Union _zval {
long lval;
double dval;
struct {
char *val;
} zval; type を使用して、zval コンテナーを使用する代わりにネイティブ C 型で関数パラメーターの値を取得します。
zend_parse_parameters() が渡されたパラメータの値を変更し、変更された値を返すためには、参照を渡す必要があります。 self_concat() を詳しく見てみましょう:
if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)
return;関数の戻り値 FAILUER (成功は SUCCESS) は、成功したかどうかを判断するために使用されます。失敗した場合はすぐに戻り、zend_parse_parameters() が警告メッセージをトリガーします。この関数は文字列 l と整数 n を受け取ることを目的としているため、その型標識として「sl」が指定されています。 s には 2 つのパラメータが必要なので、参照 char * と int (str と str_len) を zend_parse_parameters() 関数に渡します。可能な限り、関数がバイナリ セーフな環境で動作するように、コード内では常に文字列長 str_len を使用することを忘れないでください。関数がバイナリ文字列を処理しなくても構わない場合を除き、strlen() と strcpy() を使用しないでください。バイナリ文字列は、null を含む文字列です。バイナリ形式には、画像ファイル、圧縮ファイル、実行可能ファイルなどが含まれます。 "l" は引数を 1 つだけ取るので、それを n への参照として渡します。わかりやすくするために、スケルトン スクリプトによって生成される C 変数名は関数プロトタイプ定義ファイル内のパラメーター名と同じですが、これは必須ではありませんが、実際にはそうすることが推奨されます。
変換ルールに戻ります。 self_concat() 関数への次の 3 つの呼び出しでは、str、str_len、n に同じ値が与えられます。
self_concat("321", 5);
self_concat( " 321", "5");
str は文字列 "321" を指し、str_len は 3 に等しく、n は 5 に等しい。
str は文字列 "321" を指し、str_len は 3 に等しく、n は 5 に等しい。
PHP 関数に返される接続文字列を実装するコードを記述する前に、メモリ管理と、PHP 内から関数値を返すために使用される API という 2 つの重要なトピックについて話しておく必要があります。
メモリ管理
ヒープからメモリを割り当てるための PHP API は、標準の C API とほぼ同じです。拡張機能を記述するときは、C に対応する次の API 関数を使用します (改めて説明する必要はありません)。
efree(void *ptr); size );
erealloc(void *ptr, size_t size);
estrdup(const char *s, unsigned int length);経験豊富な C プログラマーは次のように考えるはずです: 「えっ? 標準 C には strndup() がないの?」 はい、その通りです。GNU 拡張機能は一般に Linux で使用できるからです。 estrndup() は、PHP の特別な関数にすぎません。これは estrdup() と同様に動作しますが、文字列が繰り返される回数を指定でき (終端の null 文字は必要ありません)、バイナリ セーフです。これが、estrdup() の代わりに estrndup() を使用することをお勧めする理由です。
ほとんどの場合、これらのメモリ割り当て関数を使用する必要があります。拡張機能がリクエスト間で持続するメモリを割り当てる必要があるため、malloc() を使用する必要がある場合がありますが、何をしているのかわからない場合は、常に上記の関数を使用する必要があります。これらのメモリ関数が使用されず、代わりに標準の C 関数によって割り当てられたメモリがスクリプト エンジンに返された場合、PHP はクラッシュします。
これらの関数の利点は、偶然解放されなかった割り当てられたメモリがページリクエストの終了時に解放されることです。したがって、実際のメモリ リークは発生しません。ただし、デバッグとパフォーマンスの両方の理由から、このメカニズムに依存しないでください。解放すべきメモリは必ず解放してください。残りの利点は、マルチスレッド環境でのパフォーマンスの向上、デバッグ モードでのメモリ エラーの検出などです。
もう 1 つの重要な理由は、これらのメモリ割り当て関数の戻り値が null かどうかを確認する必要がないことです。メモリ割り当てに失敗すると、E_ERROR エラーが発行され、拡張機能には戻りません。
PHP 関数からの戻り値
拡張 API には、関数から値を返すための豊富なマクロのセットが含まれています。これらのマクロには主に 2 つの種類があります。 1 つ目は RETVAL_type() 形式で、戻り値を設定しますが、C コードは実行を継続します。これは通常、スクリプト エンジンに制御を渡す前にクリーンアップ作業を行い、C の return ステートメント「return」を使用して PHP に戻る場合に使用されます。後者のマクロの方が一般的で、その形式は RETURN_type() です。戻り値の型が設定され、制御が PHP に返されます。 次の表では、存在するほとんどのマクロについて説明します。
戻り値を設定して関数を終了します。 戻り値マクロの戻り値の型とパラメータを設定します。 RETURN_LONG(l) RETVAL_LONG(l) 整数 RETURN_BOOL(b) RETVAL_BOOL(b) ブール数値 (1 または 0) RETURN_NULL() RETVAL_NULL() NULL RETURN_DOUBLE( d) RETVAL_DOUBLE (d) 浮動小数点数 RETURN_STRING(s, dup) RETVAL_STRING(s, dup) 文字列。 dup が 1 の場合、エンジンはコピーを使用して estrdup() を呼び出して を繰り返します。 dup が 0 の場合、s RETURN_STRINGL(s, l, dup) RETVAL_STRINGL(s, l, dup) 長さ l の文字列値を使用します。前のマクロと同じですが、 s の長さが指定されているため、より速くなります。 RETURN_TRUE RETVAL_TRUE ブール値 true を返します。 このマクロには括弧がないことに注意してください。 RETURN_FALSE RETVAL_FALSE ブール値 false を返します。このマクロには括弧がないことに注意してください。 RETURN_RESOURCE(r) RETVAL_RESOURCE(r) リソースハンドル。 self_concat() の完了 メモリを割り当て、PHP 拡張関数から関数値を返す方法を学習したので、self_concat() のエンコードを完了できます:
/* {{{ proto string self_concat(string str, int) n )
*/
PHP_FUNCTION(self_concat)
}
char *str = NULL;
int argc = ZEND_NUM_ARGS();
int str_len; 🎜 >Char *result; /* 結果の文字列を指す */
char *ptr; /* コピー先の次の場所を指す */
int result_length; /* 結果の文字列の長さ */
If (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)
return;
/* 結果の長さを計算します */
result_length = (str_len * n);
/* 結果にメモリを割り当てます */
result = (char *) emalloc(result_length + 1);
/* 結果の先頭のポイント */
ptr = result; > while (n--) {
/* str を結果にコピーします */
memcpy(ptr, str, str_len);
/* 次に書き込む位置を指すように ptr をインクリメントします* /
ptr += str_len;
}
/* 文字列はバイナリ文字列であっても常に null で終了します */
*ptr = 'bool file_close(resource filehandle)
file_close() はリソース ハンドルを受け取り、操作が成功したかどうかを示す true/false を返します。
string file_read(resource filehandle, int size)
file_read()はリソースハンドルと読み取った総バイト数を受け取り、読み取った文字列を返します。
bool file_write(resource filehandle, stringbuffer)
file_write はリソース ハンドルと書き込まれた文字列を受け取り、操作が成功したかどうかを示す true/false を返します。
bool file_eof(resource filehandle)
file_eof() はリソース ハンドルを受け取り、ファイルの終わりに到達したかどうかを示す true/false を返します。
したがって、関数定義ファイル - ext/ ディレクトリに myfile.def として保存 - には以下が含まれます:
resource file_open(string filename, string mode)
bool file_close(resource filehandle)
String file_read(resource filehandle, int size)
bool file_write(resource filehandle, stringbuffer)
bool file_eof(resource filehandle)
次に、ext_skel スクリプトを使用して、ext./ 元のコード ディレクトリで以下を実行します。コマンド:
./ext_skel --extname=myfile --proto=myfile.def
次に、前の例の手順に従って、新しく作成したスクリプトをコンパイルします。 FETCH_RESOURCE() マクロ行を含むコンパイル エラーが発生するため、スケルトン スクリプトは正常にコンパイルされません。スケルトン拡張機能をスムーズにコンパイルするには、エラー行をコメント アウトするだけです [3]。
リソース リソースは、あらゆる情報を保持できる抽象データ構造です。前述したように、この情報には通常、ファイル ハンドル、データベース接続構造、その他の複雑なタイプのデータが含まれます。
リソースを使用する主な理由は次のとおりです: リソースは集中キューによって管理され、PHP 開発者がスクリプト内で明示的に解放しない場合には自動的に解放されます。
たとえば、スクリプトを作成し、スクリプト内で mysql_connect() を呼び出して MySQL 接続を開くとしますが、データベース接続リソースが使用されなくなった場合、mysql_close() は呼び出されません。 PHP では、リソース メカニズムがリソースをいつ解放すべきかを検出し、現在のリクエストの終了時、または通常はそれ以前にリソースを解放できます。これにより、メモリ リークを軽減する「防弾」メカニズムが実現します。このようなメカニズムがないと、いくつかの Web リクエストの後、Web サーバーで多くのメモリ リソースがリークし、サーバーのクラッシュやエラーが発生する可能性があります。
リソースタイプを登録してリソースを使用するにはどうすればよいですか? Zend Engine を使用すると、リソースの使用が非常に簡単になります。最初に行う必要があるのは、リソースをエンジンに登録することです。この API 関数を使用します:
int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number)
この関数はリソース タイプ ID を返します。これは、拡張機能内のグローバル変数として保存する必要があります。他のリソース API に渡されるときに必要に応じて使用されます。 ld: リソースが解放されたときに呼び出される関数。 pld は、リクエスト間で持続する永続リソースに使用されますが、この章では説明しません。 type_name は説明的な型名を持つ文字列で、module_number はエンジンによって内部的に使用されます。この関数を呼び出すときは、すでに定義されている module_number 変数を渡すだけです。
例に戻ります。次のコードを元の myfile.c ファイルに追加します。このファイルには、zend_register_list_destructors_ex() 登録関数に渡されるリソース解放関数の定義が含まれています (リソース解放関数は、zend_register_list_destructors_ex() が呼び出されたときにすでに定義されているように、早めにファイルに追加する必要があります):
static void myfile_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
FILE *fp = (FILE *) rsrc->ptr;
fclose(fp)
}
PHP_MINIT_FUNCTION に登録行を追加します() は次のコードのようになります:
PHP_MINIT_FUNCTION(myfile)
🎜> REGISTER_INI_ENTRIES();
*/
le_myfile = zend_register_list_destructors_ex(myfile_dtor,NULL,"standard-c-file") , module_number); myfile ext_skel スクリプトによって定義されたグローバル変数です。
PHP_MINIT_FUNCTION() は、モジュール (拡張機能) に先行する起動関数であり、拡張機能に公開される API の一部です。次の表に、使用可能な機能の簡単な説明を示します。
関数宣言マクロ 関数宣言マクロ セマンティクス PHP_MINIT_FUNCTION() PHP がロードされると、モジュール起動関数がエンジンによって呼び出されます。これにより、エンジンはリソース タイプ、INI 変数の登録などの初期化を実行します。 PHP_MSHUTDOWN_FUNCTION() PHP が完全にシャットダウンされると、エンジンによってモジュール シャットダウン関数が呼び出されます。通常、INI エントリの登録を解除するために使用されます。 PHP_RINIT_FUNCTION() 各 PHP リクエストの開始時に、リクエスト前起動関数が呼び出されます。通常、事前リクエスト ロジックを管理するために使用されます。 PHP_RSHUTDOWN_FUNCTION() 各 PHP リクエストが終了すると、リクエスト前のシャットダウン関数が呼び出されます。クリーンアップ要求の前に関数を開始するロジックが適用されることがよくあります。 PHP_MINFO_FUNCTION() モジュール情報関数は、phpinfo() を呼び出すときに呼び出され、モジュール情報を出力します。 新しいリソースを作成して登録するには、file_open() 関数を実装します。ファイルを開いて FILE * を取得するときは、リソース メカニズムを使用してそれを登録する必要があります。次の主なマクロは登録関数を実装します。
ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type);
マクロ パラメータの説明については表を参照してください。
ZEND_REGISTER_RESOURCE マクロ パラメータ マクロ パラメータのパラメータ タイプ rsrc_result zval * zval * 登録されたリソース情報に設定されます。 rsrc_pointer リソース データへのポインタ rsrc_type リソース タイプを登録するときに取得されたリソース ID。 ZEND_REGISTER_RESOURCE () マクロの使用方法を理解したら、file_open() 関数の作成を開始する準備が整いました。もう一つ取り上げなければならない主題があります。
PHP がマルチスレッドサーバー上で実行されている場合、標準の C ファイルアクセス機能は使用できません。これは、あるスレッドで実行されている PHP スクリプトが現在の作業ディレクトリを変更するため、別のスレッドのスクリプトは相対パスを使用してターゲット ファイルを開くことができないためです。このエラーの発生を防ぐために、PHP フレームワークには、現在の作業ディレクトリに依存するアクセス機能を置き換えるために使用される VCWD (仮想現在の作業ディレクトリ) と呼ばれるマクロが用意されています。これらのマクロは、置き換えられた関数と同じ機能を持ち、透過的に処理されます。標準の C ライブラリ プラットフォームがない場合、VCWD フレームワークはサポートされません。たとえば、Win32 に chown() が存在しない場合、対応する VCWD_CHOWN() マクロは定義されません。
VCWD リスト 標準 C ライブラリ VCWD マクロの説明 getcwd() VCWD_GETCWD() fopen() VCWD_FOPEN open() VCWD_OPEN() 2 つのパラメーターのバージョン open() VCWD_OPEN_MODE() 3 つのパラメーターの open() のバージョン Creat () VCWD_CREAT ( ) Chdir () VCWD_CHDIR () Getwd () vcwd_getWD () VCWD_REALPATH () rename () vcwd_rename () vcwd_stat () vcwd_lstat () unlink () vcwd_unlink () mkdir ( ) VCWD_MKDIR() rmdir() VCWD_RMDIR() opendir() _OPENDIR () Popen() VCWD_POPEN() access() VCWD_ACCESS() utime() VCWD_UTIME() chmod() VCWD_CHMOD() chown() VCWD_CHOWN() 書き込みリソースの使用率 最初の PHP 関数
file_open() の実装は非常に簡単です
PHP_FUNCTION(file_open)
{
char *filename = NULL;
char *mode = NULL;
int argc = ZEND_NUM_ARGS(); ;
int mode_len;
FILE *fp; , &mode_len) == FAILURE) {
return;
}
fp = VCWD_FOPEN(ファイル名, モード); = NULL) {
RETURN_FALSE;
}
ZEND_REGISTER_RESOURCE(return_value, fp, le_myfile);
}
リソース登録マクロの最初のパラメータ return_value が見つからないことがわかります。ここ。この変数は、拡張フレームワークによって zval * 型の関数戻り値として自動的に定義されます。戻り値に影響を与える前述の RETURN_LONG() および RETVAL_BOOL() マクロは、return_value の値を変更します。したがって、プログラムは取得したファイルポインタ fp を登録し、登録したリソースに return_value を設定していると容易に推測できます。
リソースにアクセスするには、次のマクロを使用してリソースにアクセスする必要があります (マクロパラメータの説明については表を参照してください)
ZEND_FETCH_RESOURCE(rsrc, rsrc_type, pass_id,default_id,
resource_type_name, resource_type); > ZEND_FETCH_RESOURCE マクロパラメータ パラメータの意味 rsrc リソース値が保存される変数名。リソースと同じタイプである必要があります。 rsrc_type リソースを内部的に正しいタイプに変換するために使用される rsrc のタイプ pass_id 検索するリソース値 (zval ** など) default_id 値が -1 でない場合、この ID が使用されます。リソースの実装に使用されるデフォルト値。 resource_type_name エラー メッセージで使用されるリソースの短縮名。 resource_type 登録されたリソースのリソース タイプ ID。file_eof() を実装できます。
PHP_FUNCTION(file_eof)
int argc = ZEND_NUM_ARGS(); >FILE *fp;
if (zend_parse_parameters(argc TSRMLS_CC, "r", &filehandle) ==FAILURE) {
return;
if (fp == NULL) {
RETURN_FALSE;
}
if (feof(fp) 関数。したがって、この場合、ファイル ポインターを取得し、 fclose() を呼び出してファイルを閉じ、リソースを削除する必要はありません。このマクロを使用すると、 file_close() を実装できます。
PHP_FUNCTION(file_close)
{
int argc = ZEND_NUM_ARGS();
zval *filehandle = NULL; TSRMLS_CC, "r", &filehandle) == FAILURE) {
return;
}
if (zend_list_delete(Z_RESVAL_P(filehandle)) == FAILURE) {
RETURN_FALSE; > RETURN_TRUE;
}
zend_parse_parameters() を使用してパラメータ リストからリソースを取得する場合、リソース ID を取得します。 Z_RESVAL_P() マクロを使用して ID を取得し、その ID を zend_list_delete() に渡します。zval に格納されている値にアクセスするために使用される一連のマクロがあります (zend_parse_parameters() を参照してください)。ほとんどの場合、c 型に対応する値を返しますが、リソース Zval アクセスの場合も含め、zval を直接処理する必要があります。 文字列値 char * Z_STRLEN、Z_STRLEN_P、Z_STRLEN_PP 文字列長値 int Z_RESVAL、Z_RESVAL_P、Z_RESVAL_PP リソース値。 long Z_ARRVAL、Z_ARRVAL_P、Z_ARRVAL_PP 共用体配列 HashTable * Z_TYPE、Z_TYPE_P、Z_TYPE_PP Zval 型列挙 (IS_NULL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT 、IS_BOOL、IS_RESOURCE) Z_OBJPROP、Z_OBJPROP_P 、 PROP_PP オブジェクト属性ハッシュ (この章では説明しません) HashTable * Z_OBJCE、Z_OBJCE_P、Z_OBJCE_PP オブジェクト クラス情報 (この章では説明しません) zend_class_entry zval 値にアクセスするためのマクロ すべてのマクロには 3 つの形式があります。1 つは zval を受け入れるもの、もう 1 つは zval *s を受け入れるもの、最後のマクロは zval *s を受け入れるものですズヴァル**s。それらの違いは、最初のものには接尾辞がなく、zval * には接尾辞 _P (ポインターを表す) があり、最後の zval ** には接尾辞 _PP (2 つのポインターを表す) が付いていることです。
これで、file_read() 関数と file_write() 関数を個別に完了するのに十分な情報が得られました。 可能な実装は次のとおりです:
PHP_FUNCTION(file_read)
{
int argc = ZEND_NUM_ARGS();
zval *filehandle = NULL; 🎜> char *result;
size_t bytes_read;
if (zend_parse_parameters(argc TSRMLS_CC, "rl", &filehandle,&size) == FAILURE) {
}
ZEND_FETCH_RESOURCE( fp , FILE *, &filehandle, -1, "standard-cfile", le_myfile);
result = (char *) emalloc(size+1);
bytes_read = fread(result, 1, size, fp) ;
結果[バイト読み取り] = 'ZEND_BEGIN_MODULE_GLOBALS(myfile)
int global_value;
char *global_string;ファイルの数行後、スケルトン スクリプトによって MYFILE_G(v) マクロが自動的に定義されます。これらのグローバル変数にアクセスするには、すべてのコードでこのマクロを使用する必要があります。これにより、マルチスレッド環境では、相互に排他的な操作を必要とせずに、アクセスされるグローバル変数が 1 つのスレッドのコピーのみになることが保証されます。
グローバル変数を有効にするために最後に行う必要があるのは、myfile.c 内のコメントを削除することです:
ZEND_DECLARE_MODULE_GLOBALS(myfile)
各 PHP リクエストの開始時にグローバル変数を初期化したい場合があります。さらに、例として、グローバル変数はすでに割り当てられたメモリを指しているため、各 PHP リクエストの終了時に解放する必要があります。これらの目的を達成するために、グローバル変数メカニズムは、グローバル変数のコンストラクターとデストラクターを登録するための特別なマクロを提供します (マクロ パラメーターの説明については表を参照してください):
ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor)
表 ZEND_INIT_MODULE_GLOBALS マクロパラメータ パラメータ 意味 module_name ZEND_BEGIN_MODULE_GLOBALS() マクロに渡される同じ拡張名。 globals_ctor コンストラクター ポインター。 myfile 拡張子の場合、関数プロトタイプは void php_myfile_init_globals (zend_myfile_globals *myfile_globals) globals_dtor デストラクター ポインターに似ています。たとえば、php_myfile_init_globals(zend_myfile_globals *myfile_globals) myfile.c でコンストラクターと ZEND_INIT_MODULE_GLOBALS() マクロの使用例を確認できます。
カスタム INI ディレクティブを追加します
INI ファイル (php.ini) を実装すると、PHP 拡張機能がそれぞれの INI エントリを登録してリッスンできるようになります。これらの INI エントリに php.ini、Apache の htaccess、またはその他の設定方法によって値が割り当てられている場合、登録された INI 変数は常に正しい値に更新されます。 INI フレームワーク全体には、柔軟性を実現するためのさまざまなオプションが多数あります。いくつかの基本 (そして良いスタート) について説明します。この章の他の資料を活用すれば、日常の開発作業のニーズに対処できるようになります。
PHP_INI_BEGIN()/PHP_INI_END() マクロ間の STD_PHP_INI_ENTRY() マクロを介して PHP INI 命令を登録します。たとえば、この例では、myfile.c の登録プロセスは次のようになります。
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("myfile.global_value", "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_myfile_globals, myfile_globals)
STD_PHP_INI_ENTRY("myfile.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_myfile_globals, myfile_globals)
PHP_INI_END()
STD_PHP_INI_ENTRY() 以外のマクロも使用できますが、これはマクロは最も一般的に使用されます。ほとんどのニーズを満たすことができます (マクロ パラメーターの説明については表を参照してください):
STD_PHP_INI_ENTRY(name,default_value, modifiable, on_modify, property_name, struct_type, struct_ptr)
STD_PHP_INI_ENTRY マクロ パラメーター テーブル
パラメータの意味 名前 INI エントリ名 default_value INI ファイルで指定されていない場合のエントリのデフォルト値。デフォルト値は常に文字列です。 INI エントリを変更できる状況を設定する変更可能なビットフィールド。可能な値は次のとおりです。
PHP_INI_SYSTEM php.ini または http.conf などのシステム ファイルで変更できます。
PHP_INI_PERDIR で変更できます。
PHP_INI_USER。 > ?? PHP_INI_ALL INI エントリの変更を処理する on_modify コールバック関数はどこでも変更できます。独自のハンドラーを作成する必要はありません。以下に提供される関数を使用してください。含まれるもの:
OnUpdateInt
OnUpdateString
OnUpdateBool OnUpdateStringUnempty
OnUpdateReal property_name 更新する必要がある変数名 struct_type 変数が存在する構造体のタイプ。通常はグローバル変数メカニズムが使用されるため、この型は zend_myfile_globals と同様に自動的に定義されます。 struct_ptr グローバル構造体名。グローバル変数メカニズムを使用する場合、名前は myfile_globals です。 最後に、カスタム INI エントリ メカニズムが適切に動作するには、PHP_MINIT_FUNCTION(myfile) の REGISTER_INI_ENTRIES() 呼び出しと PHP_MSHUTDOWN_FUNCTION(myfile) の UNREGISTER_INI_ENTRIES() 呼び出しのコメントをそれぞれ解除する必要があります。
2 つのグローバル変数の例のいずれかにアクセスするには、拡張子に MYFILE_G(global_value) と MYFILE_G(global_string) を記述するだけで簡単です。
php.iniに以下の2行を記述すると、MYFILE_G(global_value)の値が99になります。
; php.ini 次の行は、INI エントリ myfile.global_value を 99 に設定します。
myfile.global_value = 99
スレッドセーフなリソース管理マクロ
ここで、マクロが開始されていることにお気づきでしょう。 Explorer を使用) はどこでも使用されます。これらのマクロは、前述したように、拡張機能に独自のグローバル変数を持つ可能性を提供します。
PHP 拡張機能を作成するときは、マルチプロセス環境でもマルチスレッド環境でも、このメカニズムを利用して拡張機能のグローバル変数にアクセスします。グローバル変数アクセス マクロ (MYFILE_G() マクロなど) を使用する場合は、TSRM コンテキスト情報が現在の関数に表示されることを確認する必要があります。パフォーマンス上の理由から、Zend エンジンはこのコンテキスト情報をパラメータとして、PHP_FUNCTION() の定義など、より多くの場所に渡そうとします。このため、PHP_FUNCTION() 内でアクセス マクロ (MYFILE_G() マクロなど) を使用するコードを記述するときに、特別な宣言を行う必要はありません。ただし、PHP 関数がグローバル変数にアクセスする必要がある他の C 関数を呼び出す場合、コンテキストが追加のパラメーターとして C 関数に渡されるか、コンテキストが抽出されます (これは低速です)。
コンテキストを抽出するためにグローバル変数にアクセスする必要があるコード ブロックの先頭で TSRMLS_FETCH() を使用します。例:
void myfunc()
{
TSRMLS_FETCH();
MYFILE_G(myglobal) = 2;
コードをより最適化したい場合は、方法は、コンテキストを関数に直接渡すことです (前述したように、PHP_FUNCTION() スコープで自動的に使用可能になります)。 TSRMLS_C (呼び出しの C) マクロと TSRMLS_CC (呼び出しとコンマの CC) マクロを使用できます。前者はコンテキストが単一の引数として受け取られる場合にのみ使用する必要があり、後者は複数の引数を受け入れる関数に使用する必要があります。後者の場合、名前に従ってコンマがコンテキストの前に置かれるため、TSRMLS_CC を最初の関数パラメーターにすることはできません。
関数プロトタイプでは、TSRMLS_D マクロと TSRMLS_DC マクロをそれぞれ使用して、コンテキストが受信されていることを示すことができます。
以下は、コンテキストを渡すパラメーターを使用して、前の例を書き直したものです。
void myfunc(TSRMLS_D)
{
MYFILE_G(myglobal) = 2;
}
PHP_FUNCTION(my_php_function)
{ 🎜> …
}
概要
これで、独自の拡張機能を作成するのに十分な学習ができました。この章では、PHP 拡張機能を作成して理解するための重要な基本について説明します。 Zend エンジンが提供する拡張 API は非常に豊富で、オブジェクト指向の拡張機能を開発できます。多くの高度な機能に関するドキュメントはほとんどありません。もちろん、この章で学んだ基本知識に基づいて、既存のソース コードを参照することで多くのことを学ぶことができます。
詳細については、http://www.php.net/manual/en/zend.php にある PHP マニュアルの「拡張 PHP」の章を参照してください。あるいは、PHP 自体の開発を中心とした PHP 開発者メーリング リスト Internals@lists.php.net に参加することも検討してください。新しい拡張機能生成ツール PECL_Gen (http://pear.php.net/package/PECL_Gen) もチェックアウトできます。これは開発中で、この章で使用する ext_skel よりも多くの機能を備えています。