文档章节

如何基于 PHP-X 快速开发一个 PHP 扩展

matyhtf
 matyhtf
发布于 2017/09/11 14:39
字数 1624
阅读 232
收藏 6

PHP-X是我在2017年年初创建的一个新项目。这个项目的目标就是让有一定工作经验的PHP程序都能够具备扩展开发的能力

0x00 初衷

2012年开始编写swoole,现在算来已经有5个年头了。我发现编写一个 PHP 扩展这个工作非常艰难。PHP 程序员群体中,甚至可以说 100 人中都很难找出一个会编写 PHP 扩展的人来。PHP 官方对扩展开发者非常不友好,源代码中提供的Zend API极其难用,API复杂而且凌乱,充斥着各种宏的写法。Zend API坑非常多,普通开发者很容易踩到坑里。出现各种莫名其妙的core dump问题。Zend API几乎没有任何文档,开发者如果要真正掌握这项技能需要付出大量的学习时间。

于是我今年就冒出一个新的想法,基于我编写swoole扩展超过5年的经验,我试图在Zend APIC++之间建立一个包装层,让PHP扩展开发变得简单。有一定C++基础的PHPer都可以轻松得开发一个PHP扩展。

PHP-X这个项目就这样诞生了,开发只用了一个月的时间。它的开发效率非常高,在我公司中一个只工作了3年的 PHP 程序员,都可以做出一个扩展来。接下来陆续在公司的几个项目中进行了快速验证。在3个的时间里修复了大量崩溃和内存泄漏问题。目前稳定性、性能、健壮性均已达到工业级水准。

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 新建工程

使用任意开发工具,新建一个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
通过操作argsretval两个变量,就可以实现函数的输入和输出

这里我们的代码非常简单,cpp_test($str, $n),调用这个函数返回一个$n$str的数组。

#include <phpx.h>
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++编译参数中增加-undefined dynamic_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
pdo_sqlite
Phar
posix
redis
Reflection
session
SimpleXML
sockets
SPL
sqlite3
standard
swoole
test
tokenizer
xml
xmlreader
xmlwriter
yac
zlib
zmq

[Zend Modules]

这里看到test,表明你的扩展已经加载成功了,现在就可以调用cpp_test这个扩展函数了。

0x05 执行

编写一个test.php,内容为:

<?php
var_dump(cpp_test("hello", 3));

执行test.php

php test.php
array(3) {
  [0]=>
  string(5) "hello"
  [1]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}

可以看到执行结果符合预期。那么恭喜你,现在你已经成功地开发了一个PHP扩展了。是不是很简单?

0x06 更多

上面的例子还比较简单,只是编写了一个扩展函数。要真正在实际项目中使用PHP-X你还有很多工作要做。

  • 需要C++的功底
  • 了解更多PHP-XAPI

另外配合使用EclipseIDE工具,可以实现API自动提示和补齐,开发起来会更顺手。

相比Zend APIPHP-X要简单易用地多了,相信你不会花太多时间就可以掌握此项技能。在接下来我会撰写更多教程,教大家如何使用PHP-X实现扩展类、资源、回调函数等更复杂的功能。

© 著作权归作者所有

共有 人打赏支持
matyhtf

matyhtf

粉丝 420
博文 13
码字总数 7425
作品 8
深圳
程序员
私信 提问
加载中

评论(3)

海之源666
海之源666
非常不错,大赞!
t
tianmac
期待更多的文章
MrMyj
MrMyj
成功通过安装,php-x 已开发自己的 扩展 太爽了
基于协程和 Swoole 驱动的高性能 PHP 框架--TSF

TSF 是腾讯开源的一套基于协程和 Swoole 驱动的高性能 PHP 框架,可以帮助开发者快速开发和部署具备高 IO 吞吐能力的 HTTP/TCP/UDP 服务。 TSF 具备以下特点: 基于 PHP,相比 C++ 等语言具有...

叶秀兰
2015/07/06
13.4K
14
「ThinkPHP开发者周刊」第4期——日志、调试和通知

[ 本周读数 ] ——官方扩展数量已经超过个 ThinkPHP版本开始,官方的扩展全部通过安装,目前官方维护以及参与的扩展已经超过36个,并且都已经支持最新的版本,涵盖和满足了主流的现代化开发需...

流年
11/05
0
0
轻量级PHP开发框架--CookPHP

CookPHP 开发框架是一个基于PHP7的自由、免费开源、快速、简单的面向对象的轻量级PHP开发框架,并由珠海旭爱科技有限公司团队开发与维护。 CookPHP一直也实践与众筹系统、多用户商城等商业项目...

费尔
2016/09/02
961
0
php&go&python&node

2016 第二届 PHP 全球开发者大会回顾(文末附演讲嘉宾所有资料下载) 继前年的 “PHP7 初探”、去年的“高性能的 PHP ” 主题后,2017 第三届 PHP 全球开发者大会的活动主题是“高可用的 PH...

掘金官方
2017/12/20
0
0
PHP开发框架--Leaps

=Leaps简单php开发 for You != 现在PHP开发框架非常多,一个开发框架优秀与否与它有多少特性没有太大的关系。特性也不能告诉你框架代码的质量如何、性能如何、细节处理的如何、安全性如何。...

灵魂架构师
2013/01/17
945
0

没有更多内容

加载失败,请刷新页面

加载更多

jquery通过id显示隐藏

var $div3 = $('#div3'); 显示 $div3.show(); 隐藏 $div3.hide();

yan_liu
今天
3
0
《乱世佳人》读书笔记及相关感悟3900字

《乱世佳人》读书笔记及相关感悟3900字: 之前一直听「荔枝」,后来不知怎的转向了「喜马拉雅」,一听就是三年。上班的时候听房产,买房了以后听装修,兴之所至时听旅行,分手后听亲密关系,...

原创小博客
今天
3
0
大数据教程(9.6)map端join实现

上一篇文章讲了mapreduce配合实现join,本节博主将讲述在map端的join实现; 一、需求 实现两个“表”的join操作,其中一个表数据量小,一个表很大,这种场景在实际中非常常见,比如“订单日志...

em_aaron
今天
3
0
cookie与session详解

session与cookie是什么? session与cookie属于一种会话控制技术.常用在身份识别,登录验证,数据传输等.举个例子,就像我们去超市买东西结账的时候,我们要拿出我们的会员卡才会获取优惠.这时...

士兵7
今天
3
0
十万个为什么之为什么大家都说dubbo

Dubbo是什么? 使用背景 dubbo为什么这么流行, 为什么大家都这么喜欢用dubbo; 通过了解分布式开发了解到, 为适应访问量暴增,业务拆分后, 子应用部署在多台服务器上,而多台服务器通过可以通过d...

尾生
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部