Goal
Create a PHP extension named hello and implement the only function hello_world in it, which prints out the "Hello World" string.
Prerequisites
A computer with a C compiler, PHP runtime environment installed, and a handy text editor.
Important note: Do not try to write PHP extensions under Windows. The compilers of Visual C and MinGW are not easy to use. I have been tinkering for more than a week but failed to compile successfully under Windows. So at least do it in a Unix environment. Mac and various Linux environments are available.
Download PHP source code
First use php -v to determine the PHP version on the system, and then download the corresponding source code package from php.net. Unzip it to a directory, such as php5-5.3.5. In the source code directory, the ext directory is where all PHP extensions are located, and other directories do not need to be considered for the time being.
Generate framework code for PHP extension
In the php5-5.3.5/ext directory, there is a file called ext_skel, which is a convenient tool for creating extensions. Make sure it has executable permissions (chmod u+x ext_skel) and execute it in the terminal
./ext_skel --extname=hello
That is, a hello directory will be created in the ext directory, which contains the initial skeleton code. The next task is to create the hello extension and implement the hello_world function.
Edit config.m4
Open ext/hello/config.m4 with a text editor. There are a lot of comments in it (lines starting with dnl), which basically explain many problems. What we need to do here is to put
dnl PHP_ARG_ENABLE(hello, whether to enable hello support, dnl Make sure that the comment is aligned: dnl [ --enable-hello Enable hello support])
Uncomment these three lines. In this way, during the next compilation, you can use ./configure --enable-hello to compile the extension we just wrote.
Regenerate configure
Go back to the source code root directory and run ./buildconf --force to activate the configure --enable-hello parameter. If you run buildconf you get the following error:
buildconf: Your version of autoconf likely contains buggy cache code. Running vcsclean for you. To avoid this, install autoconf-2.13.
Please install autoconf-2.13 (ubuntu lazy person’s usage)
sudo apt-get install autoconf2.13
Compile extension
At this time, the hello extension can be compiled, although the hello_world function has not yet been implemented. Compile it first to make sure there are no environment configuration problems.
./configure --enable-hello make
After a period of time (actually the entire PHP was compiled), use
./sapi/cli/php -f ext/hello/hello.php
Check the extension compilation. If nothing else, it should prompt
Functions available in the test extension: confirm_hello_compiled Congratulations! You have successfully modified ext/hello/config.m4. Module hello is now compiled into PHP.
Write hello_world function
Declare function: Open ext/hello/php_hello.h, in
PHP_MINIT_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span> PHP_MSHUTDOWN_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span> PHP_RINIT_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span> PHP_RSHUTDOWN_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span> PHP_MINFO_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span>
后面添加
PHP_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello_world<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span>
即在扩展的头文件中声明了 hello_world 的函数的原型。PHP_FUNCTION 是用来定义 PHP 函数的 C 语言宏。至于宏展开后的样子,几乎不用去想。只管用就可以了。
实现函数:打开 hello.c,在文件的末尾添加
PHP_FUNCTION<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello_world<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">{</span> php_printf<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(255, 0, 0); ">"Hello World"</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(177, 177, 0); ">return</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">}</span>
这里即是 hello_world 函数的实现。php_printf 的作用是向 SAPI 输出一段字符串,类似于 PHP 语言中的 echo。
接下来还需要将 hello_world 函数注册到 zend_module_entry,这样这个函数才能在 PHP 程序中变成“可见”的。找到
<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(153, 51, 51); ">const</span> zend_function_entry hello_functions<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">[</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">]</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">=</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">{</span> PHP_FE<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>confirm_hello_compiled<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; ">/* For testing, remove later. */</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">{</span>NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">}</span><span style="font-family: Tahoma, sans-serif; line-height: normal; ">/* Must be the last line in hello_functions[] */</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">}</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span>
将其修改为:
<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(153, 51, 51); ">const</span> zend_function_entry hello_functions<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">[</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">]</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">=</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">{</span> PHP_FE<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>confirm_hello_compiled<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; ">/* For testing, remove later. */</span> PHP_FE<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">(</span>hello_world<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">)</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">{</span>NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">,</span> NULL<span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">}</span><span style="font-family: Tahoma, sans-serif; line-height: normal; ">/* Must be the last line in hello_functions[] */</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(0, 153, 0); ">}</span><span style="font-family: Tahoma, sans-serif; line-height: normal; color: rgb(51, 153, 51); ">;</span>
此时整个的 hello 扩展的代码就编写完了。最后再来 make 一下。
测试
在终端下运行 sapi/cli/php -r 'hello_world();echo "\n";',如果看到输出“Hello World”,就成功了。
如何把扩展编译成 .so 文件
上面编译的结果是把 hello 扩展编译进了 PHP 核心中。如果想要编译成 .so 扩展,以便发布出去的话。需要使用
./configure --enable-hello=shared make
这样编译完成后,会在 modules 目录下生成 hello.so 文件。把它复制到你的 PHP 运行环境的 extension_dir 下就可以像其他扩展一样使用了。需要注意的是 PHP 版本。如果你是在 PHP 5.3.5 的源代码环境中编译的扩展,则生成的 .so 文件只能用在 PHP 5.3.5 的运行环境中。
最后提一下,如果对 PHP 扩展有兴趣,可以看看《Extending and Embedding PHP》这本书,作者还是个 MM。目前没有中文版,英文电子版的自己搜。
摘自 lostwolf blog