ホームページ >php教程 >php手册 >PHP 拡張機能開発の基本に関する簡単な説明 (1/2 ページ)

PHP 拡張機能開発の基本に関する簡単な説明 (1/2 ページ)

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBオリジナル
2016-06-13 12:12:57886ブラウズ

摘要&引言
PHP是当前应用非常广泛的一门语言,从国外的Facebook、Twitter到国内的淘宝、腾讯、百度再到互联网上林林总总的各种大中小型网站都能见到它的身影。PHP的成功,应该说很大程度上依赖于其开放的扩展API机制和丰富的扩展组件(PHP Extension),正是这些扩展组件使得PHP从各种数据库操作到XML、JSON、加密、文件处理、图形处理、Socket等领域无所不能。有时候开发人员可能需要开发自己的PHP扩展,当前PHP5的扩展机制是基于Zend API的,Zend API提供了丰富的接口和宏定义,加上一些实用工具,使得PHP扩展开发起来难度并不算特别大。本文将介绍关于PHP扩展组件开发的基本知识,并通过一个实例展示开发PHP扩展的基本过程。

PHP扩展组件的开发过程在Unix和Windows环境下有所不同,但基本是互通的,本文将基于Unix环境(具体使用Linux)。阅读本文需要简单了解Unix环境、PHP和C语言的一些基础知识,只要简单了解就行,我会尽量不涉及太过具体的操作系统和语言特性,并在必要的地方加以解释,以便读者阅读。

本文的具体开发环境为Ubuntu 10.04 + PHP 5.3.3。

下载PHP源代码
要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。我下载的是PHP最新版本5.3.3,格式为tar.bz2压缩包。下载地址为:http://cn.php.net/get/php-5.3.3.tar.bz2/from/a/mirror。

下载后,将源代码移动到合适的目录并解压。解压命令为:

复制代码 代码如下:

tar -jxvf 源码包名称


若下载的是tar.gz压缩包,解压命令为

复制代码 代码如下:

tar -zxvf 源码包名称


解压后,在源代码目录中有个ext目录,这里便是和PHP扩展有关的目录。进入目录后用ls查看,可以看到许多已经存在的扩展。下图是在我的环境下查看的结果:


其中蓝色的均是扩展包目录,其中可以看到我们很熟悉的mysql、iconv和gd等等。而ext_skel是Unix环境下用于自动生成PHP扩展框架的脚本工具,后面我们马上会用到,ext_skel_win32.php是windows下对应的脚本。

开发自己的PHP扩展——say_hello
下面我们开发一个PHP扩展:say_hello。这个扩展很简单,只是接受一个字符串参数,然后输出“Hello xxx!”。这个例子只是为了介绍PHP扩展组件的开发流程,不承担实际功能。

生成扩展组件框架
PHP的扩展组件开发目录和文件是有固定组织结构的,你可以随便进入一个已有扩展组件目录,查看其所有文件,我想你一定眼花缭乱了。当然你可以选择手工完成框架的搭建,不过我相信你更希望有什么东西来帮你完成。上文提到的ext_skel脚本就是用来自动构建扩展包框架的工具。ext_skel的完整命令为:

ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]] [--skel=dir] [--full-xml] [--no-help]

作为初学者,我们不必了解所有命令参数,实际上,大多数情况下只需要提供第一个参数就可以了,也就是扩展模块的名字。因此,我们在ext目录中键入如下命令:
./ext_skel --extname=say_hello

(如果你希望详细了解ext_skel的各项命令参数, 请参考这里)

这时再用ls查看,会发现多了一个“say_hello”目录,进入这个目录,会发现ext_skel已经为我们建立好了say_hello的基本框架,如下图:

如果你懒得弄清楚PHP扩展包目录结构的全部内容,那么里面有三个文件你必须注意:

config.m4:这是Unix环境下的Build System配置文件,后面将会通过它生成配置和安装。

php_say_hello.h:这个文件是扩展模块的头文件。遵循C语言一贯的作风,这个里面可以放置一些自定义的结构体、全局变量等等。

say_hello.c:这个就是扩展模块的主程序文件了,最终的扩展模块各个函数入口都在这里。当然,你可以将所有程序代码都塞到这里面,也可以遵循模块化思想,将各个功能模块放到不同文件中。

下面的内容主要围绕这三个文件展开。

Unix Build System配置

开发PHP扩展组件的第一步不是写实现代码,而是要先配置好Build System选项。由于我们是在Linux下开发,所以这里的配置主要与config.m4有关。

Build System の設定については、Unix システムのさまざまな事柄に関連しているので、詳しく書いても、私が興味を持って書いても、誰も読みたがらないでしょう。したがって、ここでは省略し、重要なポイントのみに焦点を当てます。config.m4 の詳細については、ここを参照してください。

生成された config.m4 ファイルを開きます。内容はおおよそ次のとおりです:

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


dnl $Id $
dnl config.m4 (拡張子 Say_hello 用)
dnl このファイル内のコメントは文字列 'dnl' で始まります。
dnl このファイルは必要に応じて削除してください。
dnl 拡張機能が外部のものを参照している場合は、次のオプションを使用します。
dnl PHP_ARG_WITH(say_hello、say_hello サポートの場合、
dnl) コメントが整列していることを確認してください:
dnl [ --with -say_hello Say_hello サポートを含めます])
dnl それ以外の場合は、enable を使用します:
dnl PHP_ARG_ENABLE(say_hello, Say_hello サポートを有効にするかどうか,
dnl コメントが整列していることを確認してください:
dnl [ --enable- Say_hello Say_hello サポートを有効にする])
if test "$PHP_SAY_HELLO" != "no"; then
dnl ここにテストの例を書きます...
dnl # --with-say_hello -> -path
dnl SEARCH_PATH ="/usr/local /usr" # この
dnl を変更する必要があるかもしれません SEARCH_FOR="/include/say_hello.h" # この
dnl を変更する可能性が高いのは次のような場合ですtest -r $PHP_SAY_HELLO/$ SEARCH_FOR; then # パラメータとして指定されたパス
dnl SAY_HELLO_DIR=$PHP_SAY_HELLO
dnl else # デフォルトのパスリストを検索
dnl AC_MSG_CHECKING([デフォルトパスのsay_helloファイルの場合])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl SAY_HELLO_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl 完了
dnl fi
dnl
dnl if test -z "$SAY_HELLO_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([say_hello ディストリビューションを再インストールしてください] )
dnl fi
dnl # --with-say_hello -> インクルード パスを追加
dnl PHP_ADD_INCLUDE($SAY_HELLO_DIR/include)
dnl # --with-say_hello ->シンボルの存在
dnl LIBNAME =say_hello # この
dnl LIBSYMBOL=say_hello # 変更する可能性が高い
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SAY_HELLO_DIR/lib, SAY_HELLO_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_SAY_HELLOLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([間違ったsay_hello lib バージョンまたは lib が見つかりません] )
dnl ], [
dnl -L$SAY_HELLO_DIR/lib -lm
dnl ])
dnl
dnl PHP_SUBST(SAY_HELLO_SHARED_LIBADD)
PHP_NEW_EXTENSION(say_hello,say_hello.c, $ ext_shared)
fi


この構造は少しわかりにくいように思えるかもしれませんが、中身を説明したいと思います。これは PHP Extension のプロトタイプなので、これを理解しないと PHP Extension の開発はできません。もちろん、各フィールドを 1 つずつ説明することはしません。多くのフィールドは手動で入力する必要がなく、特定の定義済みマクロを使用できるためです。

7 番目のフィールド「name」。このフィールドはこの PHP 拡張機能の名前で、この場合は「say_hello」です。

8 番目のフィールド「functions」には、この拡張機能で定義した関数への参照が保存されます。興味のある人は _zend_function_entry のソース コードを読むことができます。特定のコードを記述するときに、対応するマクロがここにあります。

9~12番目のフィールドはそれぞれ4つの関数ポインタであり、これら4つの関数はそれぞれ「拡張モジュールのロード時」、「拡張モジュールのアンロード時」、「各リクエストの開始時」のタイミングで呼び出されます。 」および「各リクエストの終了時」。これら 4 つの機能はインターセプト メカニズムとみなすことができ、主にリソースの割り当て、解放、および対応する時間のその他の関連操作に使用されます。

13番目のフィールド「info_func」も関数ポインタであり、phpinfo()の実行時にこのポインタが指す関数が呼び出され、カスタムモジュールの情報が表示されます。

14 番目のフィールド「version」はモジュールのバージョンです。

(zend_module_entry の詳細については、ここを参照してください)

上記のフィールドを導入した後、「say_hello.c」で自動的に生成された「say_hello_module_entry」フレームワーク コードを見てみましょう。


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


/* {{{say_hello_module_entry
*/
zend_module_entry Say_hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
「こんにちは」 ,
say_hello_functions,
PHP_MINIT(say_hello),
PHP_MSHUTDOWN(say_hello),
PHP_RINIT(say_hello), /* リクエスト開始時に何もすることがない場合は NULL に置き換えます */
PHP_RSHUTDOWN( Say_hello), /* リクエスト終了時に何もすることがない場合は NULL に置き換えます */
PHP_MINFO(say_hello),
#if ZEND_MODULE_API_NO >= 20010901
"0.1", /* のバージョン番号に置き換えます拡張機能 */
#endif
STANDARD_MODULE_PROPERTIES
}
/* }}} */


;

まず、マクロ「STANDARD_MODULE_HEADER」は最初の 6 フィールドを生成し、「STANDARD_MODULE_PROPERTIES」は「version」以降のフィールドを生成するため、まだ心配する必要はありません。私たちが注目するいくつかのフィールドもマクロによって入力または生成されており、いくつかの関数のフレームワークも「say_hello.c」内の対応する場所に生成されています。ここで、いくつかのマクロのパラメータはすべて「say_hello」ですが、これはいくつかの関数の名前がす​​べて「say_hello」であることを意味するわけではなく、C 言語には関数名のオーバーロード機構がないことに注意してください。実際、PHP 拡張機能の開発プロセスでは、グローバル変数から関数定義、さらには戻り値に至るまで、Zend で事前に定義されたさまざまなマクロがほぼあらゆる場所で使用されます。 PHP のメカニズムは名前の競合などの問題を引き起こす可能性があり、これらのマクロは関数などの要素を内部名に変換しますが、これらはプログラマにとっては透過的です (マクロのコードを読まない限り)。私たちはさまざまなマクロやマクロを通じてプログラムします。私たちのために内部の多くのことを処理してください。

これを書いた後、私たちのタスクは明確です。まず、対応する時間に何かを処理する必要がある場合は、各インターセプト関数の内容を入力する必要があります。次に、say_hello の関数関数を記述し、への参照を追加します。 Say_hello_functions 。

phpinfo() コールバック関数を作成します

say_hello 拡張機能はライフサイクルの各段階での操作を必要としないため、info_func の内容を記述するだけです。前述のように、この関数は拡張機能情報を表示するために phpinfo() が実行されるときに自動的に呼び出されます。この関数を記述するには 4 つの関数が使用されます:

php_info_print_table_start()—phpinfo テーブルを開始します。パラメータはありません。

php_info_print_table_header()—出力テーブルヘッダー。最初のパラメータはヘッダーの列数を示す整数で、後続のパラメータは表示するテキストを指定するために使用される列数に等しい (char*) タイプのパラメータです。

php_info_print_table_row()—テーブルの内容を出力します。最初のパラメータは、この行の列数を示す整数で、後続のパラメータは、表示するテキストを指定するために使用される列数に等しい (char*) タイプのパラメータです。

php_info_print_table_end()—phpinfo テーブルを終了します。パラメータはありません。

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