如何基于 PHP-X 快速开发一个 PHP 扩展
0x01 起步
PHP-X本身基于C++11开发,使用cmake进行编译配置。首先,你需要确定所有依赖项已安装好。包括:
- gcc-4.8 或更高版本
- PHP7.0 或更高版本,需要php7-dev 开发包
- cmake-2.8 或更高版本
然后安装PHP-X。
git clone https://github.com/swoole/PHP-X.git cd PHP-X cmake . make -j 4 sudo make install
未出现任何编译错误,会成功编译出libphpx.so,并安装到系统的lib目录。头文件会复制到系统的include目录。这时需要执行 sudo ldconfig刷新so文件缓存。
0x02 新建工程
使用任意开发工具,新建一个http://test.cc源文件。首先需要引入phpx.h头文件。然后使用using引入phpx的命名空间。PHP官方未使用C++,因此phpx直接使用了php作为命名空间。
#include <phpx.h> using namespace std; using namespace php;
创建扩展使用PHPX_EXTENSION宏来实现。在这宏中只需要new Extension即可创建扩展。构造方法接受2个参数,第一个是扩展的名称,第二个是扩展的版本号。在PHPX_EXTENSION宏中return这个扩展对象的指针。
PHPX_EXTENSION() { Extension *ext = new Extension("test", "0.0.1"); return ext; }
这里必须使用 new Extension,而不能直接在栈上创建对象
0x03 增加函数
一个PHP扩展的主要作用就是提供扩展函数,扩展函数由于是用C/C++代码实现,因此它的性能会比PHP用户函数性能高几十甚至上百倍。在phpx中实现函数非常简单。使用PHPX_FUNCTION来实现扩展函数,然后调用Extension::registerFunction来注册扩展函数。
PHPX_FN是一个助手宏,实际上展开就是"cpp_hello_world", cpp_hello_world PHPX_FUNCTION展开后,包含了2个变量,第一个是参数args,第二个是返回值retval
通过操作args和retval两个变量,就可以实现函数的输入和输出
这里我们的代码非常简单,cpp_test($str, $n),调用这个函数返回一个$n个$str的数组。
1 #include <phpx.h> 2 using namespace std; 3 using namespace php; 4 5 //声明函数 6 PHPX_FUNCTION(cpp_test); 7 8 PHPX_EXTENSION() 9 { 10 Extension *ext = new Extension("test", "0.0.1"); 11 ext->registerFunction(PHPX_FN(cpp_test)); 12 return ext; 13 } 14 15 //实现函数 16 PHPX_FUNCTION(cpp_test) 17 { 18 //args[1] 就是这个扩展函数的第 2 个参数 19 long n = args[1].toInt(); 20 21 //将返回值 retval 初始化为数组 22 Array _array(retval); 23 24 for(int i = 0; i < n; i++) 25 { 26 //args[0] 就是这个扩展函数的第 1 个参数 27 //append 方法表示向数组中追加元素 28 _array.append(args[0]); 29 } 30 }
0x04 编译扩展
编写一个Makefile文件。内容如下:
1 PHP_INCLUDE = `php-config --includes` 2 PHP_LIBS = `php-config --libs` 3 PHP_LDFLAGS = `php-config --ldflags` 4 PHP_INCLUDE_DIR = `php-config --include-dir` 5 PHP_EXTENSION_DIR = `php-config --extension-dir` 6 7 test.so: test.cc 8 c++ -DHAVE_CONFIG_H -g -o test.so -O0 -fPIC -shared test.cc -std=c++11 ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} -lphpx 9 10 install: test.so 11 cp test.so ${PHP_EXTENSION_DIR}/ 12 clean: 13 rm *.so
php-config 这个工具是PHP提供的,使用php-config可以得到PHP的安装路径、头文件目录、扩展目录、其他额外的编译参数等等。
这个Makefile支持了3个指令,make编译,make clean清理,make install安装到扩展目录中。
这里可能需要root权限,使用sudo make install进行安装
直接从网页复制,可能会出现tab制表符被替换为空格,请手工编辑一下Makefile使用tab缩进 MacOS下需要在c++编译参数中增加-undefined dynamic_lookup
编写好之后执行make install,就会编译扩展并将扩展test.so安装到PHP的扩展目录中。这时需要修改php.ini加入extension=test.so加载扩展。
使用php -m来观察你的扩展是否正常加载。
1 php -m 2 [PHP Modules] 3 Core 4 ctype 5 curl 6 date 7 dom 8 fileinfo 9 filter 10 gd 11 hash 12 iconv 13 inotify 14 json 15 libxml 16 mbstring 17 mcrypt 18 memcached 19 mongodb 20 mysqli 21 mysqlnd 22 openssl 23 pcntl 24 pcre 25 PDO 26 pdo_mysql 27 pdo_sqlite 28 Phar 29 posix 30 redis 31 Reflection 32 session 33 SimpleXML 34 sockets 35 SPL 36 sqlite3 37 standard 38 swoole 39 test 40 tokenizer 41 xml 42 xmlreader 43 xmlwriter 44 yac 45 zlib 46 zmq 47 48 [Zend Modules]
这里看到test,表明你的扩展已经加载成功了,现在就可以调用cpp_test这个扩展函数了。
0x05 执行
编写一个test.php,内容为:
1 <?php 2 var_dump(cpp_test("hello", 3)); 3 执行test.php: 4 5 php test.php 6 array(3) { 7 [0]=> 8 string(5) "hello" 9 [1]=> 10 string(5) "hello" 11 [2]=> 12 string(5) "hello" 13 }
可以看到执行结果符合预期。那么恭喜你,现在你已经成功地开发了一个PHP扩展了。是不是很简单?
0x06 更多
上面的例子还比较简单,只是编写了一个扩展函数。要真正在实际项目中使用PHP-X你还有很多工作要做。
- 需要C++的功底
- 了解更多PHP-X的 API
另外配合使用Eclipse等IDE工具,可以实现API自动提示和补齐,开发起来会更顺手。
相比Zend API,PHP-X要简单易用地多了,相信你不会花太多时间就可以掌握此项技能。在接下来我会撰写更多教程,教大家如何使用PHP-X实现扩展类、资源、回调函数等更复杂的功能。