扩展作为php的重要组成部分,为php提供了更多的功能和特性,由于php是解释型语言,所以在性能上不如C,所以可以将一些消耗CPU的操作通过扩展的方式代替。
比如:
php扩展通过zend_module_entry这个结构来标识,这个结构是用于保存扩展的基本信息,包括扩展名,版本号,扩展提供的函数列表等。我们每开发一个新扩展,都需要定义一个此结构的变量:
struct _zend_module_entry {
unsigned short size; // 此结构体的大小,即sizeof(struct_zend_module_entry)
unsigned int zend_api;
unsigned char zend_debug; // 是否开启debug模式
unsigned char zts; //线程是否安全
const struct _zend_ini_entry *ini_entry; // php.ini相关
const struct _zend_module_dep *deps;
const char *name; // 扩展名称,不得与其他扩展名相同
const struct _zend_function_entry *functions; // 扩展提供的内部函数列表
int (*module_startup_func)(INIT_FUNC_ARGS); // 模块初始化阶段回调的钩子函数
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; // 扩展的版本号
size_t globals_size;
void* globals_ptr;
void (*globals_ctor)(void *global);
void (*globals_dtor)(void *global);
int (*post_deactivate_func)(void);
int module_started;
unsigned char type;
void *handle;
int module_number;
const char *build_id;
};
php提供了几个用于简化扩展开发的脚本工具:ext_skel,phpize,php-config,这几个工具主要用于扩展的生成和编译:
ext_skel工具位于php源码的ext/目录下,可以用来生成一个扩展的基本骨架,帮助开发者快速的生成一个规范的扩展结构,通过ext_skel --help可以看到,我们可以用以下命令生成一个扩展结构:
假如我们想实现一个名字叫mytest的函数扩展:
$ ./ext_skel --extname=mytest
当我们执行上述命令,会在当前目录下生成几个文件,这里拿几个重要的来说明一下:
phpize我也还没有深入研究过,在这里我们只要知道它是用来生成扩展的configure文件就好了。
php-config在php的安装目录下的/bin目录下,按照php手册上的解释,php-config是一个命令行脚本,用于获取所安装的php配置的信息。
在编译扩展时,如果服务器上安装有多个PHP版本,可以在配置时用–with-php-config=[某一个php版本源码/bin/php-config]选项来指定使用哪一个版本编译,该选项指定了相对应的php-config脚本的路径。
假如你的服务器上,在/usr/local/php/目录下,分别有php-5.6.3和php-7.1.0两个版本,你想用php7来编译你的扩展,那么就可以这样做:
$ ./configure --with-php-config=/usr/local/php/php-7.1.0/bin/php-config
要编写一个扩展,除了生成扩展骨架,完善功能代码,测试通过之外,我们还要生成configure配置文件,编译扩展和安装扩展:
现在我们来实现一个类似于PHP内置函数中操作文件的系列函数:
PHP_FUNCTION(myfile_fopen) {
zend_string *filename;
zend_string *mode;
FILE *fp;
my_resource *myresource;
zend_resource *var_resource;
if(zend_parse_parameters(ZEND_NUM_ARGS(),"SS",&filename,&mode) == FAILURE) {
RETURN_FALSE;
}
fp = fopen(ZSTR_VAL(filename),ZSTR_VAL(mode));
if(fp == NULL) {
RETURN_FALSE;
}
myresource = emalloc(sizeof(my_resource));
myresource->file_name = filename;
myresource->mode = mode;
myresource->fp = fp;
var_resource = zend_register_resource(myresource,le_myfile);
RETURN_RES(var_resource);
}
PHP_FUNCTION(myfile_fread) {
long size;
char *result;
size_t bytes_read;
zval *res;
my_resource *myresource;
if(zend_parse_parameters(ZEND_NUM_ARGS(),"rl",&res,&size) == FAILURE) {
RETURN_FALSE;
}
myresource = Z_RES_VAL_P(res);
result = (char *) emalloc(size+1);
bytes_read = fread(result,sizeof(char),size,myresource->fp);
result[bytes_read] = '\0';
RETURN_STRING(result);
}
PHP_FUNCTION(myfile_fwrite) {
zval *res;
zend_string *content;
my_resource *myresource;
if(zend_parse_parameters(ZEND_NUM_ARGS(),"rS",&res,&content) == FAILURE) {
RETURN_FALSE;
}
myresource = Z_RES_VAL_P(res);
if(fwrite(ZSTR_VAL(content),sizeof(char),content->len,myresource->fp) != content->len) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(myfile_fclose) {
zval *res;
if(zend_parse_parameters(ZEND_NUM_ARGS(),"r",&res) == FAILURE) {
return;
}
if(zend_list_delete(Z_RES_VAL_P(res)) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
这里是扩展的函数列表:
const zend_function_entry myfile_functions[] = {
PHP_FE(confirm_myfile_compiled, NULL) /* For testing, remove later. */
PHP_FE(myfile_fopen,NULL)
PHP_FE(myfile_fread,NULL)
PHP_FE(myfile_fwrite,NULL)
PHP_FE(myfile_fclose,NULL)
PHP_FE_END /* Must be the last line in myfile_functions[] */
};
这里是该扩展的结构信息:
zend_module_entry myfile_module_entry = {
STANDARD_MODULE_HEADER,
"myfile",
myfile_functions,
PHP_MINIT(myfile),
PHP_MSHUTDOWN(myfile),
PHP_RINIT(myfile), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(myfile), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(myfile),
PHP_MYFILE_VERSION,
STANDARD_MODULE_PROPERTIES
};
这里只是部分功能代码,完整的代码请参照我的github地址:https://github.com/yangclsir/myfile