未加星标

PHP7扩展开发教程[7] 如何创建对象?

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏
请确定你已阅读《 php7扩展开发教程[6] 如何调用PHP函数? 》。 小憩一下

近些天连续的编写教程倍感疲惫,好在重要的内容已经讲过去大半,剩下的内容会陆陆续续写完。

在我的计划之中,整个教程应该在10篇左右,从本章开始的剩余的内容将会简单的多,希望可以尽快完成,呈现一个完整的教程给大家。

遥想从前,对于PHP扩展开发也是敬而远之的态度,主要是因为没有相关联的工作需求,后来是因为工作中需要真正用到扩展开发,所以才懵懵懂懂的走上了这条路。

虽然坎坎坷坷开发完了扩展,实际上对Zend API的认识仍旧非常模糊,以至于写出来的扩展只是”能用”而已。好在我本身对这个技术很有兴趣,同时也正是因为网上关于PHP扩展开发的博客少之又少,遇到问题几乎无法解决,只能求助源码,更别提PHP7已经大变样了。

正是如此,所以我决定从头学起,也就有了这篇教程。

正式开始

本章代码: https://github.com/owenliang/php7-extension-explore/tree/master/course7-how-to-create-object 。

我的目标是在一个全局函数中,调用类的静态方法,然后创建类对象并调用它的非静态方法,让我们开始吧。

首先,我为myext类增加了一个static方法叫做print_author,它将输出一段信息:

zend_function_entry funcs[] = { // fname,handler,arg_info,,num_args,flags {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, {NULL, NULL, NULL, 0, 0}, };

可以看一下这个方法的实现zim_myext_print_author,非常简单:

// Myext's static method void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { TRACE("zim_myext_print_author"); php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); ZVAL_BOOL(return_value, 1); }

这里需要关注的是php_output_write函数,它相当于PHP里的echo方法,相关定义如下:

PHPAPI size_t php_output_write_unbuffered(const char *str, size_t len) PHPAPI size_t php_output_write(const char *str, size_t len)

一般我们用第2个就可以,因为带缓存的输出性能会更好点。

接下来,我定义一个全局函数zif_myext_test_object,在导出扩展时注册它:

zend_function_entry global_funcs[] = { // fname,handler,arg_info,,num_args,flags {"test_object", zif_myext_test_object, NULL, 0, 0}, {NULL, NULL, NULL, 0, 0}, }; zend_module_entry module = { STANDARD_MODULE_HEADER_EX,// size,zend_api,zend_debug,zts NULL, // ini_entry NULL, // deps "myext",//name global_funcs, // functions extension_startup,// module_startup_func extension_shutdown, // module_shutdown_func extension_before_request, // request_startup_func extension_after_request,// request_shutdown_func extension_info, // info_func "1.0",// version // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, // handle,module_number,build_id STANDARD_MODULE_PROPERTIES, };

现在,我们具体来看一下这个全局函数的实现,代码将分段讲解。

// global function void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { TRACE("zif_myext_test_object"); // call myext's static method: print_author zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); zend_class_entry *myext_handle = zend_lookup_class(myext_classname); zend_string_release(myext_classname); assert(myext_handle == myext_class_handle);

首先,我创建了一个string类型的zval,其值是类名myext。将类名传给zend_lookup_class方法,可以返回对应类的class句柄。通过断言我可以确定,zend_lookup_class返回的class句柄,与我注册myext类得到的class句柄是同一个对象。最后释放myext_classname的内存,因为不再使用。

在这里我们完全可以直接使用注册myext类时返回的class句柄,然而如果该类不是我们自己注册的类,那么就只能通过zend_lookup_class去获得了。

有了class句柄,接下来我要调用myext的静态方法print_author,因此我开始准备zend_call_function的第1个调用参数:

zval retval; zend_fcall_info fci = { size: sizeof(zend_fcall_info), retval: &retval, params: NULL, object: NULL, no_separation: 1, param_count: 0, }; ZVAL_UNDEF(&fci.function_name); zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1);

与上一章相比,object设置为了NULL,因为我们调用的是静态方法并没有对象,其他字段并没有什么区别。

接下来,我在myext的句柄的function_table里找出print_author方法,它底层数据是一个zend_function,前一章我们已经见过了。

zend_fcall_info_cache fcic = { initialized: 1, function_handler: print_author_func->value.func, calling_scope: myext_handle, called_scope: NULL, object: NULL, }; assert(zend_call_function(&fci, &fcic) == SUCCESS); assert(Z_TYPE_P(&retval) == IS_TRUE);

接下来,我们定义第2个参数,function_handler指向print_author方法。calling_scope代表被调用函数所属的class句柄,所以填myext_handle。called_scope表示当前所处的类,我们不在类内部,所以传NULL。最后的object填NULL就不必多说了。

现在调用zend_call_function可以成功调用到myext类的静态方法print_author,打印出一段话在屏幕上,断言确认了返回值是True类型。

现在,我们尝试创建一个myext对象:

// new a myext object zval myext_obj; assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS);

这里主要用到了object_init_ex方法,它第1个参数是一个未经初始化的zval,第2个参数是要创建的类对应的class句柄。

我们也可以像PHP里new Object()一样,创建一个默认类型的对象,其方法名是object_init,相关定义如下:

ZEND_API int _object_init(zval *arg ZEND_FILE_LINE_DC) ZEND_API int _object_init_ex(zval *arg, zend_class_entry *class_type ZEND_FILE_LINE_DC)

在创建对象的时候,Zend并不会帮我们调用构造函数,需要我们自己显式的在object上调用__construct方法:

// call object's __construct zval ctor_name; zval ctor_retval; ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); zval_ptr_dtor(&ctor_name);

先创建函数名ctor_name,然后通过便捷函数call_user_function调用即可。

类似的,接下来调用一下obj的strtolower方法,整个过程我们已经很熟悉了:

// call object's method zval func_name; ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); zval param; ZVAL_STR(&param, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); zval retval2; assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, &param) == SUCCESS); TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); zval_ptr_dtor(&func_name); zval_ptr_dtor(&param); zval_ptr_dtor(&retval2);

先创建函数名func_name,再准备调用参数param以及返回值容器retval2,最后调用call_user_function完成调用。最后不要忘记释放资源,函数名,参数,返回值。

到这里还没有结束!如果你的object不再使用,也请释放它:

// free object zval_ptr_dtor(&myext_obj); 结语

本章你应该掌握:

获取任意类的class句柄。 调用类的静态方法。 创建类对象。

下一章,我将展示如何定义全局常量,以及获取全局变量($_GET,$_POST)。

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

主题: PHPUBSUUCDUTI数据博客需求常模
tags: myext,zend,amp,NULL,object,function,author,class,zval,print,ZEND,name,调用,func,call
分页:12
转载请注明
本文标题:PHP7扩展开发教程[7] 如何创建对象?
本站链接:http://www.codesec.net/view/561291.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(45)