扩展是PHP的重要组成部分,它是PHP提供给开发者用于扩展PHP语言功能的主要方式。开发者可以用C/C++定义自己的功能,通过扩展嵌入到PHP中,灵活的扩展能力使得PHP拥有了大量、丰富的第三方组件,这些扩展很好的补充了PHP的功能、特性,使得PHP在web开发中得以大展身手。ext目录下有一个standard扩展,这个扩展提供了大量被大家所熟知的PHP函数:sleep()、usleep()、htmlspecialchars()、md5()、strtoupper()、substr()、array_merge()等等。
C语言是PHP之母,作为世界上非常优秀的一门语言,自它诞生至今,C语言早就了大量优秀、知名的项目:Linux、Nginx、MySQL、PHP、Redis、Memcached等等,感谢里奇带给这个世界如此伟大的一份礼物。C语言的优秀也折射到PHP身上,但是PHP内核提供的功能终究有限,如果你发现PHP在某些方面已经满足不了你的需求了,那么不妨试试扩展。
常见的,扩展可以在以下几个方面有所作为:
- 介入PHP的编译、执行阶段: 可以介入PHP框架执行的那5个阶段,比如opcache,就是重定义了编译函数
- 提供内部函数: 可以定义内部函数扩充PHP的函数功能,比如array、date等操作
- 提供内部类
- 实现RPC客户端: 实现与外部服务的交互,比如redis、mysql等
- 提升执行性能: PHP是解析型语言,在性能方面远不及C语言,可以将耗cpu的操作以C语言代替
- ......
当然扩展也不是万能,它只允许我们在PHP提供的框架之上进行一些特定的处理,同时限于SAPI的差异,扩展也必须要考虑到不同SAPI的实现特点。
PHP中的扩展分为两类:PHP扩展、Zend扩展,对内核而言这两个分别称之为:模块(module)、扩展(extension),本章主要介绍是PHP扩展,也就是模块。
扩展首先需要创建一个zend_module_entry
结构,这个变量必须是全局变量,且变量名必须是:扩展名称_module_entry
,内核通过这个结构得到这个扩展都提供了哪些功能,换句话说,一个扩展可以只包含一个zend_module_entry
结构,相当于定义了一个什么功能都没有的扩展。
//zend_modules.h
struct _zend_module_entry {
unsigned short size; //sizeof(zend_module_entry)
unsigned int zend_api; //ZEND_MODULE_API_NO
unsigned char zend_debug; //是否开启debug
unsigned char zts; //是否开启线程安全
const struct _zend_ini_entry *ini_entry;
const struct _zend_module_dep *deps;
const char *name; //扩展名称,不能重复
const struct _zend_function_entry *functions; //扩展提供的内部函数列表
int (*module_startup_func)(INIT_FUNC_ARGS); //扩展初始化回调函数,PHP_MINIT_FUNCTION或ZEND_MINIT_FUNCTION定义的函数
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); //扩展关闭时回调函数
int (*request_startup_func)(INIT_FUNC_ARGS); //请求开始前回调函数
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); //请求结束时回调函数
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); //php_info展示的扩展信息处理函数
const char *version; //版本
...
unsigned char type;
void *handle;
int module_number; //扩展的唯一编号
const char *build_id;
};
这个结构包含很多成员,但并不是所有的都需要自己定义,经常用到的主要有下面几个:
- name: 扩展名称,不能重复
- functions: 扩展定义的内部函数entry
- module_startup_func: PHP在模块初始化时回调的hook函数,可以使扩展介入module startup阶段
- module_shutdown_func: 在模块关闭阶段回调的函数
- request_startup_func: 在请求初始化阶段回调的函数
- request_shutdown_func: 在请求结束阶段回调的函数
- info_func: php_info()函数时调用,用于展示一些配置、运行信息
- version: 扩展版本
除了上面这些需要手动设置的成员,其它部分可以通过STANDARD_MODULE_HEADER
、STANDARD_MODULE_PROPERTIES
宏统一设置,扩展提供的内部函数及四个执行阶段的钩子函数是扩展最常用到的部分,几乎所有的扩展都是基于这两部分实现的。有了这个结构还需要提供一个接口来获取这个结构变量,这个接口是统一的,扩展中通过ZEND_GET_MODULE(extension_name)
完成这个接口的定义:
//zend_API.h
#define ZEND_GET_MODULE(name) \
BEGIN_EXTERN_C()\
ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\
END_EXTERN_C()
展开后可以看到,实际就是定义了一个get_module()函数,返回扩展zend_module_entry结构的地址,这就是为什么这个结构的变量名必须是扩展名称_module_entry
这种格式的原因。
有了扩展的zend_module_entry结构以及获取这个结构的接口一个合格的扩展就编写完成了,只是这个扩展目前还什么都干不了:
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
zend_module_entry mytest_module_entry = {
STANDARD_MODULE_HEADER,
"mytest",
NULL, //mytest_functions,
NULL, //PHP_MINIT(mytest),
NULL, //PHP_MSHUTDOWN(mytest),
NULL, //PHP_RINIT(mytest),
NULL, //PHP_RSHUTDOWN(mytest),
NULL, //PHP_MINFO(mytest),
"1.0.0",
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(mytest)
编译、安装后执行php -m
就可以看到my_test这个扩展了。