PHP扩展开发之函数与类的实现
运行环境
- PHP Version 7.0.0-dev
- GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
前言:
我们知道函数包含函数申明,函数定义,函数申请又包括函数名以及函数参数,函数返回值。而类也有申明与定义。我们就按照这个思路来了解函数与类的实现。
目录:
- 函数名与函数参数
- 函数的定义
- 函数的注册
- 脚本调用函数
- 注册全局常量
- 类名与类的属性
- 类的方法注册
- 脚本调用类
函数名与函数参数
函数的申明是PHP_FUNCTION宏实现的,我们展开这个宏。
#define PHP_FUNCTION ZEND_FUNCTION
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value
#define ZEND_FN(name) zif_##name
即 PHP_FUNCTION(function_name) == void zif_function_name(zend_execute_data *execute_data, zval *return_value)
所以我们申明的都是返回void有两个参数的函数。
第一个参数是zend_execute_data类型的
typedef struct _zend_execute_data zend_execute_data;
struct _zend_execute_data {
const zend_op *opline; /* executed opline */
zend_execute_data *call; /* current call */
zval *return_value;
zend_function *func; /* executed op_array */
zval This;
#if ZEND_EX_USE_RUN_TIME_CACHE
void **run_time_cache;
#endif
#if ZEND_EX_USE_LITERALS
zval *literals;
#endif
zend_class_entry *called_scope;
zend_execute_data *prev_execute_data;
zend_array *symbol_table;
};
第二个参数是zval类型的,实际上正是我们自定义函数的返回值。
函数参数
我们的php自定义函数的参数又是怎么来的呢?通过PHP_FE(mysqli_affected_rows, arginfo_mysqli_only_link)可以推出,第二个参数正是我们的参数实现。
mysqli_fe.c
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_autocommit, 0, 0, 2)
MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, NULL, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, #classname, IS_OBJECT, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_ARRAY, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, NULL, type_hint, pass_by_ref, allow_null, 0 },
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 1 },
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
static const zend_internal_arg_info name[] = { \
{ (const char*)(zend_uintptr_t)(required_num_args), NULL, 0, return_reference, 0, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
#define MYSQLI_ZEND_ARG_OBJ_INFO_LINK() ZEND_ARG_INFO(0, link)
展开上面的宏
static const zend_internal_arg_info arginfo_mysqli_autocommit[] = {
{ (const char*)(zend_uintptr_t)(2), NULL, 0, 0, 0, 0 },
{ "link", NULL, 0, 0, 0, 0 },
{ "mode", NULL, 0, 0, 0, 0 },
};
zend_internal_arg_info的定义如下
/* arg_info for internal functions */
typedef struct _zend_internal_arg_info {
const char *name; /*参数的名称 或者 个数*/
const char *class_name;/*类的名称*/
zend_uchar type_hint;/*参数类型*/
zend_uchar pass_by_reference;/*是否引用*/
zend_bool allow_null;/*是否允许空*/
zend_bool is_variadic;/*是否可变参数*/
} zend_internal_arg_info;
其中type_hint的在上面都是0,其函数如下
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
/* constant expressions */
#define IS_CONSTANT 11
#define IS_CONSTANT_AST 12
/* fake types */
#define _IS_BOOL 13
#define IS_CALLABLE 14
/* internal types */
#define IS_INDIRECT 15
#define IS_PTR 17
0即是IS_UNDEF,未定义。
函数的定义
mysqli_api.c
/* { { { proto void mysqli_debug(string debug) U
*/
PHP_FUNCTION(mysqli_debug)
{
char *debug;
size_t debug_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &debug, &debug_len) == FAILURE) {
return;
}
mysql_debug(debug);
RETURN_TRUE;
}
/* }}} */
/* { { { proto bool mysqli_autocommit(object link, bool mode)
Turn auto commit on or of */
PHP_FUNCTION(mysqli_autocommit)
{
MY_MYSQL *mysql;
zval *mysql_link;
zend_bool automode;
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ob", &mysql_link, mysqli_link_class_entry, &automode) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
if (mysql_autocommit(mysql->mysql, (my_bool)automode)) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
其中最常用到的函数就是zend_parse_parameters和zend_parse_method_parameters了
ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...);
ZEND_API int zend_parse_method_parameters(int num_args, zval *this_ptr, const char *type_spec, ...);
num_args 参数的个数
this_ptr this指针
type_spec 参数的格式化字符串
... 接受参数的变量地址
在zend_parse_va_args进行预处理
static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, int flags) /* { { { */
{
for (spec_walk = type_spec; *spec_walk; spec_walk++) {
c = *spec_walk;
switch (c) {
case 'l': case 'd':
case 's': case 'b':
case 'r': case 'a':
case 'o': case 'O':
case 'z': case 'Z':
case 'C': case 'h':
case 'f': case 'A':
case 'H': case 'p':
case 'S': case 'P':
case 'L':
max_num_args++;
break;
case '|':
min_num_args = max_num_args;
break;
case '/':
case '!':
/* Pass */
break;
case '*':
case '+':
break;
default:
return FAILURE;
}
}
}
实际的参数的解析是由zend_parse_arg_impl进行的,对解析进行整理可以得出下表。
| 在它之前的变量都是必须的,后面的都有默认值。
! 将!占为变量置为0(即NULL),连续的多个!只为一个变量占位。
/ 如果传递过来的变量与别的变量共用一个zval,而且不是引用,则进行强制分离。
多个连续的/只为一个变量占位。(注:1)
l、L zend_long * integer/long
d double *p float/double
s char **,size_t * string
p char **,size_t * a valid path
P(大写)zend_string ** a valid path
S zend_string ** string
b zend_bool *p boolean
r zval ** resource
A、a zval ** array
H、h HashTable ** array
o zval ** object
O zval **,zend_class_entry *
C zend_class_entry **
f zend_fcall_info *,zend_fcall_info_cache * valid callback
z zval ** everything
Z 'Z' iz not supported anymore and should be replaced with 'z'
其中s,pO,f都是一个占位符接受两个变量,其余都是一个。
那么 ZEND_NUM_ARGS()又是什么呢?
#define ZEND_NUM_ARGS() EX_NUM_ARGS()
#define EX_NUM_ARGS() ZEND_CALL_NUM_ARGS(execute_data)
#define ZEND_CALL_NUM_ARGS(call) \
(call)->This.u2.num_args
即
ZEND_NUM_ARGS() == (execute_data)->This.u2.num_args
那么脚本中用户传入的参数是怎么传递给我们的C函数的呢?
查看zend_parse_va_args函数我们能找到下面的代码
arg = ZEND_CALL_ARG(EG(current_execute_data), i + 1);
if (zend_parse_arg(i+1, arg, va, &type_spec, quiet) == FAILURE) {
/* clean up varargs array if it was used */
if (varargs && *varargs) {
*varargs = NULL;
}
return FAILURE;
}
其中
# define EG(v) (executor_globals.v)
即在executor_globals.current_execute_data中找到第一个参数的位置,从而使用可变参数把php脚本中的值填充到C函数中的变量中去。
函数的注册
我们在mysqli_fe.c中可以找到函数注册的代码
const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_affected_rows,arginfo_mysqli_only_link)
//中间的省略
PHP_FE_END
};
#define PHP_FE ZEND_FE
#define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },
#define ZEND_FN(name) zif_##name
#define PHP_FE_END ZEND_FE_END
#define ZEND_FE_END { NULL, NULL, NULL, 0, 0 }
展开即:
const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_affected_rows,arginfo_mysqli_only_link)
{"mysqli_affected_rows",zif_mysqli_affected_rows,
arginfo_mysqli_only_link,参数个数,0},
//中间的省略
{ NULL, NULL, NULL, 0, 0 }
};
typedef struct _zend_function_entry {
const char *fname;
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
const struct _zend_internal_arg_info *arg_info;
uint32_t num_args;
uint32_t flags;
} zend_function_entry;
在分析cli执行过程的时候,我们知道循环注册函数判断条件是函数名是否为空。
那么这些我们自定义的函数是怎么注册到Zend的全局函数中去的呢?
在前面我们的php cli执行过程中,我们知道,每一个扩展都会有一个zend_module_entry全局变量,而这个变量的名称正是 扩展名_module_entry
php_mysqli.h
extern zend_module_entry mysqli_module_entry;
mysqli.c
zend_module_entry mysqli_module_entry = {
#if ZEND_MODULE_API_NO >= 20050922
STANDARD_MODULE_HEADER_EX, NULL,
mysqli_deps,
#elif ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"mysqli",
mysqli_functions,
PHP_MINIT(mysqli),
PHP_MSHUTDOWN(mysqli),
PHP_RINIT(mysqli),
PHP_RSHUTDOWN(mysqli),
PHP_MINFO(mysqli),
"0.1", /* Replace with version number for your extension */
PHP_MODULE_GLOBALS(mysqli),
PHP_GINIT(mysqli),
NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
mysqli_fe.c
const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_affected_rows, arginfo_mysqli_only_link)
PHP_FE_END
};
经过上面的分析,我们得出全局函数注册是通过"扩展名_module_entry"中的functions字段进行注册的。
脚本调用函数
那么脚本是怎么调用到我们的函数的呢?比如我们的扩展中有个函数test_php_server(),在Zend引擎解析php脚本时,发现函数名是test_php_server,那么它就会去compiler_globals.function_table中去找我们注册的C函数,并且按照要求给C函数填充参数的值,并且调用C函数。从而调用php的函数,实现了调用C函数。
zend_API.c:1950:zend_register_functions
if (!target_function_table) {
target_function_table = CG(function_table);
}
# define CG(v) (compiler_globals.v)
注册全局常量
PHP_MINIT_FUNCTION(mysqli){
//....
/* mysqli_options */
REGISTER_LONG_CONSTANT("MYSQLI_READ_DEFAULT_GROUP",
MYSQL_READ_DEFAULT_GROUP, CONST_CS | CONST_PERSISTENT);
}
zend_constants.h
#define REGISTER_NULL_CONSTANT(name, flags) zend_register_null_constant((name), sizeof(name)-1, (flags), module_number)
#define REGISTER_BOOL_CONSTANT(name, bval, flags) zend_register_bool_constant((name), sizeof(name)-1, (bval), (flags), module_number)
#define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name)-1, (lval), (flags), module_number)
#define REGISTER_DOUBLE_CONSTANT(name, dval, flags) zend_register_double_constant((name), sizeof(name)-1, (dval), (flags), module_number)
#define REGISTER_STRING_CONSTANT(name, str, flags) zend_register_string_constant((name), sizeof(name)-1, (str), (flags), module_number)
#define REGISTER_STRINGL_CONSTANT(name, str, len, flags) zend_register_stringl_constant((name), sizeof(name)-1, (str), (len), (flags), module_number)
#define REGISTER_NS_NULL_CONSTANT(ns, name, flags) zend_register_null_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (flags), module_number)
#define REGISTER_NS_BOOL_CONSTANT(ns, name, bval, flags) zend_register_bool_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (bval), (flags), module_number)
#define REGISTER_NS_LONG_CONSTANT(ns, name, lval, flags) zend_register_long_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (lval), (flags), module_number)
#define REGISTER_NS_DOUBLE_CONSTANT(ns, name, dval, flags) zend_register_double_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (dval), (flags), module_number)
#define REGISTER_NS_STRING_CONSTANT(ns, name, str, flags) zend_register_string_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (str), (flags), module_number)
#define REGISTER_NS_STRINGL_CONSTANT(ns, name, str, len, flags) zend_register_stringl_constant(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, (str), (len), (flags), module_number)
#define REGISTER_MAIN_NULL_CONSTANT(name, flags) zend_register_null_constant((name), sizeof(name)-1, (flags), 0)
#define REGISTER_MAIN_BOOL_CONSTANT(name, bval, flags) zend_register_bool_constant((name), sizeof(name)-1, (bval), (flags), 0)
#define REGISTER_MAIN_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name)-1, (lval), (flags), 0)
#define REGISTER_MAIN_DOUBLE_CONSTANT(name, dval, flags) zend_register_double_constant((name), sizeof(name)-1, (dval), (flags), 0)
#define REGISTER_MAIN_STRING_CONSTANT(name, str, flags) zend_register_string_constant((name), sizeof(name)-1, (str), (flags), 0)
#define REGISTER_MAIN_STRINGL_CONSTANT(name, str, len, flags) zend_register_stringl_constant((name), sizeof(name)-1, (str), (len), (flags), 0)
其中注册的常量是一个zend_constant结构体
typedef struct _zend_constant {
zval value;
zend_string *name;
int flags;
int module_number;
} zend_constant;
zend_register_xxx_constant会调用zend_register_constant函数,进而将常量注册到 EG(zend_constants)即executor_globals.zend_constants中
# define EG(v) (executor_globals.v)
类名与类的属性
我们可以在PHP_MINIT_FUNCTION中找到注册类的的代码
pdo.c
PHP_MINIT_FUNCTION(pdo)
{
pdo_stmt_init();
return SUCCESS;
}
pdo_stmt.c
void pdo_stmt_init(void)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
pdo_dbstmt_ce = zend_register_internal_class(&ce);
pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_traversable);
zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC);
//...
}
首先调用INIT_CLASS_ENTRY初始化zend_class_entry结构体,然后调用zend_register_internal_class将类注册到内核中。
由于内容太多就不进行展开,但是我们看一下zend_class_entry结构体,最终
ce.name = "PDOStatement";
ce.info.internal.builtin_functions = pdo_dbstmt_functions;
zend_class_entry结构体
struct _zend_class_entry {
char type;
zend_string *name;
struct _zend_class_entry *parent;
int refcount;
uint32_t ce_flags;
int default_properties_count;
int default_static_members_count;
zval *default_properties_table;
zval *default_static_members_table;
zval *static_members_table;
HashTable function_table;
HashTable properties_info;
HashTable constants_table;
union _zend_function *constructor;
union _zend_function *destructor;
union _zend_function *clone;
union _zend_function *__get;
union _zend_function *__set;
union _zend_function *__unset;
union _zend_function *__isset;
union _zend_function *__call;
union _zend_function *__callstatic;
union _zend_function *__tostring;
union _zend_function *__debugInfo;
union _zend_function *serialize_func;
union _zend_function *unserialize_func;
zend_class_iterator_funcs iterator_funcs;
/* handlers */
zend_object* (*create_object)(zend_class_entry *class_type);
zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref);
int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type); /* a class implements this interface */
union _zend_function *(*get_static_method)(zend_class_entry *ce, zend_string* method);
/* serializer callbacks */
int (*serialize)(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
int (*unserialize)(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);
uint32_t num_interfaces;
uint32_t num_traits;
zend_class_entry **interfaces;
zend_class_entry **traits;
zend_trait_alias **trait_aliases;
zend_trait_precedence **trait_precedences;
union {
struct {
zend_string *filename;
uint32_t line_start;
uint32_t line_end;
zend_string *doc_comment;
} user;
struct {
const struct _zend_function_entry *builtin_functions;
struct _zend_module_entry *module;
} internal;
} info;
};
然后调用注册函数,最后执行以下代码,将类加入到compiler_globals.class_table中
zend_hash_update_ptr(CG(class_table), lowercase_name, class_entry);
ce.ce_flags可以设置类的可见性,它可以有以下的值。
/* method flags (types) */
#define ZEND_ACC_STATIC 0x01
#define ZEND_ACC_ABSTRACT 0x02
#define ZEND_ACC_FINAL 0x04
#define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08
/* class flags (types) */
/* ZEND_ACC_IMPLICIT_ABSTRACT_CLASS is used for abstract classes (since it is set by any abstract method even interfaces MAY have it set, too). */
/* ZEND_ACC_EXPLICIT_ABSTRACT_CLASS denotes that a class was explicitly defined as abstract by using the keyword. */
#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20
#define ZEND_ACC_INTERFACE 0x80
#define ZEND_ACC_TRAIT 0x120
/* method flags (visibility) */
/* The order of those must be kept - public < protected < private */
#define ZEND_ACC_PUBLIC 0x100
#define ZEND_ACC_PROTECTED 0x200
#define ZEND_ACC_PRIVATE 0x400
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
#define ZEND_ACC_CHANGED 0x800
#define ZEND_ACC_IMPLICIT_PUBLIC 0x1000
/* method flags (special method detection) */
#define ZEND_ACC_CTOR 0x2000
#define ZEND_ACC_DTOR 0x4000
#define ZEND_ACC_CLONE 0x8000
/* method flag (bc only), any method that has this flag can be used statically and non statically. */
#define ZEND_ACC_ALLOW_STATIC 0x10000
/* shadow of parent's private method/property */
#define ZEND_ACC_SHADOW 0x20000
/* deprecation flag */
#define ZEND_ACC_DEPRECATED 0x40000
/* class implement interface(s) flag */
#define ZEND_ACC_IMPLEMENT_INTERFACES 0x80000
#define ZEND_ACC_IMPLEMENT_TRAITS 0x400000
/* class constants updated */
#define ZEND_ACC_CONSTANTS_UPDATED 0x100000
/* user class has methods with static variables */
#define ZEND_HAS_STATIC_IN_METHODS 0x800000
#define ZEND_ACC_CLOSURE 0x100000
#define ZEND_ACC_GENERATOR 0x800000
/* function flag for internal user call handlers __call, __callstatic */
#define ZEND_ACC_CALL_VIA_HANDLER 0x200000
/* disable inline caching */
#define ZEND_ACC_NEVER_CACHE 0x400000
#define ZEND_ACC_VARIADIC 0x1000000
#define ZEND_ACC_RETURN_REFERENCE 0x4000000
#define ZEND_ACC_DONE_PASS_TWO 0x8000000
/* function has arguments with type hinting */
#define ZEND_ACC_HAS_TYPE_HINTS 0x10000000
/* op_array has finally blocks */
#define ZEND_ACC_HAS_FINALLY_BLOCK 0x20000000
/* internal function is allocated at arena */
#define ZEND_ACC_ARENA_ALLOCATED 0x20000000
/* Function has a return type hint (or class has such non-private function) */
#define ZEND_ACC_HAS_RETURN_TYPE 0x40000000
属性的实现
通过调用zend_declare_property_xxx将非常量属性添加到ce.properties_info中。
typedef struct _zend_property_info {
uint32_t offset; /* property offset for object properties or
property index for static properties */
uint32_t flags;/*访问属性*/
zend_string *name;
zend_string *doc_comment;
zend_class_entry *ce;
} zend_property_info;
通过zend_declare_class_constant_xxx将常量添加到ce.constants_table中。 常量的哈希表的key对应常量的名称,value对应常量的值。
类的方法注册
在上面类名与属性中我们已经知道类是怎么被注册到compiler_globals.class_table中的,那么方法又是怎么被注册到类中呢?我们发现INIT_CLASS_ENTRY第三个参数就是我们的方法列表。
INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
const zend_function_entry pdo_dbstmt_functions[] = {
PHP_ME(PDOStatement, execute, arginfo_pdostatement_execute, ZEND_ACC_PUBLIC)
//...
{NULL, NULL, NULL}
};
#define PHP_ME ZEND_ME
#define ZEND_ME(classname, name, arg_info, flags) ZEND_FENTRY(name, ZEND_MN(classname##_##name), arg_info, flags)
#define ZEND_MN(name) zim_##name
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },
展开即:
const zend_function_entry pdo_dbstmt_functions[] = {
PHP_ME(PDOStatement, execute, arginfo_pdostatement_execute, ZEND_ACC_PUBLIC)
{"execute",zim_PDOStatement_execute,arginfo_pdostatement_execute,
参数个数,ZEND_ACC_PUBLIC},
//...
{NULL, NULL, NULL}
};
mysqli扩展中
const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(autocommit, mysqli_autocommit, arginfo_class_mysqli_autocommit)
{NULL, NULL, NULL}
};
#define PHP_FALIAS ZEND_FALIAS
#define ZEND_FALIAS(name, alias, arg_info) ZEND_FENTRY(name, ZEND_FN(alias), arg_info, 0)
#define ZEND_FN(name) zif_##name
展开即:
{"autocommit","zif_mysqli_autocommit",
arginfo_class_mysqli_autocommit,0},
可见要设置方法的访问属性,最好还是用ZEND_FENTRY宏。
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
通过上面的代码,可以看出,申明全局函数可以用PHP_FUNCTION
声明类的方法名可以用PHP_METHOD,由于方法最终都是函数的结构体,所以用上面的也行。 当需要支持类与全局函数时,最好用PHP_FUNCTION进行申明,然后用PHP_FALIAS进行注册。
脚本调用类
上面我们解析了脚本调用函数,通过简单的分析,我们可以知道调用方法跟调用函数基本一致,区别在多了运行的上下文环境,即方法是某个类的成员函数,调用时要判断访问权限之类的。