序文で述べた内容をいくつか読めば、PHP 拡張機能の開発は面倒で複雑だと考える人もいるかもしれませんが、実際にはそうではありません。すぐにキャラクターに飛び込み、最初の拡張機能を開発するだけです。
1. PHP をコンパイルする
開発前に、PHP ソース コードを準備してコンパイルする必要があります。プロセスは次のとおりです。
tar -zxvf php-5.3.9.tar.gzcd php-5.3.9
解凍後、PHP を使用しています。ソース コード ディレクトリに移動し、php.ini を直接コンパイルして追加します。
./configure --prefix=/usr/local/webserver/php --enable-fastcgi --enable-fpm --enable-debugmake && make installcp /home/soft/php-5.3.9/php.ini-development /usr/local/webserver/php/lib/php.ini
他の拡張機能は静的にコンパイルしませんでしたが、後で使用するデバッグを有効にしました。次に、php.ini 内の対応する項目を変更します。ここでは詳しく説明しません。
ここで、後で多くの作業を節約するために、環境変数に関連する PHP を追加します。
vim /root/.bash_profile
私は root を使用し、他の別のユーザーは、対応するユーザー ディレクトリ内の .bask_profile ファイルを変更し、次のパスを PATH の後に追加します。ファイル /local/webserver/php/bin/ は次のようになります:
PATH=$PATH:$HOME/bin:/usr/local/webserver/php/bin/
環境変数が設定されました。PHP バージョンを確認しましょう:
OK、コンパイルは完了しました。続行しましょう。
2. 一般的な開発プロセス
一般的な拡張機能の開発プロセスは次のとおりです:
3. 拡張機能の定義
まず、完成させたい拡張機能を定義します:
この拡張機能には 1 つだけあります。これは、32 ビット システムと 64 ビット システムで ip2long の値が異なる問題を解決するために、PHP システム関数 ip2long() を書き換えることです (この問題は、32 ビットと 64 ビットの整数の範囲が異なるために発生します)。 64 ビット システムの場合、具体的な理由については Google で調べてください)。
新しい ip2long は、32 ビット システムと同じ、-2147483648 から 2147483647 の範囲の 32 ビット符号付き整数を固定的に返します。
拡張機能名は myip 、関数名は ip2long32 です
拡張機能の関数と名前は OK です。後はプロセスに従って開発していきます。
4. 正式な開発
1. 開発スケルトンを生成します
まず、ソースコード拡張ディレクトリに入ります:
cd /home/soft/php-5.3.9/ext
次に、PHP が提供する拡張スケルトン ツール ext_skel について学習します。スケルトンを生成する、 ext_skel の使用法は次のとおりです:
./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]] [--skel=dir] [--full-xml] [--no-help] --extname=module module is the name of your extension(模块名,会在当前目录创建一个该名称子目录) --proto=file file contains prototypes of functions to create(函数原型定义文件) --stubs=file generate only function stubs in file --xml generate xml documentation to be added to phpdoc-cvs --skel=dir path to the skeleton directory(设置骨架生成的目录,不设置该项则默认在ext/extname下) --full-xml generate xml documentation for a self-contained extension (not yet implemented) --no-help don't try to be nice and create comments in the code and helper functions to test if the module compiled (生成的代码中不显示各种帮助注释)
今回は 2 つのオプションを使用します。--extname=myip は拡張機能の名前を定義し、--proto=myip.pro は拡張機能の名前を定義します。拡張機能の関数プロトタイプ まず、拡張機能プロトタイプ ファイルを生成します:
vim myip.pro
以下を追加します:
int ip2long32(string ip)
これは、拡張機能に int を返し、文字列を入力として受け取る関数があることを意味します。
この時点で、次のコマンドを実行して拡張スケルトンを生成します:
./ext_skel --extname=myip --proto=myip.pro
OK、この時点で、現在の PHP 拡張ディレクトリの下にサブディレクトリ myip が生成されていることがわかります。 myip と入力して見てください。
cd myipll
以下に示すように、生成された一連のファイルが作成されていることがわかります:
この時点で、2 番目のステップに進むことができます。
2. config.m4 を変更します
config.m4 ファイルの機能については、後の記事で詳しく説明することにします。ここでは、何をするかを説明します。
vim を使用して config.m4 を編集します:
vim config.m4
次のように、16 ~ 18 行目の先頭にある dnl を削除します:
この具体的な理由については、後の記事で説明します。直接実行して config.m4 を保存し、次のステップに進みます。
3. コーディング
最後に、myip.c に入力して関数をコーディングします。一緒に乾杯しましょう。
vim myip.c
下の図に示されている場所を見つけてください:
この図は、提供された関数プロトタイプに基づいて拡張スケルトン ツールによって生成された対応する関数です。 ここで注意すべき点がいくつかあります。
1. PHP_FUNCTION: PHP コアによって定義されるマクロです。ZEND_FUNCTION と同じであり、実際に生成される関数名は zif_ip2long32 です。
2. zend_parse_parameters: PHP は弱く型指定された言語であり、C は強く型指定された言語であるため、この関数を使用して PHP によって渡されたパラメーターを受け取り、特定の型変換を実行して PHP 変数を C 言語に変換する必要があります。認識されたタイプ。
zend_parse_parameters 関数のプロトタイプは次のとおりです:
zend_parse_parameters(int num_args TSRMLS_CC, char *type_spec, …);
パラメーターの説明:
num_args:传递给函数的参数个数。通常的做法是使用宏 ZEND_NUM_ARGS()。 TSRMLS_CC:线程安全,总是传递TSRMLS_CC宏。 详解:http://www.54chen.com/php-tech/what-is-tsrmls_cc.html type_spec:第三个参数是一个字符串,指定了函数期望的参数类型 ...:需要随参数值更新的变量列表]
type_spec是格式化字符串,其常见的含义如下:
参数 代表着的类型
b Boolean
l Integer 整型
d Floating point 浮点型
s String 字符串
r Resource 资源
a Array 数组
o Object instance 对象
O Object instance of a specified type 特定类型的对象
z Non-specific zval 任意类型~
Z zval**类型
f 表示函数、方法名称
我们将该函数修改为如下内容:
PHP_FUNCTION(ip2long32) { char *ip = NULL; int argc = ZEND_NUM_ARGS(); int ip_len; if (zend_parse_parameters(argc TSRMLS_CC, "s", &ip, &ip_len) == FAILURE) { return; } int32_t ip_int32; unsigned char ip1, ip2, ip3, ip4; sscanf(ip, "%hhu.%hhu.%hhu.%hhu", &ip1, &ip2, &ip3, &ip4); ip_int32 = (int32_t)((ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4); RETURN_LONG(ip_int32); }
功能完成了,这边有个RETURN_LONG(ip_int32)比较特殊,这也是PHP内核提供的宏,用于返回值给PHP,具体说明如下:
设置返回值并且结束函数 设置返回值 宏返回类型和参数
RETURN_LONG(l) RETVAL_LONG(l) 整数
RETURN_BOOL(b) RETVAL_BOOL(b) 布尔数(1或0)
RETURN_NULL() RETVAL_NULL() NULL
RETURN_DOUBLE(d) RETVAL_DOUBLE(d) 浮点数
RETURN_STRING(s, dup) RETVAL_STRING(s, dup) 字符串。如果dup为1,引擎会调用estrdup()重复s,使用拷贝。如果dup为0,就使用s
RETURN_STRINGL(s, l, dup) RETVAL_STRINGL(s, l, dup) 长度为l的字符串值。与上一个宏一样,但因为s的长度被指定,所以速度更快。
RETURN_TRUE RETVAL_TRUE 返回布尔值true。注意到这个宏没有括号。
RETURN_FALSE RETVAL_FALSE 返回布尔值false。注意到这个宏没有括号。
RETURN_RESOURCE(r) RETVAL_RESOURCE(r) 资源句柄。
编码完成了,保存并退出,然后我们可以开始编译了。
4. 编译
phpize./configure --with-php-config=/usr/local/webserver/php/bin/php-configmake && make install
不出意外的话编译完成后会有如下提示:
Installing shared extensions: /usr/local/webserver/php/lib/php/extensions/debug-non-zts-20090626/
进入该目录看下是否已经有myip.so,有的话最后我们就可以修改php.ini载入该so文件
5. 修改php.ini
cd /usr/local/webserver/php/libvim php.ini
修改extension_dir,并加入 extension = myip.so
extension_dir = "/usr/local/webserver/php/lib/php/extensions/debug-non-zts-20090626/"extension = myip.so
退出保存,并重启php,如果是使用Phpfpm的话可以执行如下命令:
kill -USR2 `cat /usr/local/webserver/php/var/run/php-fpm.pid`
看下扩展是否正常载入:
[root@tm977 lib]# php -m|grep myipmyip
说明已经正常载入了,最后我们测试下扩展函数吧!
6. 测试
php -r "var_dump(ip2long32('192.168.1.1'));"int(-1062731519)php -r "var_dump(ip2long('192.168.1.1'));" int(3232235777)
如上所示,ip2long32输出的是32位有符号整数,而ip2long输出的是64位无符号整数,大功告成!
五、小结
通过这一次的开发示例,是不是觉得其实开发一个扩展很简单?
确实是的,但是也别高兴的太早了,实际上要开发出功能强大的扩展远不是这么简单的事情,后面的文章我会继续深入,一边开发一边了解更加复杂的PHP核心代码。