| ユーザフォーラムで議論/質問 | マニュアル検索 | ハイライト | ハイライトオフ | ポータル | php spot |
MySQL Native Driverプラグイン のアーキテクチャこのセクションでは、mysqlndプラグイン のアーキテクチャについての概要を示します。 MySQL Native Driver の概要 mysqlndプラグイン を開発する前に、mysqlnd そのものがどのように成り立っているのかを少し知っておくと役に立ちます。mysqlnd は次に示すモジュールからなります:
C言語のオブジェクト指向パラダイム ソースコードレベルで、mysqlnd は オブジェクト指向を実装するためのパターンを採用しています。 C言語では、オブジェクトを表現するために struct(構造体) を使います。struct のメンバがオブジェクトのプロパティを表現します。関数を指している struct のメンバがメソッドを表現します。 C++ や Java のような言語と異なり、C言語におけるオブジェクト指向のパラダイムでは決まった継承のルールがありませんが、従う必要があるルールがいくつかあります。このルールについては後に述べます。 PHP のライフサイクル PHP のライフサイクルを考えるとき、ふたつの基本的なサイクルが存在します。
PHPエンジンが起動するとき、PHP はモジュールを初期化する (MINIT) 関数を登録されたエクステンションごとに呼び出します。これによって、各々のモジュールが PHPエンジンが処理を行うライフサイクルの間存在するリソースを割り当てたり、変数を定義することができます。PHPエンジンが終了するときには、 エンジンが終了(MSHUTDOWN)関数をエクステンション毎に呼び出します。 PHPエンジンが起動している間、エンジンはたくさんのリクエストを受けとります。それぞれのリクエストは別のライフサイクルを構成します。リクエスト毎にPHPエンジンはリクエストの初期化関数をエクステンション毎に呼び出します。エクステンション側では、リクエストの処理に必要な変数の定義やリソースの割り当てを行うことができます。リクエストのサイクルが終了するときは、PHPエンジンがリクエストの終了(RSHUTDOWN)関数をエクステンション毎に呼び出します。これによって、エクステンションは必要となるあらゆるクリーンアップ処理を行うことができます。 プラグインはどうやって動くか mysqlndプラグイン は mysqlnd を使うエクステンションが mysqlnd を呼び出すときの制御を奪うことによって動作します。これは mysqlnd の関数テーブルを取得し、バックアップし、カスタムの関数テーブルと置き換えることによって実現されます。この関数テーブルが、プラグインが必要とする関数を呼び出すのです。 次のコードは、mysqlnd の関数テーブルを置き換える方法を示しています: /* a place to store original function table */ struct st_mysqlnd_conn_methods org_methods; void minit_register_hooks(TSRMLS_D) { /* active function table */ struct st_mysqlnd_conn_methods * current_methods = mysqlnd_conn_get_methods(); /* backup original function table */ memcpy(&org_methods, current_methods, sizeof(struct st_mysqlnd_conn_methods); /* install new methods */ current_methods->query = MYSQLND_METHOD(my_conn_class, query); } 接続関数テーブルの管理は、モジュールを初期化(MINIT)している間に行わなければなりません。関数テーブルはグローバルに共有されるリソースです。マルチスレッド環境で、TSRMを有効にしてPHPをビルドした環境では、グローバルに共有されたリソースをリクエストを処理している間に操作すると、ほぼ確実に衝突が起こります。
親クラスのメソッドを呼び出す オリジナルの関数テーブルをバックアップしている場合、オリジナルの関数テーブルのエントリに含まれる関数を呼び出すことができます - これが親メソッドです。 場合によっては、Connection::stmt_init() のように、派生メソッドで他のあらゆる処理より先に親メソッドを呼び出すことが決定的に重要な場合があります。 MYSQLND_METHOD(my_conn_class, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) { php_printf("my_conn_class::query(query = %s)\n", query); query = "SELECT 'query rewritten' FROM DUAL"; query_len = strlen(query); return org_methods.query(conn, query, query_len); /* return with call to parent */ } プロパティを拡張する mysqlndオブジェクトは C言語の構造体で表現されます。実行時に、C言語の構造体に新たにメンバを追加することは不可能です。mysqlndオブジェクト のユーザーは、プロパティを単純にオブジェクトに追加することはできません。 mysqlnd_plugin_get_plugin_<object>_data()ファミリーの適切な関数を使って、任意のデータ (プロパティ) を mysqlnd オブジェクトに追加することができます。オブジェクトをメモリに割り付ける際に、mysqlnd はオブジェクトの最後に 任意のデータ向けの void * ポインタを保持するためのメモリ空間を予約しておきます。 mysqlnd は プラグインひとつにつき、ひとつの void *ポインタ を保持する空間を予約しています。 次の表で、特定のプラグインでポインタの位置を計算する方法を示します:
mysqlndオブジェクト のコンストラクタを継承する計画がある場合、それが許可されていることを必ず頭にいれておいてください! 次のコードはプロパティを拡張する方法を示しています: /* any data we want to associate */ typedef struct my_conn_properties { unsigned long query_counter; } MY_CONN_PROPERTIES; /* plugin id */ unsigned int my_plugin_id; void minit_register_hooks(TSRMLS_D) { /* obtain unique plugin ID */ my_plugin_id = mysqlnd_plugin_register(); /* snip - see Extending Connection: methods */ } static MY_CONN_PROPERTIES** get_conn_properties(const MYSQLND *conn TSRMLS_DC) { MY_CONN_PROPERTIES** props; props = (MY_CONN_PROPERTIES**)mysqlnd_plugin_get_plugin_connection_data( conn, my_plugin_id); if (!props || !(*props)) { *props = mnd_pecalloc(1, sizeof(MY_CONN_PROPERTIES), conn->persistent); (*props)->query_counter = 0; } return props; } プラグインの開発者には、プラグイン用のデータに使われるメモリを管理する責任があります。 mysqlnd のメモリアロケータを使うことを推奨します。これらのメモリアロケータ関数は次のような規約に従って命名されています: mnd_*loc() mysqlnd のメモリアロケーターには役に立つ機能がいくつかあります。たとえばデバッグビルドでない環境でデバッグ用のアロケータを使う機能などです。
上記の表で許可されていない場合は、モジュールを初期化した(MINIT)後のいかなる場合であっても関数テーブルを変更してはいけません。 クラスによっては、メソッドの関数テーブルへのポインタが含まれている場合があります。このようなクラスのインスタンスはすべて、同じ関数テーブルを共有しています。混乱を避けるため、特にマルチスレッドの環境下では、このような関数テーブルは MINIT (モジュール初期化) 時にだけ変更するようにしてください。 そうでないクラスでは、グローバルに共有された関数テーブルのコピーを使っています。クラスの関数テーブルのコピーがオブジェクトとともに作成されます。それぞれのオブジェクトは自分の関数テーブルを使います。これによって開発者は二つの選択肢が得られます: MINIT(モジュール初期化) 時にオブジェクトのデフォルトの関数テーブルを変更するか、同じクラスの他のインスタンスに影響を与えることなくオブジェクトのメソッドを追加で変更することができます。 関数テーブルを共有する利点は、パフォーマンスの向上です。関数テーブルをそれぞれの、すべてのオブジェクトにコピーする必要がないからです。
コンストラクタを全面的に置き換えないことを強く推奨します。コンストラクタはメモリへの割り当てを実行します。mysqlndプラグインAPI と オブジェクトのロジックにとってメモリへの割り当ては決定的に重要です。開発者が警告を無視してコンストラクタへのフックを強行する場合、コンストラクタで何かをする前に親のコンストラクタを少なくとも呼び出すべきです。 すべての警告に関わらず、コンストラクタを継承することが役に立つ場合があります。コンストラクタは、オブジェクトの関数テーブルを共有されていないオブジェクトの関数テーブルと一緒に修正するのにぴったりな場所です。共有されていないオブジェクトの関数テーブルの例としては、結果セットやネットワーク、wire protocol が挙げられます。
デストラクタは、mysqlnd_plugin_get_plugin_<object>_data() で取得したプロパティを破棄するのに適切な場所です。 ここで挙げたデストラクタは、オブジェクトそのものを破棄する 実際の mysqlnd メソッドと一致しないかもしれません。しかし、これらのデストラクタは、開発者がフックし、プラグインデータを解放する最良の場所なのです。コンストラクタに関しては、開発者がメソッドを完全に置き換えることができるものの、推奨されません。上の表に複数のメソッドが示されていた場合、mysqlnd がどのメソッドをはじめに呼び出したかに関わらず、開発者はここで示されているすべてのメソッドをフックし、プラグインのデータを解放する必要があります。 プラグインに推奨されるやり方は、単純にメソッドをフックし、メモリを解放した後に親クラスの実装を速やかに呼び出すことです。 警告
PHP 5.3.0 から 5.3.3 までは、PHPに存在するバグによって、プラグインはプラグイン固有のデータを持続的接続に結びつけることができませんでした。これは、ext/mysql と ext/mysqli が mysqlnd で必要な end_psession() メソッドを呼び出していなかったため、プラグインがメモリリークしていた可能性があったためでした。このバグは PHP 5.3.4 で修正されています。 |
![]() |
各種マニュアル:
PHPマニュアル |
PEARマニュアル |
Smarty(英語)マニュアル |
PHP-GTKマニュアル |
「MySQL Native Driverプラグイン のアーキテクチャ」をGoogle検索
|