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