ホームページ >バックエンド開発 >PHPチュートリアル >PHP 拡張機能を作成するためのステップバイステップ ガイド

PHP 拡張機能を作成するためのステップバイステップ ガイド

WBOY
WBOYオリジナル
2016-07-25 09:12:321016ブラウズ

1. まず書いてください

インターネットの急速な発展とランプ アーキテクチャの人気に伴い、PHP はますます多くの拡張機能をサポートしており、これが PHP の開発を直接促進しています。

ただし、PHP にはスクリプト言語に関する避けられない問題もあり、そのパフォーマンスは C などのコンパイル言語とは大きく異なるため、パフォーマンスの問題を考慮する場合は、PHP 拡張機能を使用して問題を解決するのが最善です。

それでは、PHP拡張機能を作成する方法を説明します。例から始めましょう (この記事には C の基礎が必要です)。

2. 問題を解決する

システムにおいて、配列の二乗和が頻繁に必要な場合は、次のように書くことができます。

  1. function array_square_sum($data){
  2. $sum = 0;
  3. foreach($data as $value){
  4. $sum += $value * $value;
  5. }
  6. return $sum ;
  7. }
コードをコピー

実際の実行中、PHP zend エンジンはこの段落を C 言語に翻訳し、毎回メモリ割り当てが必要になります。したがって、パフォーマンスは比較的悪いです。さらに、パフォーマンスの考慮事項に基づいて、これを行うための拡張機能を作成できます。

3. 拡張機能を書く

拡張機能を構築するには、少なくとも 2 つのファイルが必要です。 1 つはコンフィギュレーター ファイルで、この拡張機能をコンパイルするために少なくともどの依存ライブラリが必要かをコンパイラーに指示します。2 つ目は実際の実行ファイルです。

3.1 フレームワークを生成する

複雑に聞こえますが、実際には、拡張フレームワークを理解するのに役立つツールがあります。 PHP ソース コードには ext_skel というツールがあり、拡張フレームワークの生成に役立ちます。

  1. liujun@ubuntu:~/test/php-5.5.8/ext$ ls ext_skel
  2. ext_skel
コードをコピー

次に、それを使用して拡張された array_square_sum を生成します。 ($はプロンプトコマンドを表します)

  1. $ ./ext_skel --extname=array_square_sum
  2. ディレクトリarray_square_sumの作成
  3. 基本ファイルの作成: config.m4 config.w32 .svnignore array_square_sum.c php_array_square_sum.h クレジット実験テスト/001.php array_square_sum.php [完了].
  4. 新しい拡張機能を使用するには、次の手順を実行する必要があります:
  5. 1. $ vi ext/array_square_sum/config.m4
  6. 3. buildconf
  7. 4. $ ./configure --[with|enable]-array_square_sum
  8. 5. $ make
  9. 6. $ vi ext/array_square_sum/array_square_sum c
  10. 8. $ make
  11. ext/array_square_sum/config.m4 に満足するまでステップ 3 ~ 6 を繰り返し、
  12. モジュールが PHP にコンパイルされたことを確認します。その後、
  13. コードの作成を開始し、最後の 2 つのステップを繰り返します。必要に応じて頻繁に実行します。
コードをコピーします
コマンドを実行すると、ターミナルに新しい拡張機能の作成方法が表示されます。ファイルの内容を確認すると、config.m4、php_array_square_sum.h、array_square_sum.c など、さらに重要なファイルがいくつかあることがわかります。これらについては、以下で 1 つずつ説明します。

liujun@ubuntu:~/test/php-5.5.8/ext$ ll array_square_sum/
    total 44
  1. drwxr-xr-x 3 liujun liujun 4096 1月29日15:40 ./
  2. drwxr-xr-x 80 liujun liujun 4096 1月29日 15:40 ../
  3. -rw-r--r-- 1 liujun liujun 5548 1月29日 15:40 array_square_sum.c
  4. -rw-r--r-- 1 liujun liujun 532 1月29日15:40 array_square_sum.php
  5. -rw-r--r-- 1 liujun liujun 2354 1月29日 15:40 config.m4
  6. -rw-r--r-- 1 liujun liujun 366 1月29日 15:40 config.w32
  7. -rw-r--r-- 1 liujun liujun 16 1月29日 15:40 クレジット
  8. -rw-r--r-- 1 liujun liujun 0 1月29日 15:40 実験
  9. -rw-r--r-- 1 liujun liujun 3112 1月29日 15:40 php_array_square_sum.h
  10. -rw-r--r-- 1 liujun liujun 16 1月29日 15:40 .svnignore
  11. drwxr-xr-x 2 liujun liujun 4096 1月29日 15:40 testing/
コードをコピー

3.2 設定ファイル config.m4

dnl PHP_ARG_WITH(array_square_sum、array_square_sum サポート用,
    dnl コメントが整列していることを確認してください:
  1. dnl [ --with-array_square_sum Include array_square_sum support])
コードをコピー
dnldnl

php_arg_with(array_square_sum、array_square_sum supportの場合、コメントが調整されていることを確認してください:
[ - with-array_square_sumにはarray_square_sum supportを含む])
  1. コピーコード

これは、./configure の処理中にこの拡張設定ファイルに到達したときに、PHP_ARG_ENABLE の 2 番目のパラメーターが表示されるときに、enable-sample オプションを呼び出すことができるための最小要件です。エンドユーザーの場合、ヘルプ情報として /configure --help が表示されます

3.3 ヘッダーファイル

php_array_square_sum.h を変更し、confirm_array_square_sum_compiled をconfirm_array_square_sum に変更します。これは、今後実際に呼び出す関数の名前です。もちろん、confirm_array_square_sum_compiled を削除せずに、関数confirm_array_square_sum を直接追加することもできます。

  1. PHP_FUNCTION(confirm_array_square_sum_compiled);
コードをコピー

  1. PHP_FUNCTION(array_square_sum);
である必要がありますコードをコピー

3.3 ソースコード

array_square_sum.c を変更し、confirm_array_square_sum_compiled をconfirm_array_square_sum に変更します。これは、この拡張機能を登録するための関数です。3.2 でconfirm_array_square_sum を直接追加した場合は、このステップで直接confirm_array_square_sum を追加します。

  1. const zend_function_entry array_square_sum_functions[] = {
  2. PHP_FE(confirm_array_square_sum_compiled, NULL) /* テストのため、後で削除します */
  3. PHP_FE_END /* array_square_sum_functions[] の最後の行である必要があります */
  4. };
コード

をコピーして

  1. const zend_function_entry array_square_sum_functions[] = {
  2. PHP_FE(array_square_sum, NULL) /* テスト用に後で削除します */
  3. PHP_FE_END /* array_square_sum の最後の行である必要があります。 _関数[] * /
  4. };
コードをコピー

次に、最も重要なステップは、confirm_array_square_sum を書き換えることです。現時点では、confirm_array_square_sum_compiled をconfirm_array_square_sum に書き換えるだけです (confirm_array_square_sum_compiled は 3.1 では削除されていないため、confirm_array_square_sum を追加する必要があります)。

  1. PHP_FUNCTION(confirm_array_square_sum_compiled)
コードをコピー

  1. PHP_FUNCTION(array_square_sum)
  2. {
  3. zval* array_data;
  4. HashTable *ht_data;
  5. int ret;
  6. char* key;
  7. uint Index;
  8. zval **pdata;
  9. double sum = 0; として書き換えられました。
  10. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_data) == FAILURE) {
  11. return;
  12. }
  13. ht_data = Z_ARRVAL_P(array_data);
  14. zend_hash_internal_pointer_reset(ht_data); while ( HASH_KEY_NON_EXISTANT != (ret = zend_hash_get_current_key(ht_data, &key, &index, 0)) ) {
  15. ret = zend_hash_get_current_data(ht_data, &pdata);
  16. if( Z_TYPE_P(*pdata) == IS_LONG){
  17. sum += Z_LVAL_P(*pdata) * Z_LVAL_P( * pdata);
  18. }else {
  19. RETURN_FALSE;
  20. }
  21. zend_hash_move_forward(ht_data);
  22. }
  23. zend_hash_internal_pointer_end(Z_ARRVAL_P(array_data));
  24. RETVAL_DOUBLE(sum);
  25. }
コードをコピー

PHP は弱い型付け言語であり、そのデータは zval 構造体に保存されます (

詳細については、「php extensiondevelopment.pdf」などの専門的な情報を参照してください)。

    typedef Union _zval {
  1. long lval;
  2. double dval;
  3. struct {
  4. char *val;
  5. int len;
  6. } str;
  7. HashTable *ht;
  8. zend_object_value obj;
  9. }
  10. コピーコード
関数によって渡されたパラメーターを取得するには、zend_parse_parameters() API 関数を使用できます。以下は関数のプロトタイプです:

zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);
  1. コードをコピー

カーネル内のマクロを使用して、zend_parse_parameters() 関数の最初のいくつかのパラメータを ZEND_NUM_ARGS() TSRMLS_CC の形式で直接生成できます。2 つの間にはスペースがありますが、カンマはないことに注意してください。名前からわかるように、ZEND_NUM_ARGS() はパラメータの数を表します。次に、共通パラメータのタイプ (C 言語の printf に似ています)、その後に共通パラメータのリストが続きます。
次の表に、一般的なパラメータのタイプを示します。

パラメータの種類 オブジェクトCタイプ 手順
l 長い 整数
b ブール ブール値
文字* 文字列
d ダブル 浮動小数点数
配列(zval*) 配列
z ズヴァル* 不確実性zval


さらに、配列は大きなハッシュテーブルとして実装されるため、zend_hash_get_current_key は配列を走査し、マクロ Z_LVAL_P(zval*) を使用して実際の値を取得できます。最後に、結果を合計することができます。 RETVAL_DOUBLE(value)もマクロであり、戻り値はdoubleであり、詳細は「php拡張機能開発.pdf」を参照してください。

このメイン関数の開発がついに完了しました。

3.4 設定ファイルを生成する

次に ~/php/bin/phpize を実行します


    /home/liujun/php/bin/phpize
コードをコピー

    の設定:
  1. PHP APIバージョン: 20121113
  2. ZendモジュールAPI番号: 20121 212
  3. Zend 拡張 API いいえ: 220121212
コードをコピー


実行可能スクリプトのconfigureがarray_square_sumに表示されていることがわかります。

3.5 コンピレーション

システムのデフォルトの php-config-path が現在使用している php パスではない可能性があるため、コンパイル時に php-config PATH を使用することが最善です。

    liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ./configure --with-php-config=/home/liujun/php/bin/php-config
コピーコード
が正常にコンパイルされると、ターミナルに次のプロンプトが表示されます:

    creating libtool
  1. appendingconfigurationtag "CXX" to libtool
  2. configure:creating ./config.status
  3. config.status:creating config .h
  4. config .status: config.h は変更されていません
コードをコピーします
array_square_sum ディレクトリのモジュール ディレクトリを確認すると、その中に array_square_sum.so が生成されていることがわかります。これが必要な拡張子です。

    liujun@ubuntu:~/test/php-5.5.8/ext/array_square_sum$ ls modules/
  1. array_square_sum.la array_square_sum.so
コードをコピー

4. 拡張機能を使用する

4.1. 設定拡張

PHP 設定 php.ini を変更し、設定内容を追加します。

    [array_square_sum]
  1. extension=array_square_sum.so
コードをコピー

4.2. モジュールを追加する

PHP 拡張機能は通常 $PHP_PATH/lib/php/extensions/no-debug-non-zts-yyyymmdd にあります。見つからない場合は、Baidu または Google にアクセスしてください。そこには多数の .so ファイルがあります。

3.5で作成したarray_sum_square.soをコピーするだけです。

fastcgi モードを使用する場合は、php を再起動する必要があるため、php には array_square_sum という拡張子が付いている必要があります。 詳細については、phpinfo を確認してください (分からない場合は、Baidu または Google を使用してください )。

4.2. コードを書く

拡張機能を作成すると操作効率が向上するため、ここでは拡張機能を使用した場合と PHP コードを直接使用した場合のパフォーマンスを比較およびテストします。複数回実験すると誤差が減るため、100,000 個の数値の二乗和を求めるには 2,000 回実験してください。コードは次のとおりです:

    $data = array();
  1. $max_index = 100000;
  2. $test_time = 2000;
  3. for($i=0; $i<$max_index; $ i++){
  4. $data[] = $i;
  5. }
  6. $php_test_time_start = time();
  7. php_test($test_time, $data);
  8. $php_test_time_stop = time();
  9. echo "php テストの拡張時間は " . ($ php_test_time_stop - $php_test_time_start). "n";
  10. $c_test_time_start = time();
  11. c_test($test_time, $data);
  12. $c_test_time_stop = time();
  13. echo "php テスト時間は ". $c_test_time_stop - $c_test_time_start). "n";
  14. function php_test($test_time, $test_data){
  15. for($i=0; $i<$test_time; $i++){
  16. $sum = 0;
  17. foreach( $test_data as $data){
  18. $sum += $data * $data;
  19. }
  20. }
  21. }
  22. function c_test($test_time, $test_data){
  23. for($i=0; $i<$test_time; $i++) {
  24. $sum = array_square_sum($test_data);
  25. }
  26. }
コードをコピー
テスト結果は次のとおりです:

    liujun@ubuntu:~/php$ ~/php/ bin/php test.php
  1. phpテストの拡張時間は30です
  2. phpテスト時間は2です
コードをコピーします

この拡張機能は、php を直接使用するよりも 15 倍高速であることがわかります。ビジネス ロジックが複雑になるにつれて、この差別化はさらに大きくなります。

次に、C 言語を使用してこれを直接実行します。これもテストするコードです (テスト条件はまったく同じです):

  1. #include
  2. #include
  3. #include
  4. #定義 TEST_TIME 2000
  5. #define MAX_INDEX 100000
  6. int main()
  7. {
  8. int data[MAX_INDEX];
  9. double sum = 0;
  10. for(int i=0; i
  11. data[i] = i ;
  12. }
  13. struct timeval start;
  14. struct timeval end;
  15. gettimeofday(&start,NULL);
  16. for(int i=0; i
  17. for(int j=0; j<) ;MAX_INDEX ; j++){
  18. sum += data[j] * data[j];
  19. }
  20. }
  21. gettimeofday(&end,NULL);
  22. double time=(end.tv_sec-start.tv_sec) + (end. tv_usec- start.tv_usec) * 1.0 /1000000;
  23. printf("合計時間は %lfn", time );
  24. printf("合計時間は %lfn", sum);
  25. return 0;
  26. }
コードをコピー

実行して効果を確認すると、C を直接使用した場合の時間はわずか 0.261746 で、C 拡張機能を使用した場合の 13.09%、PHP を直接使用した場合の 0.87% であることがわかります。もちろん、IO などの複雑な操作が含まれる場合、C/C++ は PHP よりも数万倍高速になります (テスト済み)。

  1. liujun@ubuntu:~/php$ g++ test.cpp -o test
  2. liujun@ubuntu:~/php$ ./test
  3. 合計時間は 0.261746
  4. 合計時間は 36207007178615872 です。 0000
コードをコピー

したがって、インデックス作成や単語分割など、実際に非常に高いパフォーマンス要件を必要とするサービスの場合は、C を使用して基礎となるサービスのセットを作成し、PHP を使用してカプセル化された呼び出しを行うことができます。



声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。