PHP 拡張機能の開発に関する記事やブログは数多くあります。より古典的なものは次のとおりです。
私は、この一連のブログ投稿で PHP 拡張機能の開発について学んだことと洞察を要約し、Linux システムで PHP 拡張機能を開発するために知っておくべき最も基本的な知識を簡単かつ明確に説明するつもりです。レベルが低いので間違いがあるかもしれませんがご指摘ください。
準備
まず、PHP ソース コードのコピーを取得し (Github からチェックアウトするか、公式 Web サイトから最新の安定バージョンをダウンロードできます)、コンパイルする必要があります。コンパイルを高速化するには、余分な拡張機能をすべて無効にすること (--disable-all オプションを使用) をお勧めしますが、デバッグ (--enable-debug オプションを使用) とスレッド セーフ (--enable-maintainer を使用) をオンにすることをお勧めします。 -zts)、ただし、拡張機能を公開するときにデバッグをオフにし、状況に応じてスレッド セーフをオンにするかどうかを選択する必要があります:
$ ./buildconf --force$ ./configure --disable-all --enable-debug --enable-maintainer-zts$ make
--prefix オプション (または make install) を指定していないことに注意してください。 、これは必要ないからです。 PHP を正常にコンパイルするには、いくつかの依存関係パッケージをインストールする必要がある場合がありますので、出力情報に注意してください。
コンパイルされた PHP 実行可能プログラムは、ソース コードの sapi ディレクトリにあります。将来的には、主に cli (コマンド ライン インターフェイス) 環境を使用することになります。参照:
$ alias php-dev=/usr/local/src/php-5.6.5/sapi/cli/php
非常に便利なコマンドライン オプションがいくつかあります:
php-dev -h # 打印帮助信息php-dev -v # 打印版本信息php-dev --ini # 打印配置信息 php-dev -m # 打印加载的模块信息php-dev -i # phpinfophp-dev -r <code> # 执行code里的代码
拡張スケルトン
PHP のすべての公式拡張機能はソース コードの ext ディレクトリにあり、拡張機能は私たちがwrite own もこのディレクトリに置くことができます。このディレクトリには、PHP 拡張機能のスケルトンを生成するために使用される ext_skel という名前のシェル スクリプトがあることに注意してください。このスクリプトを使用すると、PHP 拡張機能をすばやく作成できます。
$ ./ext_skel --extname=myext
上記のコマンドは、名前付きのmyext の拡張子であり、ソース コードは myext ディレクトリにあります。パラメーターを指定せずにスクリプトを実行すると、ヘルプ情報が出力され、スクリプトによって提供されるその他のオプションを確認できます。
次に、拡張機能を完成させましょう。 myext ディレクトリに入り、config.m4 構成ファイルを編集し、PHP_ARG_ENABLE マクロ関数を見つけて、前の dnl コメント (合計 3 行) を削除します。ソース コードのルート ディレクトリに戻り、buildconf、configure、make コマンドを再実行します。
$ ./buildconf --force$ ./configure --help | grep myext --enable-myext Enable myext support$ ./configure --disable-all --enable-myext --enable-debug --enable-maintainer-zts$ make
拡張機能の読み込みステータスを出力するために ./configure --help grep myext | を使用したことに注意してください。以下の出力が表示される場合は、拡張機能が正常に構成されていないことを意味します。config.m4 ファイルを確認してください。
コードの大部分がすでにコンパイルされているため、このコンパイルは非常に高速になるはずです。 PHP には拡張機能をコンパイルする別の方法 (拡張機能を .so ファイルにコンパイルする動的リンクを使用する) がありますが、拡張機能を開発する場合は、構成ファイルに拡張機能をロードする必要がなくなるため、静的コンパイルを使用することをお勧めします。
すべてがうまくいけば、最初の拡張機能を実行する準備ができています:
$ php-dev -m | grep myextmyext$ php-dev -r 'echo confirm_myext_compiled("myext") . "\n";'Congratulations! You have successfully modified ext/myext/config.m4. Module myext is now compiled into PHP.
最初のコマンドは、拡張機能がロードされたことを示します。 2 番目のコマンドは、ext_skel 拡張スケルトンが自動的に作成した関数を実行します。もちろん、この関数には意味はありませんが、この関数を hello world に簡単に適用できます。
拡張機能を手動で作成する
ほとんどのチュートリアルでは、拡張機能の開発を説明するためのプロトタイプとして ext_skel 拡張機能スケルトンを使用します。もちろん、このアプローチは非常に便利で高速です。しかし、私は個人的には拡張機能を純粋に手作業で開発することを好みます。なぜなら、あらゆる詳細を理解するのが簡単だからです。
拡張機能を手動で作成するには、まず ext ディレクトリに入り、拡張機能ディレクトリ myext2 を作成します。いくつかのファイルが必要です: config.m4、myext2.c、php_myext2.h。
まず、設定ファイル config.m4 を書きましょう:
PHP_ARG_ENABLE(myext2, whether to enable myext2 support,[ --enable-myext2 Enable myext2 support])if test "PHP_MYEXT2" != "no"; then PHP_NEW_EXTENSION(myext2, myext2.c, $ext_shared)fi
config.m4 は、実際には autoconf プログラムによって使用される設定ファイルであり、autotools ツールボックスの重要なコンポーネントです。 autoconf の使用法を完全に紹介するには長い時間がかかりますが、幸いなことに、ここでの使用法は非常に簡単です。
PHP_ARG_ENABLE は、autoconf 用に PHP によって定義されたマクロ関数です。Myext2 はその最初のパラメータで、拡張機能の名前を示します。後の 2 つのパラメータは、make と configure が実行されるときに表示するためにのみ使用されるため、必要なものを自由に記述できます。 [ ] は、autoconf 構文の二重引用符のように機能し、文字列をラップするために使用されます (2 番目のパラメーターにはスペースが含まれていますが、角括弧で囲む必要はないことに注意してください)。拡張機能がデフォルトでオンかオフかを示すために使用される 4 番目のパラメータもあります (デフォルトは no)。
下面三行其实就是shell语法,判断我们是否开启了PHP_MYEXT2扩展模块。如果开启了该扩展模块(--enable-myext2),则$PHP_MYEXT2变量的值不为no,因此执行PHP_NEW_EXTENSION宏。这个宏函数也是PHP为autoconf定义的扩展语法,第一个参数同样是扩展名称;第二个参数是扩展要编译的C文件,如果有多个,依次写下去就可以了(空格分隔);第三个参数固定是$ext_shared。
接下来编写php_myext2.h头文件,该文件的命名是PHP扩展的规范 ? php_扩展名.h:
1 #ifndef PHP_MYEXT2_H 2 #define PHP_MYEXT2_H 3 4 extern zend_module_entry myext2_module_entry; 5 #define phpext_myext2_ptr &myext2_module_entry 6 7 #define PHP_MYEXT2_VERSION "0.1.0" 8 9 /* prototypes */10 PHP_FUNCTION(hello);11 12 #endif /* PHP_MYEXT2_H */
这里主要的代码是定义了名为phpext_myext2_ptr的宏,PHP底层通过该宏来引用我们的扩展。可以看出,该宏的命名同样是有规范的 ? phpext_扩展名_ptr。而myext2_module_entry是我们稍后要在.c文件里定义的结构体,它的命名也是规范的 ? 扩展名_module_entry。
此外我们还定义了一个标识我们扩展版本号的宏和一个函数原型(通过PHP_FUNCTION宏,PHP_FUNCTION宏函数的参数是外部可使用的函数名),稍后我们会来实现这个函数。
最后来看下myext2.c文件的实现:
1 #include "php.h" 2 #include "php_myext2.h" 3 4 /* {{{ myext2_functions[] 5 * 6 * Every user visible function must have an entry in myext2_functions[]. 7 */ 8 static const zend_function_entry myext2_functions[] = { 9 PHP_FE(hello, NULL)10 PHP_FE_END11 };12 /* }}} */13 14 /* {{{ myext2_module_entry15 */16 zend_module_entry myext2_module_entry = {17 STANDARD_MODULE_HEADER,18 "myext2", /* module name */19 myext2_functions, /* module functions */20 NULL, /* module initialize */21 NULL, /* module shutdown */22 NULL, /* request initialize */23 NULL, /* request shutdown */24 NULL, /* phpinfo */25 PHP_MYEXT2_VERSION, /* module version */26 STANDARD_MODULE_PROPERTIES27 };28 /* }}} */29 30 #ifdef COMPILE_DL_MYEXT231 ZEND_GET_MODULE(myext2)32 #endif33 34 /* {{{ proto void hello()35 Print "hello world!" */36 PHP_FUNCTION(hello)37 {38 php_printf("hello world!\n");39 }40 /* }}} */
对比下扩展骨架创建的.c文件就会发现,我们的.c文件非常的简单,其实这些对一个最基本的扩展来说就已经足够了。
上面的代码是简单而清晰的,大部分注释已经很具说明性了。我们再简要概括下:
这里面涉及了一些宏,比如PHP_FE,PHP_FE_END,PHP_FUNCTION等等,完整介绍这些宏要到后续的博文中才可以,眼下最简单的办法就是记住这些宏。
注意到我们每一个文件的命名,变量的命名,空格和缩进,以及注释等都是非常规范的,遵循这些规范,可以使我们编写的代码和PHP本身的代码更加契合,我们也推荐你使用这样的规范来开发PHP扩展。
最后,编译运行我们的扩展:
$ ./buildconf --force$ ./configure --help | grep myext2 --enable-myext2 Enable myext2 support$ ./configure --disable-all --enable-myext2 --enable-debug --enable-maintainer-zts$ make$ php-dev -m | grep myext2myext2$ php-dev -r 'hello();'hello world!