php扩展开发(一)自己写一个类
平时自己用php写一个类包是非常容易的,直接 $zhangleiinstance = new Abelappplication();
但是我们如何用c语言在php中写一个函数和类呢
1.下载php源码包,然后在php的源码包目录的ext下面创建框架骨架
./ext_skel --extname=helloworld
2.修改config.m4
##动态编译选项,通过.so的方式链接,去掉dnl注释:
PHP_ARG_WITH(helloworld, for helloworld support,
Make sure that the comment is aligned:
[ --with-helloworld Include helloworld support])
##静态编译选项,通过enable来启用,去掉dnl注释:
PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,
Make sure that the comment is aligned:
[ --enable-helloworld Enable helloworld support])
3.phpize生成解析文件(我是yum装的所以不需要指定路径)
phpize
./configure && make &&make install
4.在头文件中声明自己的函数和自己的类
声明函数用PHP_FUNCTION宏
声明类包用PHP_METHOD宏、
我在phpabelapplication.h中声明自己的函数和类以及方法
PHP_FUNCTION(helloworld);
PHP_METHOD(Abel_application,__construct);
PHP_METHOD(Abel_application,__destruct);
PHP_METHOD(Abel_application,ourMethod1);
PHP_METHOD(Abel_application,ourMrthod2);
5.声明要接收的参数
ZEND_BEGIN_ARG_INFO_EX(arg_abel_info,0,0,2)
ZEND_ARG_INFO(0,name)
ZEND_END_ARG_INFO()
解释:
ZEND_ARG_INFO 声明普通参数
ZEND_ARG_OBJ_INFO 声明对象类型的参数
ZEND_ARG_ARRAY_INFO 声明数组类型的参数
ZEND_ARG_PASS_INFO(pass_by_ref) pass_by_ref为1时,强制设置后续的参数为引用类型
6.在栈上声明一个函数列表的数组(这一步用于注册)
const zend_function_entry abel_function[] = {
PHP_FE(helloworld,NULL)
PHP_ME(Abel_application,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(Abel_application,__destruct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
PHP_ME(Abel_application,ourMethod1,arg_abel_info,ZEND_ACC_PUBLIC)
PHP_ME(Abel_application,ourMethod2,arg_abel_info,ZEND_ACC_PUBLIC)
PHP_FE_END
};
7.实现方法体
PHP_FUNCTION(helloworld)
{
php_printf("Hello Ligang,this is the first extension!\n");
RETURN_TRUE;
}
PHP_METHOD(Abel_application,__construct)
{
php_printf("hello,zhang lei this is your first construct extension");
};
PHP_METHOD(Abel_application,__destruct)
{
php_printf("hello,zhang lei this is your first destrcut extension");
};
PHP_METHOD(Abel_application,ourMethod1)
{
};
PHP_METHOD(Abel_application,ourMethod2)
{
};
8.注册到SAPI初始化中
PHP_MINIT_FUNCTION(abel_application)
{
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/
zend_class_entry abel_entry;
INIT_CLASS_ENTRY(abel_entry,"abel_application",abel_function);
abel_ce = zend_register_internal_class_ex(&abel_entry,NULL);
zend_declare_property_null(abel_ce,ZEND_STRL("name"),ZEND_ACC_PUBLIC TSRMLS_CC);
return SUCCESS;
}
注意:
zend_module_entry abel_application_module_entry = {
STANDARD_MODULE_HEADER,
"abel_application",
abel_function,
PHP_MINIT(abel_application),
PHP_MSHUTDOWN(abel_application),
PHP_RINIT(abel_application),
PHP_RSHUTDOWN(abel_application),
PHP_MINFO(abel_application),
PHP_ABEL_APPLICATION_VERSION,
STANDARD_MODULE_PROPERTIES
};
要把module条目中的abel_function 的参数改为刚才声明的函数列表数组
tipi中是这样说的
这样的结构在PHP的源码中有多处使用, 比如在PHP扩展开发中,每个扩展都需要定义一个zendmoduleentry结构体。 这个结构体的作用与sapimodulestruct结构体类似,都是一个类似模板方法模式的应用。 在PHP的生命周期中如果需要调用某个扩展,其调用的方法都是zendmoduleentry结构体中指定的方法, 如在上一小节中提到的在执行各个扩展的请求初始化时,都是统一调用requeststartupfunc方法, 而在每个扩展的定义时,都通过宏PHPRINIT指定requeststartupfunc对应的函数。 以VLD扩展为例:其请求初始化为PHPRINIT(vld),与之对应在扩展中需要有这个函数的实现: PHPRINITFUNCTION(vld) { }
9.调用实例:
<?php
$a = new Abel_application();
$a->name=3;
$aa = $a->ourMethod1(1,2,3,4,5,6,7,8,9);
echo "\r\n";
var_dump($aa);
echo "\r\n";
//helloworld();
//var_dump($a);
再次回顾tipi中的声明周期
开始和结束
PHP开始执行以后会经过两个主要的阶段:处理请求之前的开始阶段和请求之后的结束阶段。 开始阶段有两个过程:第一个过程是模块初始化阶段(MINIT), 在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序整个执行过程中), 该过程只进行一次。第二个过程是模块激活阶段(RINIT),该过程发生在请求阶段, 例如通过url请求某个页面,则在每次请求之前都会进行模块激活(RINIT请求开始)。 例如PHP注册了一些扩展模块,则在MINIT阶段会回调所有模块的MINIT函数。 模块在这个阶段可以进行一些初始化工作,例如注册常量,定义模块使用的类等等。 模块在实现时可以通过如下宏来实现这些回调函数:
PHPMINITFUNCTION(myphpextension) { // 注册常量或者类等初始化操作 return SUCCESS; } 请求到达之后PHP初始化执行脚本的基本环境,例如创建一个执行环境,包括保存PHP运行过程中变量名称和值内容的符号表, 以及当前所有的函数以及类等信息的符号表。然后PHP会调用所有模块的RINIT函数, 在这个阶段各个模块也可以执行一些相关的操作,模块的RINIT函数和MINIT回调函数类似:
PHPRINITFUNCTION(myphpextension) { // 例如记录请求开始时间 // 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了 return SUCCESS; } 请求处理完后就进入了结束阶段,一般脚本执行到末尾或者通过调用exit()或die()函数, PHP都将进入结束阶段。和开始阶段对应,结束阶段也分为两个环节,一个在请求结束后停用模块(RSHUTDOWN,对应RINIT), 一个在SAPI生命周期结束(Web服务器退出或者命令行脚本执行完毕退出)时关闭模块(MSHUTDOWN,对应MINIT)。
PHPRSHUTDOWNFUNCTION(myphpextension) { // 例如记录请求结束时间,并把相应的信息写入到日至文件中。 return SUCCESS; }