热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

浅谈PHPExtension的开发——基础篇

要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。我下载的是PHP最新版本5.3.3,格式为tar.bz2压缩包。
要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。我下载的是PHP最新版本5.3.3,格式为tar.bz2压缩包。

摘要&引言
PHP是当前应用非常广泛的一门语言,从国外的Facebook、Twitter到国内的淘宝、腾讯、百度再到互联网上林林总总的各种大中小型网站都能见到它的身影。PHP的成功,应该说很大程度上依赖于其开放的扩展API机制和丰富的扩展组件(PHP Extension),正是这些扩展组件使得PHP从各种数据库操作到XML、JSON、加密、文件处理、图形处理、Socket等领域无所不能。有时候开发人员可能需要开发自己的PHP扩展,当前PHP5的扩展机制是基于Zend API的,Zend API提供了丰富的接口和宏定义,加上一些实用工具,使得PHP扩展开发起来难度并不算特别大。本文将介绍关于PHP扩展组件开发的基本知识,并通过一个实例展示开发PHP扩展的基本过程。

PHP扩展组件的开发过程在Unix和Windows环境下有所不同,但基本是互通的,本文将基于Unix环境(具体使用Linux)。阅读本文需要简单了解Unix环境、PHP和C语言的一些基础知识,只要简单了解就行,我会尽量不涉及太过具体的操作系统和语言特性,并在必要的地方加以解释,以便读者阅读。

本文的具体开发环境为Ubuntu 10.04 + PHP 5.3.3。

下载PHP源代码
要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。我下载的是PHP最新版本5.3.3,格式为tar.bz2压缩包。下载地址为:https://www.php1.cn/。

下载后,将源代码移动到合适的目录并解压。解压命令为:

代码如下:

tar -jxvf 源码包名称

若下载的是tar.gz压缩包,解压命令为

代码如下:

tar -zxvf 源码包名称

解压后,在源代码目录中有个ext目录,这里便是和PHP扩展有关的目录。进入目录后用ls查看,可以看到许多已经存在的扩展。下图是在我的环境下查看的结果:


代码如下:

dnl $Id$ 
dnl config.m4 for extension say_hello 
dnl Comments in this file start with the string 'dnl'. 
dnl Remove where necessary. This file will not work 
dnl without editing. 
dnl If your extension references something external, use with: 
dnl PHP_ARG_WITH(say_hello, for say_hello support, 
dnl Make sure that the comment is aligned: 
dnl [ --with-say_hello Include say_hello support]) 
dnl Otherwise use enable: 
dnl PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, 
dnl Make sure that the comment is aligned: 
dnl [ --enable-say_hello Enable say_hello support]) 
if test "$PHP_SAY_HELLO" != "no"; then 
dnl Write more examples of tests here... 
dnl # --with-say_hello -> check with-path 
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 
dnl SEARCH_FOR="/include/say_hello.h" # you most likely want to change this 
dnl if test -r $PHP_SAY_HELLO/$SEARCH_FOR; then # path given as parameter 
dnl SAY_HELLO_DIR=$PHP_SAY_HELLO 
dnl else # search default path list 
dnl AC_MSG_CHECKING([for say_hello files in default path]) 
dnl for i in $SEARCH_PATH ; do 
dnl if test -r $i/$SEARCH_FOR; then 
dnl SAY_HELLO_DIR=$i 
dnl AC_MSG_RESULT(found in $i) 
dnl fi 
dnl done 
dnl fi 
dnl 
dnl if test -z "$SAY_HELLO_DIR"; then 
dnl AC_MSG_RESULT([not found]) 
dnl AC_MSG_ERROR([Please reinstall the say_hello distribution]) 
dnl fi 
dnl # --with-say_hello -> add include path 
dnl PHP_ADD_INCLUDE($SAY_HELLO_DIR/include) 
dnl # --with-say_hello -> check for lib and symbol presence 
dnl LIBNAME=say_hello # you may want to change this 
dnl LIBSYMBOL=say_hello # you most likely want to change this 
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 
dnl [ 
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SAY_HELLO_DIR/lib, SAY_HELLO_SHARED_LIBADD) 
dnl AC_DEFINE(HAVE_SAY_HELLOLIB,1,[ ]) 
dnl ],[ 
dnl AC_MSG_ERROR([wrong say_hello lib version or lib not found]) 
dnl ],[ 
dnl -L$SAY_HELLO_DIR/lib -lm 
dnl ]) 
dnl 
dnl PHP_SUBST(SAY_HELLO_SHARED_LIBADD) 
PHP_NEW_EXTENSION(say_hello, say_hello.c, $ext_shared) 
fi


这个结构体可能看起来会让人有点头疼,不过我还是要解释一下里面的内容。因为这就是PHP Extension的原型,如果不搞清楚,就没法开发PHP Extension了。当然,我就不一一对每个字段进行解释了,只拣关键的、这篇文章会用到的字段说,因为许多字段并不需要我们手工填写,而是可以使用某些预定义的宏填充。

第7个字段“name”,这个字段是此PHP Extension的名字,在本例中就是“say_hello”。

第8个字段“functions”,这个将存放我们在此扩展中定义的函数的引用,具体结构不再分析,有兴趣的朋友可以阅读_zend_function_entry的源代码。具体编写代码时这里会有相应的宏。

第9-12个字段分别是四个函数指针,这四个函数会在相应时机被调用,分别是“扩展模块加载时”、“扩展模块卸载时”、“每个请求开始时”和“每个请求结束时”。这四个函数可以看成是一种拦截机制,主要用于相应时机的资源分配、释放等相关操作。

第13个字段“info_func”也是一个函数指针,这个指针指向的函数会在执行phpinfo()时被调用,用于显示自定义模块信息。

第14个字段“version”是模块的版本。

(关于zend_module_entry更详尽的介绍请参考这里)

介绍完以上字段,我们可以看看“say_hello.c”中自动生成的“say_hello_module_entry”框架代码了。

代码如下:

/* {{{ say_hello_module_entry 
*/ 
zend_module_entry say_hello_module_entry = { 
#if ZEND_MODULE_API_NO >= 20010901 
STANDARD_MODULE_HEADER, 
#endif 
"say_hello", 
say_hello_functions, 
PHP_MINIT(say_hello), 
PHP_MSHUTDOWN(say_hello), 
PHP_RINIT(say_hello), /* Replace with NULL if there's nothing to do at request start */ 
PHP_RSHUTDOWN(say_hello), /* Replace with NULL if there's nothing to do at request end */ 
PHP_MINFO(say_hello), 
#if ZEND_MODULE_API_NO >= 20010901 
"0.1", /* Replace with version number for your extension */ 
#endif 
STANDARD_MODULE_PROPERTIES 
}; 
/* }}} */


首先,宏“STANDARD_MODULE_HEADER”会生成前6个字段,“STANDARD_MODULE_PROPERTIES ”会生成“version”后的字段,所以现在我们还不用操心。而我们关心的几个字段,也都填写好或由宏生成好了,并且在“say_hello.c”的相应位置也生成了几个函数的框架。这里要注意,几个宏的参数均为“say_hello”,但这并不表示几个函数的名字全为“say_hello”,C语言中也不可能存在函数名重载机制。实际上,在开发PHP Extension的过程中,几乎处处都要用到Zend里预定义的各种宏,从全局变量到函数的定义甚至返回值,都不能按照“裸写”的方式来编写C语言,这是因为PHP的运行机制可能会导致命名冲突等问题,而这些宏会将函数等元素变换成一个内部名称,但这些对程序员都是透明的(除非你去阅读那些宏的代码),我们通过各种宏进行编程,而宏则为我们处理很多内部的东西。

写到这里,我们的任务就明了了:第一,如果需要在相应时机处理一些东西,那么需要填充各个拦截函数内容;第二,编写say_hello的功能函数,并将引用添加到say_hello_functions中。

编写phpinfo()回调函数

因为say_hello扩展在各个生命周期阶段并不需要做操作,所以我们只编写info_func的内容,上文说过,这个函数将在phpinfo()执行时被自动调用,用于显示扩展的信息。编写这个函数会用到四个函数:

php_info_print_table_start()——开始phpinfo表格。无参数。

php_info_print_table_header()——输出表格头。第一个参数是整形,指明头的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。

php_info_print_table_row()——输出表格内容。第一个参数是整形,指明这一行的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。

php_info_print_table_end()——结束phpinfo表格。无参数。


下面是“say_hello.c”中需要编写的info_func的具体代码:

代码如下:

/* {{{ PHP_MINFO_FUNCTION 
*/ 
PHP_MINFO_FUNCTION(say_hello) 
{ 
php_info_print_table_start(); 
php_info_print_table_header(2, "say_hello support", "enabled"); 
php_info_print_table_row(2, "author", "Zhang Yang"); /* Replace with your name */ 
php_info_print_table_end(); 
/* Remove comments if you have entries in php.ini 
DISPLAY_INI_ENTRIES(); 
*/ 
} 
/* }}} */


可以看到我们编写了两行内容、组件是否可用以及作者信息。

编写核心函数
编写核心函数,总共分为三步:1、使用宏PHP_FUNCTION定义函数体;2、使用宏ZEND_BEGIN_ARG_INFO和ZEND_END_ARG_INFO定义参数信息;3、使用宏PHP_FE将函数加入到say_hello_functions中。下面分步说明。

使用宏PHP_FUNCTION定义函数体

代码如下:

PHP_FUNCTION(say_hello_func) 
{ 
char *name; 
int name_len; 
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) 
{ 
return; 
} 
php_printf("Hello %s!", name); 
RETURN_TRUE; 
}


上文说过,编写PHP扩展时几乎所有东西都不能裸写,而是必须使用相应的宏。从上面代码可以清楚看到这一点。总体来说,核心函数代码一般由如下几部分构成:

定义函数,这一步通过宏PHP_FUNCTION实现,函数的外部名称就是宏后面括号里面的名称。

声明并定义局部变量。

解析参数,这一步通过zend_parse_parameters函数实现,这个函数的作用是从函数用户的输入栈中读取数据,然后转换成相应的函数参数填入变量以供后面核心功能代码使用。zend_parse_parameters的第一个参数是用户传入参数的个数,可以由宏“ZEND_NUM_ARGS() TSRMLS_CC”生成;第二个参数是一个字符串,其中每个字母代表一个变量类型,我们只有一个字符串型变量,所以第二个参数是“s”;最后各个参数需要一些必要的局部变量指针用于存储数据,下表给出了不同变量类型的字母代表及其所需要的局部变量指针。

代码如下:

const zend_function_entry say_hello_functions[] = { 
PHP_FE(say_hello_func, arginfo_say_hello_func) 
{NULL, NULL, NULL} 
};


这一步就是通过PHP_EF宏实现,注意这个数组最后一行必须是{NULL, NULL, NULL} ,请不要删除。

下面是编写完成后的say_hello.c全部代码:

代码如下:

/* 
+----------------------------------------------------------------------+ 
| PHP Version 5 | 
+----------------------------------------------------------------------+ 
| Copyright (c) 1997-2010 The PHP Group | 
+----------------------------------------------------------------------+ 
| This source file is subject to version 3.01 of the PHP license, | 
| that is bundled with this package in the file LICENSE, and is | 
| available through the world-wide-web at the following url: | 
| https://www.php1.cn/ | 
| If you did not receive a copy of the PHP license and are unable to | 
| obtain it through the world-wide-web, please send a note to | 
| license@php.net so we can mail you a copy immediately. | 
+----------------------------------------------------------------------+ 
| Author: | 
+----------------------------------------------------------------------+ 
*/ 
/* $Id: header 297205 2010-03-30 21:09:07Z johannes $ */ 
#ifdef HAVE_CONFIG_H 
#include "config.h" 
#endif 
#include "php.h" 
#include "php_ini.h" 
#include "ext/standard/info.h" 
#include "php_say_hello.h" 
/* If you declare any globals in php_say_hello.h uncomment this: 
ZEND_DECLARE_MODULE_GLOBALS(say_hello) 
*/ 
/* True global resources - no need for thread safety here */ 
static int le_say_hello; 
/* {{{ PHP_FUNCTION 
*/ 
PHP_FUNCTION(say_hello_func) 
{ 
char *name; 
int name_len; 
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) 
{ 
return; 
} 
php_printf("Hello %s!", name); 
RETURN_TRUE; 
} 
ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) 
ZEND_END_ARG_INFO() 
/* }}} */ 
/* {{{ say_hello_functions[] 
* 
* Every user visible function must have an entry in say_hello_functions[]. 
*/ 
const zend_function_entry say_hello_functions[] = { 
PHP_FE(say_hello_func, arginfo_say_hello_func) 
{NULL, NULL, NULL} /* Must be the last line in say_hello_functions[] */ 
}; 
/* }}} */ 
/* {{{ say_hello_module_entry 
*/ 
zend_module_entry say_hello_module_entry = { 
#if ZEND_MODULE_API_NO >= 20010901 
STANDARD_MODULE_HEADER, 
#endif 
"say_hello", 
say_hello_functions, 
NULL, 
NULL, 
NULL, 
NULL, 
PHP_MINFO(say_hello), 
#if ZEND_MODULE_API_NO >= 20010901 
"0.1", /* Replace with version number for your extension */ 
#endif 
STANDARD_MODULE_PROPERTIES 
}; 
/* }}} */ 
#ifdef COMPILE_DL_SAY_HELLO 
ZEND_GET_MODULE(say_hello) 
#endif 
/* {{{ PHP_MINFO_FUNCTION 
*/ 
PHP_MINFO_FUNCTION(say_hello) 
{ 
php_info_print_table_start(); 
php_info_print_table_header(2, "say_hello support", "enabled"); 
php_info_print_table_row(2, "author", "Zhang Yang"); /* Replace with your name */ 
php_info_print_table_end(); 
/* Remove comments if you have entries in php.ini 
DISPLAY_INI_ENTRIES(); 
*/ 
} 
/* }}} */


编译并安装扩展
在say_hello目录下输入下面命令:

代码如下:

/usr/bin/phpize 
./configure 
make 
make install


这样就完成了say_hello扩展的安装(如果没有报错的话)。

这时如果你去放置php扩展的目录下,会发现多了一个say_hello.so的文件。如下图所示:

代码如下:


执行这个脚本,结果如下:

本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载或演绎,但是必须保留本文的署名张洋(包含链接),且不得用于商业用途。如您有任何疑问或者授权方面的协商,请与我联系。

以上就是浅谈PHP Extension的开发——基础篇_php技巧的内容,更多相关内容请关注PHP中文网(www.php1.cn)!

推荐阅读
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • 本文讲述了作者从最初对软件工程的选择迷茫到逐渐喜欢并坚持学习的经历。作者在大学期间通过学习专业课和参与项目开发,不断挑战自己并取得成就感。虽然曾考虑过转专业和复读,但最终决定坚持学习软件工程,并为自己的未来努力奋斗。作者还提到了大学生活与自己最初的预期不同,但对此并没有太多抱怨。 ... [详细]
author-avatar
别被风景迷了眼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有