>백엔드 개발 >PHP 튜토리얼 >Linux 下PHP扩展开发系列:二. 一个典型的扩展开发_PHP教程

Linux 下PHP扩展开发系列:二. 一个典型的扩展开发_PHP教程

WBOY
WBOY원래의
2016-07-20 11:14:08753검색

 

看完前言中所说的一些内容后,各位应该对PHP扩展开发有个笼统的了解了,可能有些人会觉得开发扩展很麻烦很复杂,实际上并非如此,这一篇我们就快速进入角色,开发出我们的第一个扩展。


 

一、编译PHP

开发之前还需要先准备好PHP源码并编译,过程如下:

<span tar</span> -zxvf php-<span 5.3</span>.<span 9</span>.<span tar</span><span .gz
cd php</span>-<span 5.3</span>.<span 9</span>

我使用的是php5.3.9,解压后,我们进入了PHP源码目录,然后我们直接编译并增加php.ini:

./configure --prefix=/usr/local/webserver/php --enable-fastcgi --enable-fpm --enable-<span debug
</span><span make</span> && <span make</span> <span install</span>
<span cp</span> /home/soft/php-<span 5.3</span>.<span 9</span>/php.ini-development /usr/local/webserver/php/lib/php.ini

编译完成,我没有静态编译其他扩展,但是开启了debug,这个后面会用到。然后修改php.ini中对应的项,这里就不细说了。

现在把PHP相关加入环境变量中,省去后面很多工作:

vim /root/.bash_profile

我是使用root,其他不同用户修改对应用户目录下的.bask_profile文件,在文件中的PATH后面加入:/usr/local/webserver/php/bin/,类似下面这样:

PATH=$PATH:$HOME/bin:/usr/local/webserver/php/bin/

环境变量设置好了,我们查看下PHP版本:

 OK,编译工作完成,让我们继续。

 

二、典型开发流程

一个典型的扩展开发流程如下图:

 

 三、扩展功能定义

-2147483648 到 2147483647,与32位系统相同。

 

四、正式开发  

   

   

 

cd /home/soft/php-<span 5.3</span>.<span 9</span>/ext

 

    然后了解下PHP提供的扩展骨架工具ext_skel生成骨架,ext_skel的用法如下:

./ext_skel --extname=module [--proto=<span file</span>] [--stubs=<span file</span>] [--xml[=<span file</span><span ]]
           [</span>--skel=<span dir</span>] [--full-xml] [--no-<span help]

  </span>--extname=<span module   module is the name of your extension(模块名,会在当前目录创建一个该名称子目录)
  </span>--proto=<span file</span>       <span file</span><span  contains prototypes of functions to create(函数原型定义文件)
  </span>--stubs=<span file</span>       generate only <span function</span> stubs <span in</span> <span file</span>
  --xml              generate xml documentation to be added to phpdoc-<span cvs
  </span>--skel=<span dir</span>         path to the skeleton directory(设置骨架生成的目录,不设置该项则默认在ext/<span extname下)
  </span>--full-xml         generate xml documentation <span for</span> a self-<span contained extension
                     (not yet implemented)
  </span>--no-help          don<span '</span><span t try to be nice and create comments in the code</span>
                     and helper functions to test <span if</span> the module compiled (生成的代码中不显示各种帮助注释)

    这次我们准备用到两个选项,--extname=myip 即定义扩展的名称,而--proto=myip.pro则是定义扩展的函数原型,首先我们生成扩展函数原型文件:

vim myip.pro

    加入以下内容:

<span int</span> ip2long32(<span string</span> ip)

    这意味着我们的扩展中有一个函数,返回值为int型,输入为string。

    这时候执行以下命令生成扩展骨架:

./ext_skel --extname=myip --proto=myip.pro

    OK,这时候你会发现在当前PHP扩展目录下生成了一个子目录myip,进入myip看下:

<span cd myip
ll</span>

    你会发现生成了一堆文件,如下图:

    此时我们就可以进行第二步了。

    2. 修改config.m4

    关于config.m4文件的功能,我们留到后面的文章中在详细进行说明,现在只说明要做什么。

    使用vim编辑config.m4:

vim config.m4

    将16至18行行首的dnl去掉,如下:

    具体这样做的原因在后面的文章中会说明,这边我们直接退出并保存config.m4,继续进入下一步。

    3. 编码

    重头戏来啦,终于可以进入myip.c中进行功能的编码了,一起欢呼下吧!

vim myip.c

   找到下图所示的位置:

   图中就是扩展骨架工具根据我们提供的函数原型生成的对应函数,此处有几个需要注意的地方:

   1. PHP_FUNCTION:是PHP核心定义的一个宏,与ZEND_FUNCTION相同,用于定义扩展函数,实际生成的函数名称为zif_ip2long32。

   2. zend_parse_parameters:由于PHP为弱类型语言,而C是强类型,因此需要使用该函数用于接收PHP传入的参数,并进行一定的类型转换,将PHP的变量转为C语言能够辨认的类型。

      zend_parse_parameters函数的原型如下:

zend_parse_parameters(<span int</span> num_args TSRMLS_CC, <span char</span> *type_spec, &hellip;);

      参数说明:

  •       num_args:传递给函数的参数个数。通常的做法是使用宏 ZEND_NUM_ARGS()。
  •       TSRMLS_CC:线程安全,总是传递TSRMLS_CC宏。 详解:http://www.54chen.com/php-tech/what-is-tsrmls_cc.html
  •       type_spec:第三个参数是一个字符串,指定了函数期望的参数类型
  •       ...:需要随参数值更新的变量列表]

  • type_spec是格式化字符串,其常见的含义如下: 参数   代表着的类型 b   Boolean l   Integer 整型 d   Floating point 浮点型 s   String 字符串 r   Resource 资源 a   Array 数组 o   Object instance 对象 O   Object instance of a specified type 特定类型的对象 z   Non-specific zval 任意类型~ Z   zval**类型 f   表示函数、方法名称

   我们将该函数修改为如下内容:

<span  PHP_FUNCTION(ip2long32)
 {
         </span><span char</span> *ip =<span  NULL;
         </span><span int</span> argc =<span  ZEND_NUM_ARGS();
         </span><span int</span><span  ip_len;
 
         </span><span if</span> (zend_parse_parameters(argc TSRMLS_CC, <span "</span><span s</span><span "</span>, &ip, &ip_len) ==<span  FAILURE) {
                 </span><span return</span><span ;
         }
         
         int32_t ip_int32;
         unsigned </span><span char</span><span  ip1, ip2, ip3, ip4;
         
         sscanf(ip, </span><span "</span><span %hhu.%hhu.%hhu.%hhu</span><span "</span>, &ip1, &ip2, &ip3, &<span ip4);
         ip_int32 </span>= (int32_t)((ip1 << <span 24</span>) | (ip2 << <span 16</span>) | (ip3 << <span 8</span>) |<span  ip4);
         RETURN_LONG(ip_int32);
 }</span>

    功能完成了,这边有个RETURN_LONG(ip_int32)比较特殊,这也是PHP内核提供的宏,用于返回值给PHP,具体说明如下:

设置返回值并且结束函数        设置返回值             宏返回类型和参数
RETURN_LONG(l)           RETVAL_LONG(l)           整数
RETURN_BOOL(b)          RETVAL_BOOL(b)           布尔数(1或0)
RETURN_NULL()           RETVAL_NULL()           NULL
RETURN_DOUBLE(d)        RETVAL_DOUBLE(d)        浮点数
RETURN_STRING(s, dup)       RETVAL_STRING(s, dup)       字符串。如果dup为1,引擎会调用estrdup()重复s,使用拷贝。如果dup为0,就使用s
RETURN_STRINGL(s, l, dup)     RETVAL_STRINGL(s, l, dup)    长度为l的字符串值。与上一个宏一样,但因为s的长度被指定,所以速度更快。
RETURN_TRUE           RETVAL_TRUE            返回布尔值true。注意到这个宏没有括号。
RETURN_FALSE           RETVAL_FALSE           返回布尔值false。注意到这个宏没有括号。
RETURN_RESOURCE(r)        RETVAL_RESOURCE(r)       资源句柄。

    编码完成了,保存并退出,然后我们可以开始编译了。

    4. 编译

<span phpize
.</span>/configure --with-php-config=/usr/local/webserver/php/bin/php-<span config
</span><span make</span> && <span make</span> <span install</span>

    不出意外的话编译完成后会有如下提示:

Installing shared extensions:     /usr/local/webserver/php/lib/php/extensions/debug-non-zts-<span 20090626</span>/

    进入该目录看下是否已经有myip.so,有的话最后我们就可以修改php.ini载入该so文件

    5. 修改php.ini

cd /usr/local/webserver/php/<span lib
vim php.ini</span>

    修改extension_dir,并加入 extension = myip.so

extension_dir = "/usr/local/webserver/php/lib/php/extensions/debug-non-zts-20090626/"<span 
extension </span>= myip.so

    退出保存,并重启php,如果是使用Phpfpm的话可以执行如下命令:

<span kill</span> -USR2 `<span cat</span> /usr/local/webserver/php/var/run/php-fpm.pid`

    看下扩展是否正常载入:

[root@tm977 lib]# php -m|<span grep</span><span  myip
myip</span>

    说明已经正常载入了,最后我们测试下扩展函数吧!

    6. 测试

php -r <span "</span><span var_dump(ip2long32('192.168.1.1'));</span><span "</span>
<span int</span>(-<span 1062731519</span><span )
php </span>-r <span "</span><span var_dump(ip2long('192.168.1.1'));</span><span "</span>  
<span int</span>(<span 3232235777</span>)

    如上所示,ip2long32输出的是32位有符号整数,而ip2long输出的是64位无符号整数,大功告成!

 

五、小结

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/440319.htmlTechArticle看完前言中所说的一些内容后,各位应该对PHP扩展开发有个笼统的了解了,可能有些人会觉得开发扩展很麻烦很复杂,实际上并非如此,这...
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.