ホームページ  >  記事  >  バックエンド開発  >  php_PHPチュートリアルでCコードを呼び出す実装方法

php_PHPチュートリアルでCコードを呼び出す実装方法

WBOY
WBOYオリジナル
2016-07-13 10:36:13789ブラウズ

C コードは php プログラムで使用する必要があり、次の 2 つの状況で使用する必要があります:

1 すでに C コードがあり、それを PHP プログラムで直接使用したいです
2 PHP のパフォーマンスの問題により、一部の関数を実装するには C を使用する必要があります

最初のケースでは、システム コールを使用して既存の C コードを独立したプログラムに書き込むのが最も適切な方法です。パラメーターはコマンド ラインまたは標準入力を通じて渡され、結果は標準出力から読み取られます。次に、少し面倒な方法は、C コードをデーモンとして記述し、PHP プログラムがソケットを使用してデーモンと通信することです。

2 番目のケースに注目してください。システム コール方式を使用することもできますが、多くのプロセスを頻繁に起動すると、当然ながらパフォーマンスが低下することを考慮してください。デーモンを作成する方法は確かに実行可能ですが、はるかに複雑です。

私の簡単なテストでは、同じアルゴリズムが PHP よりも C で書かれた方が 500 倍効率的であることがわかりました。また、PHP 拡張機能を使用すると、90 倍以上改善することもできます (パフォーマンスの低下はパラメーターの受け渡しにあると思います)。

そのため、php 拡張機能が最良の選択となる場合もあります。

ここでは、PHP を再コンパイルせずに C で PHP 拡張機能を記述する方法に焦点を当てます。

まず、PHP ソース コードを見つけます。これは、ターゲット プラットフォームの PHP バージョンとは関係ありません。

ソースコードの ext ディレクトリに ext_skel という名前のスクリプトがあります (Windows プラットフォームでは ext_skel_win32.php を使用します)
このディレクトリで ./ext_skel --extname=hello を実行します (例として hello を使用します)
ディレクトリが生成されます現時点では、ディレクトリ内にいくつかのファイルがあります。config.m4 hello.c php_hello.h

の 3 つだけを気にする必要があります。

このディレクトリを任意の場所にコピーし、そこに cd して、
phpize
/configure
make
を実行しても何も起こりませんよね?
これは手順を見逃したためです。config.m4 を開いて次の
dnl を見つけます。拡張機能が外部のものを参照している場合は、with:
..
dnl を使用します。それ以外の場合は、enable:
..
を使用します。これは拡張機能を選択するためのものです。で使用する または有効にして、使用しましょう。 with 部分のコメントを解除します。
私と同じように vim エディターを使用している場合は、3 文字の dnl が元々コメントを表していることが簡単にわかるでしょう (これは、vim にはデフォルトでさまざまなファイル形式用の構文カラーリング パッケージが付属しているためです)

config.m4 を変更したら、
phpize
/configure
make
を続けます。このとき、モジュールの下に hello.so および hello.la ファイルが生成されます。 1 つは動的ライブラリで、もう 1 つは静的ライブラリです。

php 拡張機能は準備ができていますが、まだ必要な機能を実現していません。まず、この拡張機能の使用方法について説明します。 ext_skel は呼び出し例を使用して hello.php を生成しましたが、その例では hello.so を PHP の拡張ディレクトリにコピーする必要があります。私たちは独自の関数を実装したいだけであり、PHP のコピーバージョンを作成したくありません。代わりに、次を使用してロードします:

コードをコピーします コードは次のとおりです:

if(!extension_loaded("hello")) {
dl_local("hello.so");
}
function dl_local( $extensionFile ) {
//ライブラリをロードできることを確認します06。        if( !(bool)ini_get( "enable_dl" ) || (bool)ini_get( "safe_mode" ) ) {
die( "dh_local(): 拡張機能の読み込みは許可されていません。/n" );
}
//checkファイルが存在することを確認します11。        if( !file_exists(dirname(__FILE__) . "/". $extensionFile ) ) {
die( "dl_local(): ファイル '$extensionFile' が存在しません。/n" );
}
// ファイルのアクセス許可を確認します16 。        if( !is_executable(dirname(__FILE__) . "/". $extensionFile ) ) {
die( "dl_local(): ファイル '$extensionFile' は実行可能ではありません。/n" );
}
//パス21。        $currentDir = ディレクトリ名(__FILE__) 。 "/";
$currentExtPath = ini_get( "extension_dir" );
$subDirs = preg_match_all( "////" , $currentExtPath , $matches );
unset( $matches );
// 抽出したことを確認しましょう有効な拡張パス27。        if( !(bool)$subDirs ) {
die( "dl_local(): 有効な拡張パス [extension_dir] を決定できませんでした。/n" );
}
$extPathLastChar = strlen( $currentExtPath ) - 1;
もし( $extPathLastChar == strrpos( $currentExtPath , "/" ) ) {
$subDirs--;
}
$backDirStr = "";
for( $i = 1; $i $backDirStr .= "..";
if( $i != $subDirs ) {
$backDirStr .= "/";
}
}
//load46 への最終パスを構築します。        $finalExtPath = $backDirStr 。 $currentDir 。 $extensionFile;
//次に、dl() を実行して実際にモジュールをロードします49。        if( !dl( $finalExtPath ) ) {
die();
}
//モジュールが正しくロードされた場合は、モジュール名を取得する必要があります54。        $loadedExtensions = get_loaded_extensions();
$thisExtName = $loadedExtensions[ sizeof( $loadedExtensions ) - 1 ];
// 最後に、拡張機能名 58 を返します。        return $thisExtName;
}//end dl_local()

これの利点は、php 拡張機能が php コードに従うことができ、環境に優しい拡張機能であることです。

人々が次に懸念しているのは、関数を追加し、パラメーターの転送と戻り値を実装する方法です

関数を追加する手順は次のとおりです:
php_hello.h:
PHP_FUNCTION(confirm_hello_compiled) // 括弧内に関数名を入力します

hello.c
zend_function_entry hello_functions[] = {
PHP_FE(confirm_hello_compiled, NULL) /* ここに行を追加します*/
{NULL, NULL, NULL} /* hello_functions[] の最後の行である必要があります */
};
PHP_FUNCTION(confirm_hello_compiled)
{//ここに関数本体を記述します
}
実装される関数のプロトタイプは実際には同じです。さらに、hello_functions に次のことを示す情報が 1 行追加されます。この機能があります。

つまり、それらはすべて同じ関数プロトタイプを持っていますが、戻り値とパラメーターをどのように区別するのでしょうか?
例を示します:

コードをコピーします コードは次のとおりです:

PHP_FUNCTION(hello_strdiff)
{
char *r1 = NULL, *r2 = NULL;
int n = 0, m = 0; (i = 0; i for(j = 0; j for (i = 1; i for(j = 1; j if(r1[ i-1] == r2[j-1]) コスト = 0;
その他のコスト = 1; +1);
a = MIN(a, d[i-1][j-1]+コスト);
}


这是一个求两个字符串差异度的算法,输入参数两个字符串,返回整型。
参数的传递看这里
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &r1, &n, &r2, &m)

把这个当成是scanf来理解好了。
类型说明见下表:

Boolean b zend_bool
Long l long
Double d double
String s char*, int
Resource r zval*
Array a zval*
Object o zval*
zval z zval*

如果想实现可选参数的话,例如一个字符串,一个浮点,再加一个可选的bool型,可以用"sd|b"来表示。
和scanf有一点不同的是,对于字符串,你要提供两个变量来存储,一个是char *,存字符串的地址,一个int,来存字符串的长度。这样有必要的时候,你可以安全的处理二进制数据。

那么返回值怎么办呢?
使用下面一组宏来表示:

RETURN_STRING
RETURN_LONG
RETURN_DOUBLE
RETURN_BOOL
RETURN_NULL

注意RETURN_STRING有两个参数
当你需要复制一份字符串时使用
RETURN_STRING("Hello World", 1);

否则使用
RETURN_STRING(str, 0);

这里涉及到了模块中内存的分配,当你申请的内存需要php程序中去释放的话,请参照如下表

Traditional Non-Persistent Persistent
malloc(count)
calloc(count, num)
emalloc(count)
ecalloc(count, num)
pemalloc(count, 1)*
pecalloc(count, num, 1)
strdup(str)
strndup(str, len)
estrdup(str)
estrndup(str, len)
pestrdup(str, 1)
pemalloc() & memcpy()
無料(ポイント) efree(ptr) pefree(ptr, 1)
realloc(ptr, newsize) errealloc(ptr, newsize) perrealloc(ptr, newsize, 1)
malloc(count * num + extr)** safe_emalloc(カウント、数値、拡張子) safe_pemalloc(count, num, extr)

通常は、「非永続的」にリストされているものを使用します。

基本的にはこれで、php 拡張機能の作成を開始できます。
今のアプリからは文字列操作ができれば十分なので、これくらいしか紹介できません。

www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/740214.html技術記事 PHP プログラムで C コードを使用する必要がある場合は、次の 2 つの状況が考えられます。 1. すでに C コードがあり、それを PHP プログラムで直接使用したい場合 2. PHP のパフォーマンスの問題により、C コードを使用する必要がある場合。 C でいくつかの関数を実装します。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。