PHP-X
是我在
2017
年年初创建的一个新项目。这个项目的目标就是
让有一定工作经验的
PHP
程序都能够具备扩展开发的能力
。
0x00 初衷
从
2012
年开始编写
swoole
,现在算来已经有
5
个年头了。我发现编写一个 PHP 扩展这个工作非常艰难。PHP 程序员群体中,甚至可以说 100 人中都很难找出一个会编写 PHP 扩展的人来。PHP 官方对扩展开发者非常不友好,源代码中提供的
ZendAPI
极其难用,
API
复杂而且凌乱,充斥着各种宏的写法。
ZendAPI
坑非常多,普通开发者很容易踩到坑里。出现各种莫名其妙的
coredump
问题。
ZendAPI
几乎没有任何文档,开发者如果要真正掌握这项技能需要付出大量的学习时间。
于是我今年就冒出一个新的想法,基于我编写
swoole
扩展超过
5
年的经验,我试图在
ZendAPI
和
C++
之间建立一个包装层,让
PHP
扩展开发变得简单。有一定
C++
基础的
PHPer
都可以轻松得开发一个
PHP
扩展。
PHP-X
这个项目就这样诞生了,开发只用了一个月的时间。它的开发效率非常高,在我公司中一个只工作了
3
年的 PHP 程序员,都可以做出一个扩展来。接下来陆续在公司的几个项目中进行了快速验证。在
3
个的时间里修复了大量崩溃和内存泄漏问题。目前稳定性、性能、健壮性均已达到工业级水准。
0x01 起步
PHP-X
本身基于
C++11
开发,使用
cmake
进行编译配置。首先,你需要确定所有依赖项已安装好。包括:
然后安装
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 新建工程
使用任意开发工具,新建一个
test.cc
源文件。首先需要引入
phpx.h
头文件。然后使用
using
引入
phpx
的命名空间。
PHP
官方未使用
C++
,因此
phpx
直接使用了
php
作为命名空间。
#include
using namespace std;
using namespace php;
创建扩展使用
PHPX_EXTENSION
宏来实现。在这宏中只需要
newExtension
即可创建扩展。构造方法接受
2
个参数,第一个是扩展的名称,第二个是扩展的版本号。在
PHPX_EXTENSION
宏中
return
这个扩展对象的指针。
PHPX_EXTENSION()
{
Extension *ext = new Extension("test", "0.0.1");
return ext;
}
这里必须使用
newExtension
,而不能直接在栈上创建对象
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
的数组。
#include
using
namespace std;
using namespace php;
//声明函数
PHPX_FUNCTION(cpp_test);
PHPX_EXTENSION()
{
Extension *ext = new Extension("test", "0.0.1");
ext->registerFunction(PHPX_FN(cpp_test));
return ext;
}
//实现函数
PHPX_FUNCTION(cpp_test)
{
//args[1] 就是这个扩展函数的第 2 个参数
long n = args[1].toInt();
//将返回值 retval 初始化为数组
Array _array(retval);
for(int i = 0; i < n; i++)
{
//args[0] 就是这个扩展函数的第 1 个参数
//append 方法表示向数组中追加元素
_array.append(args[0]);
}
}
0x04 编译扩展
编写一个
Makefile
文件。内容如下:
PHP_INCLUDE = `php-config --includes`
PHP_LIBS =
`php-config --libs`
PHP_LDFLAGS = `php-config --ldflags`
PHP_INCLUDE_DIR = `php-config --include-dir`
PHP_EXTENSION_DIR = `php-config --extension-dir`
test.so: test.cc
c++ -DHAVE_CONFIG_H -g -o test.so -O0 -fPIC -shared test.cc -std=c++11 ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} -lphpx
install: test.so
cp test.so ${PHP_EXTENSION_DIR}/
clean:
rm *.so
php-config
这个工具是
PHP
提供的,使用
php-config
可以得到
PHP
的安装路径、头文件目录、扩展目录、其他额外的编译参数等等。
这个
Makefile
支持了
3
个指令,
make
编译,
make clean
清理,
make install
安装到扩展目录中。
这里可能需要
root
权限,使用
sudo make install
进行安装
直接从网页复制,可能会出现
tab
制表符被替换为空格,请手工编辑一下
Makefile
使用
tab
缩进
MacOS
下需要在
c++
编译参数中增加
-undefineddynamic_lookup
编写好之后执行
make install
,就会编译扩展并将扩展
test.so
安装到
PHP
的扩展目录中。这时需要修改
php.ini
加入
extension=test.so
加载扩展。
使用
php-m
来观察你的扩展是否正常加载。
php -m
[PHP Modules]
Core
ctype
curl
date
dom
fileinfo
filter
gd
hash
iconv
inotify
json
libxml
mbstring
mcrypt
memcached
mongodb
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql