ホームページ >バックエンド開発 >PHPチュートリアル >Linux での PHP 拡張機能の C++ 実装

Linux での PHP 拡張機能の C++ 実装

WBOY
WBOYオリジナル
2016-06-20 12:32:011234ブラウズ

読み取り値: 1

現在、データベース内の要塞マシンのパスワードは rc4 暗号化暗号文として保存されています。以前は、PHP を呼び出す必要がありました。パスワードを読み取った後、バックグラウンド システム コマンドの stdout を読み取って、復号化された平文パスワードを取得します。

この呼び出し方法は 1 回の暗号化または復号化のためのものであるため、システム コマンドの呼び出しにかかる時間コストは 10ms 程度で、それほど大きな問題にはなりません。すべてのアカウントのパスワードの複雑さを検証するための現在の要件では、ページをロードするときにすべてのアカウントのパスワードが要件を満たしている必要があります。アカウントのサイズが比較的大きい場合、アカウントの応答時間が問題になります。 request は 5m19s に達し、復号化部分をコメントアウトした場合、応答時間は 2 ~ 3s です。この問題を最適化するために、まず xmlrpc を使用して Python の C++ 拡張機能を呼び出して応答速度を向上させてみました。その効果も良好で、応答時間は約 20 秒に増加しました。調査の結果、xmlrpc が呼び出されたときの 20,000 個のデータのシリアル化と逆シリアル化にかかる時間は、20 秒のうち 15 秒かかっていることが判明しました。応答速度をさらに向上させるために、暗号化および復号化のソース コードは C++ コードで実装されているため、C++ を使用して PHP 拡張機能を実装することを考えました。その方がより良い結果が得られる可能性があるため、この rc4 PHP 拡張機能の実装を開始しました。

C++ を使用して PHP 拡張機能を作成する

1 準備作業

まず、開発マシン上で対応するバージョンの Bastion Machine PHP のソース コードを取得し、解凍します。

yangshuofei@IPS-dev3 ~/src $ tarxvfphp-5.5.25.tar.bz2 

2 構成ファイルの書き込み

config.m4 ファイルは、主に autoconf を使用して構成を生成します。構成ファイルを作成し、使い慣れた Makefile ファイルを自動的に生成します。

config.m4 ファイルを自分で作成することも、シェル スクリプト ext_skel を使用してテンプレートを生成することもできます。ここでは後者を使用します。

ソース コードの ext フォルダーに入り、次のように ./ext_skel コマンドを実行します。

yangshuofei@IPS-dev3 ~/src $ cdphp-5.5.25/ext/yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext $ ./ext_skel --extname=rc4Creatingdirectoryrc4Creatingbasicfiles: config.m4config.w32 .svnignorerc4.c php_rc4.h CREDITSEXPERIMENTALtests/001.phpt rc4.php [done]. To use yournew extension, youwillhaveto executethefollowingsteps: 1.  $ cd ..2.  $ viext/rc4/config.m43.  $ ./buildconf4.  $ ./configure --[with|enable]-rc45.  $ make6.  $ ./sapi/cli/php -f ext/rc4/rc4.php7.  $ viext/rc4/rc4.c8.  $ make Repeatsteps 3-6 untilyouaresatisfiedwithext/rc4/config.m4andstep 6 confirmsthatyourmoduleis compiledintoPHP. Then, startwritingcodeand repeatthelasttwostepsas oftenas necessary.

の下に rc4 という追加のディレクトリがあります。 ext ディレクトリ。ディレクトリに入ると、ディレクトリ内にいくつかのファイルが見つかります:

yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext $ cdrc4yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext/rc4 $ ls -ltotal 32-rw-r--r-- 1 yangshuofeiyangshuofei    3 Mar 14 17:27 CREDITS-rw-r--r-- 1 yangshuofeiyangshuofei    0 Mar 14 17:27 EXPERIMENTAL-rw-r--r-- 1 yangshuofeiyangshuofei 1954 Mar 14 17:27 config.m4-rw-r--r-- 1 yangshuofeiyangshuofei  275 Mar 14 17:27 config.w32-rw-r--r-- 1 yangshuofeiyangshuofei 2787 Mar 14 17:27 php_rc4.h-rw-r--r-- 1 yangshuofeiyangshuofei 5002 Mar 14 17:27 rc4.c-rw-r--r-- 1 yangshuofeiyangshuofei  493 Mar 14 17:27 rc4.phpdrwxr-xr-x 2 yangshuofeiyangshuofei 4096 Mar 14 17:27 tests

その後、プロンプトに従って config.m4 ファイルを変更できます。ここにはいくつかの重要なマクロ コマンドがあります。

  • dnl はコメントです。
  • PHP_ARG_WITH または PHP_ARG_ENABLE は、PHP 拡張モジュールがどのように機能するかを指定します。前者はサードパーティのライブラリが必要ないことを意味し、後者はその逆です。 ;
  • PHP_REQUIRE_CXX は、この拡張機能が C++ を使用することを指定するために使用されます。
  • PHP_ADD_INCLUDE は、PHP 拡張モジュールによって使用されるヘッダー ファイル ディレクトリを指定します。定義およびライブラリ接続エラー情報など;
  • PHP_ADD_LIBRARY(stdc++,"",EXTERN_NAME_LIBADD) は、標準 C++ ライブラリを拡張機能にリンクするために使用されます。この拡張機能がダイナミック リンク ライブラリにコンパイルされる方法を示します。
  • PHP_NEW_EXTENSION は、コンパイルするソース ファイルを指定するために使用されます。
  • モジュール フレームワークext_skel によって生成されるデフォルトは C 用です。PHP 拡張には C++ を使用する必要があり、上記の 2 つの必要なマクロ PHP_REQUIRE_CXX と PHP_ADD_LIBRARY に加えて、rc4.c の名前を rc4.cc に変更する必要があります。
  • 同様の Makefile 構文は config.m4 でも使用できることに注意してください。そのフラグメントは次のとおりです:

3 .h ファイルの書き込み

PHP_ARG_WITH(rc4, for rc4support,Makesurethatthecommentis aligned:[  --with-rc4            Include rc4support])...  COMMON_PATH="../../common"  PHP_REQUIRE_CXX()  PHP_SUBST(RC4_SHARED_LIBADD)  PHP_ADD_INCLUDE($COMMON_PATH/crypt)  PHP_ADD_LIBRARY(stdc++, 1, RC4_SHARED_LIBADD)  PHP_ADD_LIBRARY_WITH_PATH(SashCommon, $COMMON_PATH, RC4_SHARED_LIBADD)  CCFILES="rc4.cc"  PHP_NEW_EXTENSION(rc4, $CCFILES, $ext_shared)...

php_rc4.h ヘッダー ファイルを変更します。

TSRM.h ファイルに含まれる関数とクラスは純粋な C 言語で記述されているため、次のように extern を使用して記述します。

次のように php 関数宣言を追加します。

extern "C" {#ifdef ZTS#include "TSRM.h"#endif}

php_rc4.h ヘッダー ファイルまたは rc4.cc ファイルが C++ 言語の一部のコンテナまたは関数を使用する場合、対応する C++ ライブラリのヘッダー ファイルをヘッダー ファイルに含める必要があります。そうしないと、対応する C++ 関数が見つからないというエラーが発生します。

PHP_FUNCTION(rc4_encrypt);  /* For encrypt string. */PHP_FUNCTION(rc4_decrypt);  /* For decrypt string. */ 
4 .cc ファイルを書き込みます

rc4.cc ファイルを変更します。

config.h、php.h、php_ini.h、ext/standard/info.h に含まれる関数とクラスは TSRM.h と同様に純粋な C 言語で記述されているため、extern の記述も必要です

#include "php_ext_name.h" 文を extern "C" に含める必要がなくなりました。さらに、ZEND_GET_MODULE マクロも追加されました。コマンドも次のように特別に宣言する必要があります:

extern "C" {#ifdef HAVE_CONFIG_H#include "config.h"#endif #include "php.h"#include "php_ini.h"#include "ext/standard/info.h"}#include "php_rc4.h"

つまり、C で書かれた一部のライブラリまたは h は互換性のある方法で解決される必要があります。既製の C++ 実装があるため、関連するヘッダー ファイルが導入されます:

#ifdef COMPILE_DL_RC4BEGIN_EXTERN_C()ZEND_GET_MODULE(rc4)END_EXTERN_C()#endif

関連関数を追加します:

 #include "Crypt.h" 

関数の追加 実装は次のとおりです。

const zend_function_entryrc4_functions[] = {    PHP_FE(rc4_encrypt, NULL)      /* For encrypt string. */    PHP_FE(rc4_decrypt, NULL)      /* For decrypt string. */    PHP_FE_END  /* Must be the last line in rc4_functions[] */};

関数自体はマクロ PHP_FUNCTION() を使用して定義されており、Zend エンジンに適した関数プロトタイプを生成できます。ロジック自体は、呼び出し関数のパラメーターとロジック自体のセマンティック部分に分割されます。

PHP_FUNCTION(rc4_encrypt){    char *arg = NULL;    int arg_len, len;    const char *key = "key";    int key_len = sizeof("key") - 1;     char *strg;    const char *result = NULL;     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",         &arg, &arg_len, &key, &key_len) == FAILURE) {        return;    }    result = SASH::Crypt::encryptWithKey(arg, key).c_str();     len = spprintf(&strg, 0, "%s", result);    RETURN_STRINGL(strg, len, 0);}
関数によって渡されたパラメーターを取得するには、zend_parse_parameters() API 関数を使用できます。関数のプロトタイプは次のとおりです:

zend_parse_parameters()函数大部分的代码看起来几乎都一样。ZEND_NUM_ARGS()向Zend Engine提供关于接收到的参数数量,TSRMLS_CC被用来保证线程安全,最后函数会返回SUCCESS或者FAILURE。在普通情况下zend_parse_parameters()将会返回SUCCESS;如果一个调用脚本传递的参数数量超过了函数定义的参数数量,或者传递的参数不能转换成合适的数据类型,Zend将会自动的输出一个错误信息,然后函数会优雅地把控制权返回给调用脚本。

可能指定的类型

表中的zval是用来存储PHP中所有用户空间变量的真实数据类型。三个“复杂”的数据类型,Resource, Array和Object,当他们的数据类型字母标示在zend_parse_parameters()中被使用的时候,Zend Engine会对其进行类型检查,但是它们在C语言中没有相对应的数据类型,所以不会有任何转换会被实际执行。

由于解密有默认key,所以我在rc4_encrypt中采用了可变参数”s|s”,可以这样来解读:我首先需要一个字符串类型的参数,然后一个管道字符说明接下来的参数列表是可选的(如果一个可选的参数在函数调用过程中没有被传递,那么zend_parse_parameters()不会改变已经传给它的参数值),后面的s表示我需要一个可选的字符串参数。后面的&arg, &arg_len, &key, &key_len以引用的方式传递进来,所以zend_parse_parameters()可以用参数值来填充它们。(扩展还支持可变参数 )

从C++函数向PHP返回值,并不能使用通常的return语句,Zend将返回值地址作为参数传给我们,return_value是Zend为我们预先创建的一个标准zval结构,相当于一个局部变量,用户获得返回值时就相当于对return_value进行赋值操作,我们只需填充它即可;return_value_used表明用户是否使用了返回值,0表明没有使用返回值,当函数结束后return_value的refcount将被减为0,并被销毁,因此,这种情况下完全可以不处理返回值;return_value_ptr用于返回引用,它需要和zend_function_entry.arg_info联合使用,通常都是NULL。

用于填充return_value的一组宏

这些宏将在填充完return_value后,执行return语句。如果不想return,可以改用相应RETURN_xxx宏的RETVAL_xxx版本。在这里我们使用了RETURN_STRINGL(strg, len, 0);来返回加密后的字符串。

5 生成.so

使用phpize生成configure(由于和开发机php版本不同,所以这里使用的是自己编译的phpize):

yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext/rc4 $ /home/yangshuofei/php/bin/phpize  Configuringfor:PHPApiVersion:        20121113ZendModuleApiNo:      20121212ZendExtensionApiNo:  220121212

生成Makefile(由于和开发机php版本不同,所以这里使用的是自己编译的php-config):

 yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext/rc4 $ ./configure  --with-php-config=/home/yangshuofei/php/bin/php-config 

使用phpize生成configure执行文件后,可以使用./configure –help查看帮助信息,修改config.m4文件可以修改configure的帮助信息。每次修改了config.m4文件,需要使用清除临时文件 命令phpize –clean来完成消除configure。

生成.so,生成后会放在modules文件夹下:

yangshuofei@IPS-dev3 ~/src/php-5.5.25/ext/rc4 $ make...----------------------------------------------------------------------Librarieshavebeeninstalledin:  /home/yangshuofei/src/php-5.5.25/ext/rc4/modules If youeverhappento wantto linkagainstinstalledlibrariesin a givendirectory, LIBDIR, youmusteitheruse libtool, andspecifythefullpathnameofthelibrary, or use the `-LLIBDIR'flag during linking and do at least one of the following:   - add LIBDIR to the `LD_LIBRARY_PATH' environmentvariable    duringexecution  - addLIBDIRto the `LD_RUN_PATH' environment variable     during linking   - use the `-Wl,--rpath -Wl,LIBDIR' linkerflag  - haveyoursystemadministratoraddLIBDIRto `/etc/ld.so.conf' See any operating system documentation about shared libraries formore information, such as the ld(1) and ld.so(8) manual pages.---------------------------------------------------------------------- Build complete.Don't forgetto run 'make test'.

6 在设备上使用

根据php.ini中的配置:

extension_dir = /etc/php/apache2-php5.3/ext 

将生成的rc4.so文件放在设备的放在/etc/php/apache2-php5.3/ext文件夹下,

在/etc/php/apache2-php5.3/ext-active文件夹添加rc4.ini内容如下:

extension= rc4.so 

然后重启apache:

Develop>apache2ctlrestart 

接下来就可以在php文件中调用so中的rc4_encrypt和rc4_decrypt函数来进行加解密了。

修改前后性能对比

使用扩展前响应时间5m19s

使用扩展前响应时间

使用扩展后响应时间2.73s

使用扩展后响应时间

如果您需要了解更多内容,可以

加入QQ群:486207500

直接询问:010-68438880-8669

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