1.前言
关于php的扩展开发,网上有很多教程,搜一搜有很多.很多的教程已经和实际使用上有了一定的出入了.文本就算是做一个更新吧.
关于第三方库的引用上,我只做一种方法的描述.也许还有更好更简便的方法,希望能和大家有个交流.
现在假设一下我的需求: 有一个复杂的引用了其他第三方静态库的功能库使其能够让php调用.
2.编写扩展
编写之前,我们先大概捋一下,我们有什么,要做些什么.
先说一下主要的功能逻辑都在功能库里封装好了,
php扩展调用就可以了.而功能库又依赖于几个第三方的静态库.(为什么是静态库,不是本文的重点 -_-
). 好了总结下来我们要做如下工作:
1.生成php扩展框架
1.改写php扩展调用功能的.cpp
2.重新编译第三方库 libb1.a libb2.a
libb3.a (为什么要重新编译后面说)
3.重新编译功能库 liba1.a
4.修改扩展框架的Makefile文件
5.再手动编译成完整的扩展库.so
2.1生成框架
生成之前需要写一个以skel后缀的php接口定义文档.把需要的接口都定义出来,以换行分隔.例如:
int test1(string filePath)
bool test2(int maxNum)
void test3(bool isOK)
在php的目录下的ext文件夹下找到ext_skel,把刚才写的skel文件放到该目录下执行:
[root@localhost ext]# ./ext_skel --extname=test
--proto=test.skel
就会在该级目录下生成一个test的文件夹,并提示
Creating directory test
awk: /opt/phpext/php/ext/skeleton/create_stubs:56: 警告:
转义序列“\|”被当作单纯的“|”
NOTICE: lower casing function name 'test1'
NOTICE: lower casing function name 'test2'
Creating basic files: config.m4 config.w32 .gitignore test.c
php_test.h CREDITS EXPERIMENTAL tests/001.phpt test.php [done].
To use your new extension, you will have to execute the
following steps:
1. $ cd ..
2. $ vi ext/test/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-test
5. $ make
6. $ ./sapi/cli/php -f ext/test/test.php
7. $ vi ext/test/test.c
8. $ make
Repeat steps 3-6 until you are satisfied with ext/test/config.m4
and
step 6 confirms that your module is compiled into PHP. Then, start
writing
code and repeat the last two steps as often as necessary.
[root@localhost ext]#
这就说的很明白了,那么我们就按照这提示步骤做吧.
2.2修改config.m4文件
我们把 10-12行的注释去掉
dnl PHP_ARG_WITH(test, for test
support,
dnl Make sure that the comment is
aligned:
dnl [ --with-test Include test support])
变为:
PHP_ARG_WITH(test, for test support,
Make sure that the comment is aligned:
[ --with-test Include test support])
***如果扩展没有再用别的库,那么到这就可以了, 如果还用了一堆其他的库,继续....
在最后一个方法PHP_NEW_EXTENSION(test, test.c,
$ext_shared)前,加上:
PHP_REQUIRE_CXX() //如果用到c++的话
PHP_ADD_LIBRARY(stdc++,"/usr/lib/gcc/x86_64-redhat-linux/4.4.4/",
TEST_SHARED_LIBADD)
//加上我们要用的功能库和功能库使用的第三方库
PHP_ADD_LIBRARY_WITH_PATH(a1, "/opt/phptest/test/libs",
TEST_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(b1, "/opt/phptest/test/libs",
TEST_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(b2, "/opt/phptest/test/libs",
TEST_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(b3, "/opt/phptest/test/libs",
TEST_SHARED_LIBADD)
PHP_SUBST(TEST_SHARED_LIBADD)
如果是c++,还需要将这句中的文件后缀改为cpp,文件也同步改为cpp
PHP_NEW_EXTENSION(test, test.c, $ext_shared)
就此,m4文件就修改完毕了.
2.3写你的方法代码
生成框架后, 会生成以项目名命名的.c文件.会有如下的类似代码
//{{{ proto int test1(string filePath)
PHP_FUNCTION(test1) //这就是我们在skel文件里申明的方法,注释是他的c原型
{
char *filePath = NULL;
int argc = ZEND_NUM_ARGS();
int filePath_len;
//获取php中传来的参数
if (zend_parse_parameters(argc TSRMLS_CC, "s", &filePath,
&filePath_len) == FAILURE) return;
//将下面的错误提示注掉,然后写上自己的代码就可以了, 当然也可以用这种形式做异常信息的抛出
php_error(E_WARNING, "test1: not yet implemented");
}
2.4编译
在和config.m4同级目录下执行phpize,就可以生成出来configure了, 然后make,然后很快就提示Build
complete.了, 现在modules文件夹下面生成了了一个test.so,这就是我们需要的扩展库了,哈哈....等等...大小不对,还没第三库的一半大.合着第三方的静态库都没打进去.
这时有个最简单的方法将你自己的静态库也打进去.
手动执行:
g++ -shared .libs/test.o -o .libs/test.so
-L/opt/phpext/php/ext/test/include -la1 -lb1 -lb2
-lb3
执行后很有可能会报
/usr/bin/ld:
//opt/phpext/php/ext/test/include/liba1.a(xxxxx.cpp.o): relocation
R_X86_64_32 against `.bss' can not be used when making a shared
object; recompile with -fPIC
//opt/phpext/php/ext/test/include/liba1.a: could not read symbols:
Bad value
问题的原因和解决方法说的很明白了, 这下我们去将liba1的编译文件修改一下,将-fPic 的参数加上重新编译.
将这些第三方库全部重新编译后,当然,有些库他自身的编译本身就带有该参数的就不用重新编译了,比方说Freetype库.
至此,我们得到了一个test.so的php扩展, 赶紧放到环境中去测试一下吧.
--------------------------------------------------------------------------------------------------