function 的声明
<?php
function test()
{
echo "abc";
} test();
?>
结论:
一 编译
a.对 函数声明进行词法分析和语法分析:在语法分析中的函数zend_do_begin_function_declaration 作用是: 初始化zend_op_array,填充 function_name ,line_start ,设定相应opcode:ZEND_DECLARE_FUNCTION, 以key为function_name, value为 zend_op_array,存入CG(function_table)中;zend_do_end_function_declaration作用为: 填充zend_op_array中的line_end
b.对 函数调用进行词法分析和语法分析: 语法分析中的函数 zend_do_begin_function_call 作用是:将函数名转化为小写,zend_do_end_function_call中设定opcode:ZEND_DO_FCALL, op1为function_name,op2为no use
二 执行
c.执行a中的hanlder ZEND_DECLARE_FUNCTION,如果函数名重复出现,则报错
d.执行b中的handler ZEND_DO_FCALL ,
1)词法分析,提取function 关键字
<ST_IN_SCRIPTING>"function" {
return T_FUNCTION;
}
2)语法分析,每一个字符串表达式后面跟着一个方法,此方法是用来生成opcode
function:
T_FUNCTION { $$.u.op.opline_num = CG(zend_lineno); }
; unticked_function_declaration_statement:
function is_reference T_STRING { zend_do_begin_function_declaration(&$, &$, , $.op_type, NULL TSRMLS_CC); }
'(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$ TSRMLS_CC); }
; parameter_list:
non_empty_parameter_list
| /* empty */
;
//用来处理参数
non_empty_parameter_list:
optional_class_type T_VARIABLE { $$.op_type = IS_UNUSED; $$.u.op.num=; zend_do_receive_arg(ZEND_RECV, &$, &$$, NULL, &$, TSRMLS_CC); } //$$.u.op.num=1 说明是第一个参数,也只存在一个参数
| optional_class_type '&' T_VARIABLE { $$.op_type = IS_UNUSED; $$.u.op.num=; zend_do_receive_arg(ZEND_RECV, &$, &$$, NULL, &$, TSRMLS_CC); } //这种方法已经取消了,估计是向下兼容
| optional_class_type '&' T_VARIABLE '=' static_scalar { $$.op_type = IS_UNUSED; $$.u.op.num=; zend_do_receive_arg(ZEND_RECV_INIT, &$, &$$, &$, &$, TSRMLS_CC); }
| optional_class_type T_VARIABLE '=' static_scalar { $$.op_type = IS_UNUSED; $$.u.op.num=; zend_do_receive_arg(ZEND_RECV_INIT, &$, &$$, &$, &$, TSRMLS_CC); }
| non_empty_parameter_list ',' optional_class_type T_VARIABLE { $$=$; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV, &$, &$$, NULL, &$, TSRMLS_CC); } // $$.u.op.num++递增,多个参数,用逗号分隔
| non_empty_parameter_list ',' optional_class_type '&' T_VARIABLE { $$=$; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV, &$, &$$, NULL, &$, TSRMLS_CC); }
| non_empty_parameter_list ',' optional_class_type '&' T_VARIABLE '=' static_scalar { $$=$; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV_INIT, &$, &$$, &$, &$, TSRMLS_CC); }
| non_empty_parameter_list ',' optional_class_type T_VARIABLE '=' static_scalar { $$=$; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV_INIT, &$, &$$, &$, &$, TSRMLS_CC); }
;
对函数头进行语法分析 ,主要 往zend_op_array中填充信息,比如 函数开始行号
void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */
{
zend_op_array op_array;
char *name = function_name->u.constant.value.str.val;
int name_len = function_name->u.constant.value.str.len;
int function_begin_line = function_token->u.op.opline_num;
zend_uint fn_flags;
const char *lcname;
zend_bool orig_interactive;
ALLOCA_FLAG(use_heap) if (is_method) {
//忽略,这是针对类方法的
fn_flags = Z_LVAL(fn_flags_znode->u.constant); /* must be done *after* the above check */
} else {
fn_flags = ;
}
if ((fn_flags & ZEND_ACC_STATIC) && (fn_flags & ZEND_ACC_ABSTRACT) && !(CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)) {
zend_error(E_STRICT, "Static function %s%s%s() should not be abstract", is_method ? CG(active_class_entry)->name : "", is_method ? "::" : "", Z_STRVAL(function_name->u.constant));
} function_token->u.op_array = CG(active_op_array);//保存之前的CG(active_op_array),zend_do_end_funciton_declaration函数里将恢复CG(active_op_array) orig_interactive = CG(interactive);
CG(interactive) = ;
init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
CG(interactive) = orig_interactive;
op_array.function_name = name; //填充functio_name字段
if (return_reference) {
op_array.fn_flags |= ZEND_ACC_RETURN_REFERENCE;
}
op_array.fn_flags |= fn_flags; op_array.scope = is_method?CG(active_class_entry):NULL;
op_array.prototype = NULL;
op_array.line_start = zend_get_compiled_lineno(TSRMLS_C); //填充 函数首行行号 if (is_method) {
//忽略,这是针对类方法的处理
} else {
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
zval key; if (CG(current_namespace)) { //针对命名空间的
/* Prefix function name with current namespace name */
znode tmp; tmp.u.constant = *CG(current_namespace);
zval_copy_ctor(&tmp.u.constant);
zend_do_build_namespace_name(&tmp, &tmp, function_name TSRMLS_CC);
op_array.function_name = Z_STRVAL(tmp.u.constant);
name_len = Z_STRLEN(tmp.u.constant);
lcname = zend_str_tolower_dup(Z_STRVAL(tmp.u.constant), name_len);
} else {
lcname = zend_str_tolower_dup(name, name_len); //函数名设置为小写
} opline->opcode = ZEND_DECLARE_FUNCTION; //设置执行时的handler,在这里,如果函数名重复出现,会提示 已声明过函数了
opline->op1_type = IS_CONST;
build_runtime_defined_function_key(&key, lcname, name_len TSRMLS_CC);
opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); //将key存入到zend_op_array中的literals中,他本身是个数组,即op1.constant是键,值是key
Z_HASH_P(&CONSTANT(opline->op1.constant)) = zend_hash_func(Z_STRVAL(CONSTANT(opline->op1.constant)), Z_STRLEN(CONSTANT(opline->op1.constant)));
opline->op2_type = IS_CONST;
LITERAL_STRINGL(opline->op2, lcname, name_len, ); //opline->op2.constant设置为 literals数组中的键,值为lcname
CALCULATE_LITERAL_HASH(opline->op2.constant);
opline->extended_value = ZEND_DECLARE_FUNCTION;
//放入CG中函数符号表,上面的key为 \0函数名文件名称,不知道为什么这样写?为什么不把函数名 直接存入 函数符号表?
zend_hash_quick_update(CG(function_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array));
zend_stack_push(&CG(context_stack), (void *) &CG(context), sizeof(CG(context)));
zend_init_compiler_context(TSRMLS_C);
} //忽略
}
}
ZEND_API char *zend_str_tolower_copy(char *dest, const char *source, unsigned int length) /* {{{ */
{
register unsigned char *str = (unsigned char*)source;
register unsigned char *result = (unsigned char*)dest;
register unsigned char *end = str + length; while (str < end) {
*result++ = zend_tolower((int)*str++);
}
*result = '\0'; return dest;
}
/* }}} */ ZEND_API char *zend_str_tolower_dup(const char *source, unsigned int length) /* {{{ */
{
return zend_str_tolower_copy((char *)emalloc(length+), source, length);
}
static void build_runtime_defined_function_key(zval *result, const char *name, int name_length TSRMLS_DC) /* {{{ */
{
char char_pos_buf[];
uint char_pos_len;
const char *filename; char_pos_len = zend_sprintf(char_pos_buf, "%p", LANG_SCNG(yy_text));
if (CG(active_op_array)->filename) {
filename = CG(active_op_array)->filename;
} else {
filename = "-";
} /* NULL, name length, filename length, last accepting char position length */
result->value.str.len = +name_length+strlen(filename)+char_pos_len; /* must be binary safe */
result->value.str.val = (char *) safe_emalloc(result->value.str.len, , );
result->value.str.val[] = '\0';
sprintf(result->value.str.val+, "%s%s%s", name, filename, char_pos_buf); result->type = IS_STRING;
Z_SET_REFCOUNT_P(result, );
}
static inline void zend_insert_literal(zend_op_array *op_array, const zval *zv, int literal_position TSRMLS_DC) /* {{{ */
{
if (Z_TYPE_P(zv) == IS_STRING || Z_TYPE_P(zv) == IS_CONSTANT) {
zval *z = (zval*)zv;
Z_STRVAL_P(z) = (char*)zend_new_interned_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv) + , TSRMLS_CC);
}
CONSTANT_EX(op_array, literal_position) = *zv;
Z_SET_REFCOUNT(CONSTANT_EX(op_array, literal_position), );
Z_SET_ISREF(CONSTANT_EX(op_array, literal_position));
op_array->literals[literal_position].hash_value = ;
op_array->literals[literal_position].cache_slot = -;
}
/* }}} */ /* Is used while compiling a function, using the context to keep track
of an approximate size to avoid to relocate to often.
Literals are truncated to actual size in the second compiler pass (pass_two()). */
int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC) /* {{{ */
{
int i = op_array->last_literal;
op_array->last_literal++;
if (i >= CG(context).literals_size) {
while (i >= CG(context).literals_size) {
CG(context).literals_size += ; /* FIXME */
}
op_array->literals = (zend_literal*)erealloc(op_array->literals, CG(context).literals_size * sizeof(zend_literal));
}
zend_insert_literal(op_array, zv, i TSRMLS_CC);
return i;
}
zend_op *get_next_op(zend_op_array *op_array TSRMLS_DC)
{
zend_uint next_op_num = op_array->last++;
zend_op *next_op; if (next_op_num >= CG(context).opcodes_size) {
if (op_array->fn_flags & ZEND_ACC_INTERACTIVE) {
/* we messed up */
zend_printf("Ran out of opcode space!\n"
"You should probably consider writing this huge script into a file!\n");
zend_bailout();
}
CG(context).opcodes_size *= ;
op_array_alloc_ops(op_array, CG(context).opcodes_size);
} next_op = &(op_array->opcodes[next_op_num]); init_op(next_op TSRMLS_CC); return next_op;
}
对函数体进行语法分析,主要是往zend_op_array中填充 函数结束行号,使用 pass_two函数,遍历CG(active_op_array)中的opcodes,设置zend_op 的handler
void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /* {{{ */
{
char lcname[];
int name_len; zend_do_extended_info(TSRMLS_C);
zend_do_return(NULL, TSRMLS_CC); pass_two(CG(active_op_array) TSRMLS_CC);
zend_release_labels(TSRMLS_C); if (CG(active_class_entry)) {
zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC);
} else {
/* we don't care if the function name is longer, in fact lowercasing only
* the beginning of the name speeds up the check process */
name_len = strlen(CG(active_op_array)->function_name);
zend_str_tolower_copy(lcname, CG(active_op_array)->function_name, MIN(name_len, sizeof(lcname)-));
lcname[sizeof(lcname)-] = '\0'; /* zend_str_tolower_copy won't necessarily set the zero byte */
if (name_len == sizeof(ZEND_AUTOLOAD_FUNC_NAME) - && !memcmp(lcname, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)) && CG(active_op_array)->num_args != ) {
zend_error(E_COMPILE_ERROR, "%s() must take exactly 1 argument", ZEND_AUTOLOAD_FUNC_NAME);
}
}
//得到函数体结束的 行
CG(active_op_array)->line_end = zend_get_compiled_lineno(TSRMLS_C);
CG(active_op_array) = function_token->u.op_array; /* Pop the switch and foreach seperators */
zend_stack_del_top(&CG(switch_cond_stack));
zend_stack_del_top(&CG(foreach_copy_stack));
}
ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
{
zend_op *opline, *end; if (op_array->type!=ZEND_USER_FUNCTION && op_array->type!=ZEND_EVAL_CODE) {
return ;
}
if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) {
zend_update_extended_info(op_array TSRMLS_CC);
}
if (CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY) {
zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array TSRMLS_CC);
} if (!(op_array->fn_flags & ZEND_ACC_INTERACTIVE) && CG(context).vars_size != op_array->last_var) {
op_array->vars = (zend_compiled_variable *) erealloc(op_array->vars, sizeof(zend_compiled_variable)*op_array->last_var);
CG(context).vars_size = op_array->last_var;
}
if (!(op_array->fn_flags & ZEND_ACC_INTERACTIVE) && CG(context).opcodes_size != op_array->last) {
op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
CG(context).opcodes_size = op_array->last;
}
if (!(op_array->fn_flags & ZEND_ACC_INTERACTIVE) && CG(context).literals_size != op_array->last_literal) {
op_array->literals = (zend_literal*)erealloc(op_array->literals, sizeof(zend_literal) * op_array->last_literal);
CG(context).literals_size = op_array->last_literal;
} opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
if (opline->op1_type == IS_CONST) {
opline->op1.zv = &op_array->literals[opline->op1.constant].constant;
}
if (opline->op2_type == IS_CONST) {
opline->op2.zv = &op_array->literals[opline->op2.constant].constant;
}
switch (opline->opcode) {
case ZEND_GOTO:
if (Z_TYPE_P(opline->op2.zv) != IS_LONG) {
zend_resolve_goto_label(op_array, opline, TSRMLS_CC);
}
/* break omitted intentionally */
case ZEND_JMP:
opline->op1.jmp_addr = &op_array->opcodes[opline->op1.opline_num];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_JMP_SET_VAR:
opline->op2.jmp_addr = &op_array->opcodes[opline->op2.opline_num];
break;
}
ZEND_VM_SET_OPCODE_HANDLER(opline);
opline++;
} op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
return ;
}
ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
} static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)
{
static const int zend_vm_decode[] = {
_UNUSED_CODE, /* 0 */
_CONST_CODE, /* 1 = IS_CONST */
_TMP_CODE, /* 2 = IS_TMP_VAR */
_UNUSED_CODE, /* 3 */
_VAR_CODE, /* 4 = IS_VAR */
_UNUSED_CODE, /* 5 */
_UNUSED_CODE, /* 6 */
_UNUSED_CODE, /* 7 */
_UNUSED_CODE, /* 8 = IS_UNUSED */
_UNUSED_CODE, /* 9 */
_UNUSED_CODE, /* 10 */
_UNUSED_CODE, /* 11 */
_UNUSED_CODE, /* 12 */
_UNUSED_CODE, /* 13 */
_UNUSED_CODE, /* 14 */
_UNUSED_CODE, /* 15 */
_CV_CODE /* 16 = IS_CV */
};
return zend_opcode_handlers[opcode * + zend_vm_decode[op->op1_type] * + zend_vm_decode[op->op2_type]]; //通过 zend_op中的opcode 和左右值类型 ,计算出实际的handler;zend_opcode_handlers是一个巨大的数组,里面都是指针函数
}
如果函数体中,还有其他数据,例如赋值,就把相应opcode放到zend_op_array中的opcodes中
static int ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE SAVE_OPLINE();
do_bind_function(EX(op_array), opline, EG(function_table), );
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
//在执行期间,将函数名放入 EG(function_table)中,如果发现已经有了,则报错
ZEND_API int do_bind_function(const zend_op_array *op_array, zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
{
zend_function *function;
zval *op1, *op2; if (compile_time) {
op1 = &CONSTANT_EX(op_array, opline->op1.constant);
op2 = &CONSTANT_EX(op_array, opline->op2.constant);
} else {
op1 = opline->op1.zv; //这里的op1.zv的值是通过上面的pass_two这个函数中得到的,他会遍历zend_op_array中的opcodes,如果op1_type为 IS_CONST,就会进行把zend_op_array中的literals相应的值赋给op1.zv
op2 = opline->op2.zv; //同上
}
//这里的op1为上面的\0函数名文件名称,从EG(function_table)中取出,值赋给function
zend_hash_quick_find(function_table, Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_HASH_P(op1), (void *) &function);
//将键为op2(只有函数名),值为function,插入到EG(function)中,如果插入失败,说明函数多次被插入,要报函数已被声明的提示
if (zend_hash_quick_add(function_table, Z_STRVAL_P(op2), Z_STRLEN_P(op2)+, Z_HASH_P(op2), function, sizeof(zend_function), NULL)==FAILURE) {
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
zend_function *old_function; if (zend_hash_quick_find(function_table, Z_STRVAL_P(op2), Z_STRLEN_P(op2)+, Z_HASH_P(op2), (void *) &old_function)==SUCCESS
&& old_function->type == ZEND_USER_FUNCTION
&& old_function->op_array.last > ) {
zend_error(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
function->common.function_name,
old_function->op_array.filename,
old_function->op_array.opcodes[].lineno);
} else {
zend_error(error_level, "Cannot redeclare %s()", function->common.function_name);
}
return FAILURE;
} else {
(*function->op_array.refcount)++;
function->op_array.static_variables = NULL; /* NULL out the unbound function */
return SUCCESS;
}
}
对于 调用函数时的编译
function_call:
namespace_name '(' { $.u.op.opline_num = zend_do_begin_function_call(&$, TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$, &$$, &$, , $.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name '(' { $.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$.u.constant); zend_do_build_namespace_name(&$, &$, &$ TSRMLS_CC); $.u.op.opline_num = zend_do_begin_function_call(&$, TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$, &$$, &$, , $.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NS_SEPARATOR namespace_name '(' { $.u.op.opline_num = zend_do_begin_function_call(&$, TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$, &$$, &$, , $.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { $.u.op.opline_num = zend_do_begin_class_member_function_call(&$, &$ TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call($.u.op.opline_num?NULL:&$, &$$, &$, $.u.op.opline_num, $.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$, BP_VAR_R, TSRMLS_CC); zend_do_begin_class_member_function_call(&$, &$ TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$, , TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { zend_do_begin_class_member_function_call(&$, &$ TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$, , TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$, BP_VAR_R, TSRMLS_CC); zend_do_begin_class_member_function_call(&$, &$ TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$, , TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_without_objects '(' { zend_do_end_variable_parse(&$, BP_VAR_R, TSRMLS_CC); zend_do_begin_dynamic_function_call(&$, TSRMLS_CC); }
function_call_parameter_list ')'
{ zend_do_end_function_call(&$, &$$, &$, , TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
;
int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace TSRMLS_DC) /* {{{ */
{
zend_function *function;
char *lcname;
char *is_compound = memchr(Z_STRVAL(function_name->u.constant), '\\', Z_STRLEN(function_name->u.constant)); zend_resolve_non_class_name(function_name, check_namespace TSRMLS_CC); if (check_namespace && CG(current_namespace) && !is_compound) {
/* We assume we call function from the current namespace
if it is not prefixed. */ /* In run-time PHP will check for function with full name and
internal function with short name */
zend_do_begin_dynamic_function_call(function_name, TSRMLS_CC);
return ;
} lcname = zend_str_tolower_dup(function_name->u.constant.value.str.val, function_name->u.constant.value.str.len); //将函数名称转化为小写,因为调用的时候,函数名可能是大写的
if ((zend_hash_find(CG(function_table), lcname, function_name->u.constant.value.str.len+, (void **) &function)==FAILURE) ||
((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) &&
(function->type == ZEND_INTERNAL_FUNCTION))) {
zend_do_begin_dynamic_function_call(function_name, TSRMLS_CC);
efree(lcname);
return ; /* Dynamic */
}
efree(function_name->u.constant.value.str.val);
function_name->u.constant.value.str.val = lcname; zend_stack_push(&CG(function_call_stack), (void *) &function, sizeof(zend_function *));
zend_do_extended_fcall_begin(TSRMLS_C);
return ;
}
ZEND_API int zend_stack_push(zend_stack *stack, const void *element, int size)
{
if (stack->top >= stack->max) { /* we need to allocate more memory */
stack->elements = (void **) erealloc(stack->elements,
(sizeof(void **) * (stack->max += STACK_BLOCK_SIZE)));
if (!stack->elements) {
return FAILURE;
}
}
stack->elements[stack->top] = (void *) emalloc(size);
memcpy(stack->elements[stack->top], element, size);
return stack->top++;
} typedef struct _zend_stack {
int top, max;
void **elements;
} zend_stack;
void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */
{
zend_op *opline; if (is_method && function_name && function_name->op_type == IS_UNUSED) { //类方法
/* clone */
if (Z_LVAL(argument_list->u.constant) != ) {
zend_error(E_WARNING, "Clone method does not require arguments");
}
opline = &CG(active_op_array)->opcodes[Z_LVAL(function_name->u.constant)];
} else { //普通方法
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
opline->opcode = ZEND_DO_FCALL; //设置执行时的handler
SET_NODE(opline->op1, function_name); //设置左值为 方法的名称
CALCULATE_LITERAL_HASH(opline->op1.constant);
GET_CACHE_SLOT(opline->op1.constant);
} else {
opline->opcode = ZEND_DO_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
}
} opline->result.var = get_temporary_variable(CG(active_op_array));
opline->result_type = IS_VAR;
GET_NODE(result, opline->result) ;
SET_UNUSED(opline->op2); zend_stack_del_top(&CG(function_call_stack));
opline->extended_value = Z_LVAL(argument_list->u.constant);
}
#define CONSTANT_EX(op_array, op) \
(op_array)->literals[op].constant #define CONSTANT(op) \
CONSTANT_EX(CG(active_op_array), op) #define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \
if ((src)->op_type == IS_CONST) { \
target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant TSRMLS_CC); \
} else { \
target = (src)->u.op; \
} \
} while () #define GET_NODE(target, src) do { \
(target)->op_type = src ## _type; \
if ((target)->op_type == IS_CONST) { \
(target)->u.constant = CONSTANT(src.constant); \
} else { \
(target)->u.op = src; \
(target)->EA = ; \
} \
} while ()
static zend_uint get_temporary_variable(zend_op_array *op_array) /* {{{ */
{
return (op_array->T)++ * ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable));
}
zend_op *get_next_op(zend_op_array *op_array TSRMLS_DC)
{
zend_uint next_op_num = op_array->last++;
zend_op *next_op; if (next_op_num >= CG(context).opcodes_size) {
if (op_array->fn_flags & ZEND_ACC_INTERACTIVE) {
/* we messed up */
zend_printf("Ran out of opcode space!\n"
"You should probably consider writing this huge script into a file!\n");
zend_bailout();
}
CG(context).opcodes_size *= ;
op_array_alloc_ops(op_array, CG(context).opcodes_size);
} next_op = &(op_array->opcodes[next_op_num]); init_op(next_op TSRMLS_CC); return next_op;
}
void init_op(zend_op *op TSRMLS_DC)
{
memset(op, , sizeof(zend_op)); //初始化
op->lineno = CG(zend_lineno);
SET_UNUSED(op->result);
}
typedef struct _zend_file_handle {
zend_stream_type type;
const char *filename;
char *opened_path;
union {
int fd;
FILE *fp;
zend_stream stream;
} handle;
zend_bool free_filename;
} zend_file_handle;
void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size TSRMLS_DC)
{
op_array->type = type; if (CG(interactive)) {
/* We must avoid a realloc() on the op_array in interactive mode, since pointers to constants
* will become invalid
*/
initial_ops_size = INITIAL_INTERACTIVE_OP_ARRAY_SIZE;
} op_array->refcount = (zend_uint *) emalloc(sizeof(zend_uint));
*op_array->refcount = ;
op_array->last = ;
op_array->opcodes = NULL;
op_array_alloc_ops(op_array, initial_ops_size); op_array->last_var = ;
op_array->vars = NULL; op_array->T = ; op_array->function_name = NULL;
op_array->filename = zend_get_compiled_filename(TSRMLS_C);
op_array->doc_comment = NULL;
op_array->doc_comment_len = ; op_array->arg_info = NULL;
op_array->num_args = ;
op_array->required_num_args = ; op_array->scope = NULL; op_array->brk_cont_array = NULL;
op_array->try_catch_array = NULL;
op_array->last_brk_cont = ; op_array->static_variables = NULL;
op_array->last_try_catch = ; op_array->this_var = -; op_array->fn_flags = CG(interactive)?ZEND_ACC_INTERACTIVE:; op_array->early_binding = -; op_array->last_literal = ;
op_array->literals = NULL; op_array->run_time_cache = NULL;
op_array->last_cache_slot = ; memset(op_array->reserved, , ZEND_MAX_RESERVED_RESOURCES * sizeof(void*)); zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array TSRMLS_CC);
}
执行 opcode
static int ZEND_FASTCALL ZEND_DO_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE zval *fname = opline->op1.zv; zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope)); if (CACHED_PTR(opline->op1.literal->cache_slot)) {
EX(function_state).function = CACHED_PTR(opline->op1.literal->cache_slot);
} else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(fname), Z_STRLEN_P(fname)+, Z_HASH_P(fname), (void **) &EX(function_state).function)==FAILURE)) {
//上面的zend_hash_quick_find很重要,从EG(function_table)中取出key为fname的value值,并将些值放入EX(fucntion_state)的function中
SAVE_OPLINE();
zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val);
} else {
CACHE_PTR(opline->op1.literal->cache_slot, EX(function_state).function); //放入原生态C数组中,因为会有第二次调用函数
}
EX(object) = NULL; return zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_bool should_change_scope = ;
zend_function *fbc = EX(function_state).function; SAVE_OPLINE();
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != )) {
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != )) {
zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", fbc->common.scope->name, fbc->common.function_name);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE(); /* Never reached */
}
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != )) {
zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated",
fbc->common.scope ? fbc->common.scope->name : "",
fbc->common.scope ? "::" : "",
fbc->common.function_name);
}
}
if (fbc->common.scope &&
!(fbc->common.fn_flags & ZEND_ACC_STATIC) &&
!EX(object)) { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
/* FIXME: output identifiers properly */
zend_error(E_STRICT, "Non-static method %s::%s() should not be called statically", fbc->common.scope->name, fbc->common.function_name);
} else {
/* FIXME: output identifiers properly */
/* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */
zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically", fbc->common.scope->name, fbc->common.function_name);
}
} if (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) {
should_change_scope = ;
EX(current_this) = EG(This);
EX(current_scope) = EG(scope);
EX(current_called_scope) = EG(called_scope);
EG(This) = EX(object);
EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(object)) ? fbc->common.scope : NULL;
EG(called_scope) = EX(called_scope);
} zend_arg_types_stack_3_pop(&EG(arg_types_stack), &EX(called_scope), &EX(current_object), &EX(fbc));
EX(function_state).arguments = zend_vm_stack_push_args(opline->extended_value TSRMLS_CC);
LOAD_OPLINE(); if (fbc->type == ZEND_INTERNAL_FUNCTION) { //php内部函数
temp_variable *ret = &EX_T(opline->result.var); if (fbc->common.arg_info) {
zend_uint i=;
zval **p = (zval**)EX(function_state).arguments;
ulong arg_count = opline->extended_value; while (arg_count>) {
zend_verify_arg_type(fbc, ++i, *(p-arg_count), TSRMLS_CC);
arg_count--;
}
} if (EXPECTED(EG(exception) == NULL)) {
MAKE_STD_ZVAL(ret->var.ptr);
ZVAL_NULL(ret->var.ptr);
ret->var.ptr_ptr = &ret->var.ptr;
ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != ; if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
} else {
zend_execute_internal(execute_data, RETURN_VALUE_USED(opline) TSRMLS_CC);
} if (!RETURN_VALUE_USED(opline)) {
zval_ptr_dtor(&ret->var.ptr);
}
} else if (RETURN_VALUE_USED(opline)) {
EX_T(opline->result.var).var.ptr = NULL;
}
} else if (fbc->type == ZEND_USER_FUNCTION) { //php算定义的函数
EX(original_return_value) = EG(return_value_ptr_ptr);
EG(active_symbol_table) = NULL;
EG(active_op_array) = &fbc->op_array;
EG(return_value_ptr_ptr) = NULL;
if (RETURN_VALUE_USED(opline)) {
temp_variable *ret = &EX_T(opline->result.var); ret->var.ptr = NULL;
EG(return_value_ptr_ptr) = &ret->var.ptr;
ret->var.ptr_ptr = &ret->var.ptr;
ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != ;
} if (EXPECTED(zend_execute == execute)) {
if (EXPECTED(EG(exception) == NULL)) {
ZEND_VM_ENTER();
}
} else {
zend_execute(EG(active_op_array) TSRMLS_CC);
} EG(opline_ptr) = &EX(opline);
EG(active_op_array) = EX(op_array);
EG(return_value_ptr_ptr) = EX(original_return_value);
if (EG(active_symbol_table)) {
if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) {
zend_hash_destroy(EG(active_symbol_table));
FREE_HASHTABLE(EG(active_symbol_table));
} else {
/* clean before putting into the cache, since clean
could call dtors, which could use cached hash */
zend_hash_clean(EG(active_symbol_table));
*(++EG(symtable_cache_ptr)) = EG(active_symbol_table);
}
}
EG(active_symbol_table) = EX(symbol_table);
} else { /* ZEND_OVERLOADED_FUNCTION */
MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr);
ZVAL_NULL(EX_T(opline->result.var).var.ptr); /* Not sure what should be done here if it's a static method */
if (EXPECTED(EX(object) != NULL)) {
Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, opline->extended_value, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
} else {
zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object");
} if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
efree((char*)fbc->common.function_name);
}
efree(fbc); if (!RETURN_VALUE_USED(opline)) {
zval_ptr_dtor(&EX_T(opline->result.var).var.ptr);
} else {
Z_UNSET_ISREF_P(EX_T(opline->result.var).var.ptr);
Z_SET_REFCOUNT_P(EX_T(opline->result.var).var.ptr, );
EX_T(opline->result.var).var.fcall_returned_reference = ;
EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr;
}
} EX(function_state).function = (zend_function *) EX(op_array);
EX(function_state).arguments = NULL; if (should_change_scope) {
if (EG(This)) {
if (UNEXPECTED(EG(exception) != NULL) && IS_CTOR_CALL(EX(called_scope))) {
if (IS_CTOR_USED(EX(called_scope))) {
Z_DELREF_P(EG(This));
}
if (Z_REFCOUNT_P(EG(This)) == ) {
zend_object_store_ctor_failed(EG(This) TSRMLS_CC);
}
}
zval_ptr_dtor(&EG(This));
}
EG(This) = EX(current_this);
EG(scope) = EX(current_scope);
EG(called_scope) = EX(current_called_scope);
} EX(object) = EX(current_object);
EX(called_scope) = DECODE_CTOR(EX(called_scope)); zend_vm_stack_clear_multiple(TSRMLS_C); if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL TSRMLS_CC);
if (RETURN_VALUE_USED(opline) && EX_T(opline->result.var).var.ptr) {
zval_ptr_dtor(&EX_T(opline->result.var).var.ptr);
}
HANDLE_EXCEPTION();
} ZEND_VM_NEXT_OPCODE();
}
void init_executor(TSRMLS_D) /* {{{ */
{
zend_init_fpu(TSRMLS_C); INIT_ZVAL(EG(uninitialized_zval));
/* trick to make uninitialized_zval never be modified, passed by ref, etc. */
Z_ADDREF(EG(uninitialized_zval));
INIT_ZVAL(EG(error_zval));
EG(uninitialized_zval_ptr)=&EG(uninitialized_zval);
EG(error_zval_ptr)=&EG(error_zval);
zend_ptr_stack_init(&EG(arg_types_stack));
/* destroys stack frame, therefore makes core dumps worthless */
#if 0&&ZEND_DEBUG
original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv);
#endif
EG(return_value_ptr_ptr) = NULL; EG(symtable_cache_ptr) = EG(symtable_cache) - ;
EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - ;
EG(no_extensions) = ; EG(function_table) = CG(function_table); //很重要
EG(class_table) = CG(class_table); EG(in_execution) = ;
EG(in_autoload) = NULL;
EG(autoload_func) = NULL;
EG(error_handling) = EH_NORMAL; zend_vm_stack_init(TSRMLS_C);
zend_vm_stack_push((void *) NULL TSRMLS_CC); zend_hash_init(&EG(symbol_table), , NULL, ZVAL_PTR_DTOR, );
EG(active_symbol_table) = &EG(symbol_table); zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_activator TSRMLS_CC);
EG(opline_ptr) = NULL; zend_hash_init(&EG(included_files), , NULL, NULL, ); EG(ticks_count) = ; EG(user_error_handler) = NULL; EG(current_execute_data) = NULL; zend_stack_init(&EG(user_error_handlers_error_reporting));
zend_ptr_stack_init(&EG(user_error_handlers));
zend_ptr_stack_init(&EG(user_exception_handlers)); zend_objects_store_init(&EG(objects_store), ); EG(full_tables_cleanup) = ;
#ifdef ZEND_WIN32
EG(timed_out) = ;
#endif EG(exception) = NULL;
EG(prev_exception) = NULL; EG(scope) = NULL;
EG(called_scope) = NULL; EG(This) = NULL; EG(active_op_array) = NULL; EG(active) = ;
EG(start_op) = NULL;
}
struct _zend_vm_stack {
void **top;
void **end;
zend_vm_stack prev;
}; static zend_always_inline void zend_vm_stack_push(void *ptr TSRMLS_DC)
{
ZEND_VM_STACK_GROW_IF_NEEDED();
*(EG(argument_stack)->top++) = ptr;
}
http://www.cnblogs.com/driftcloudy/p/3213504.html
函数传递参数入栈的原理
http://www.laruence.com/2010/01/03/1225.html
http://blog.sina.com.cn/s/blog_6ecf35840100m669.html 传值,传引用
http://www.phppan.com/2012/04/php-compiled-variable/ cv
本文主要参考“风雪之域”Laruence的文章。转载:http://www.laruence.com/2008/04/01/116.html
我们刚学php的时候,学到函数时,常会向里面传参数。比如function example($a,$b).那到底a和b的参数是怎么传入到example函数以及函数是怎么接受的呢,下面来为大家揭秘。
在 Zend/zend_language_scanner.l中我们会找到如下所示的代码:
<ST_IN_SCRipTING>
"function" {
return T_FUNCTION;
}
它所表示的含义是function将会生成T_FUNCTION标记。T_FUNCTION标记只是用来定义函数的生命,标明这是一个函数,更多的东西则是参数,返回值等等。
当php等高级语言被编译成机器码时面临一个问题,cpu不能识别函数怎么传参,传的几个参数,传递参数的工作必须由函数调用者或函数本书来完成。当函数调用时,调用者依次把参数压栈,然后调用函数,调用后,在堆栈中取得数据,并进行计算。函数计算完成后,由调用者或者函数本身修改堆栈,使堆栈恢复原状。在参数传递过程中,有下列三个问题需要注意:
1:参数多余一个时,传递参数的顺序
2:函数调用后,谁来恢复堆栈
3:函数的返回值放在什么地方。
在高级语言中,通过函数调用规范来说明这几个问题。常见的调用规范有下面几个:
stdcall、cdecl、fastcall、thiscall、naked call
具体的调用规范大家可以参考具体的例子,这里只是把这几个问题的答案说出来
1:传递参数是由后到前传递。function example($a,$b).中,先把$b压栈再把$a压栈
2:由相关的规范决定
function 的声明的更多相关文章
- javascript:function 函数声明和函数表达式 详解
函数声明(缩写为FD)是这样一种函数: 有一个特定的名称 在源码中的位置:要么处于程序级(Program level),要么处于其它函数的主体(FunctionBody)中 在进入上下文阶段创建 影响 ...
- 【JavaScript】[bind,call,apply] (function cal(){}());声明函数立即执行
---恢复内容开始--- 1.js 里函数调用有 4 种模式:方法调用.正常函数调用.构造器函数调用.apply/call 调用.同时,无论哪种函数调用除了你声明时定义的形参外,还会自动添加 2 个形 ...
- function——函数声明头的提升和预解析
函数: 即function语句的集合,就是将多个语句封装到一起: 函数的执行要会自己遍历,遇见函数 a():执行语句,就要移交控制权,函数执行完毕之后,控制权又移交回来了! 函数的参数要罗列在func ...
- es6 - 一共有 6 种声明变量的方法(var, function, let, const, class, import)
var命令和function命令声明的全局变量,依旧是顶层对象的属性:let命令.const命令.class命令声明的全局变量,不属于顶层对象的属性.也就是说,从 ES6 开始,全局变量将逐步与顶层对 ...
- javascript精雕细琢(一):var let const function声明的区别
目录 引言 一.var 二.let 三.const 四.function 五.总结 引言 在学习javascript的过程中,变量是无时无刻不在使用的.那么相对应的,变量声明方法也如是. ...
- JavaScript 函数声明,函数表达式,匿名函数的区别,深入理解立即执行函数(function(){…})()
function fnName(){xxxx}; // 函数声明:使用function关键字声明一个函数,在指定一个函数名. //例如:(正常,因为 提升 了函数声明,函数调用可以在函数声明之前) f ...
- 【repost】js中(function(){…})()立即执行函数写法理解
摘要: javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ...
- js函数表达式和函数声明的区别
我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐 藏"起来,外部作用域无法访问包装函数内部的任何内容. 例如: var a = 2; function ...
- JavaScript 函数的两种声明方式
1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...
随机推荐
- PHP5 GD库生成图形验证码(汉字)
PHP5 GD库生成图形验证码且带有汉字的实例分享. 1,利用GD库函数生成图片,并在图片上写指定字符imagecreatetruecolor 新建一个真彩色图像imagecolorallocate ...
- Treimu更新记录1.2.9.0
Treimu是一个WPF音乐播放器个人小项目.程序集文件:http://pan.baidu.com/s/1pJLSHsB项目源代码:http://pan.baidu.com/s/1jGHtjfC 1. ...
- 《WPF程序设计指南》读书笔记——第8章 依赖属性
1.依赖属性的效果 一旦规定视觉树上一个对象的fontsize属性,那么属于他的节点之下的所有对象都会沿袭这个属性,然而如果某个子节点明确的设定了自己的fontsize,就不会沿袭父节点的fontsi ...
- 【原创】一起学C++ 之->(箭头符号) ---------C++ primer plus(第6版)
1.C++新手在指定结构成员时,不知道何时用.运算符,何时是用->运算符. 结论:如果结构标识符是结构名,则使用句点运算符:如果标识符是指向结构的指针,则使用箭头运算符. #include &l ...
- Have Fun with Numbers (大数)
Notice that the number 123456789 is a 9-digit number consisting exactly the numbers from 1 to 9, wit ...
- GHOST中DISK TO DISK 和DISK FROM to image的区别
Ghost的Disk菜单下的子菜单项可以实现硬盘到硬盘的直接对拷(Disk-To Disk)、硬盘到镜像文件(Disk-To Image)、从镜像文件还原硬盘内容(Disk-From Image)。 ...
- C++ 虚函数表解析(转载)
转载自:陈皓 http://blog.csdn.net/haoel/article/details/1948051/ 前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型 ...
- Lua中cJson的读写
这里采用的是Lua CJson库,是一个高性能的JSON解析器和编码器,其性能比纯Lua库要高10~20倍.并且Lua Json完全支持UTF-8,无需以来其他非Lua/LuaJit相关包. 环境安装 ...
- sql shard/partition
sql http://www.infoq.com/news/2011/02/SQL-Sharding/ http://channel9.msdn.com/Shows/Data-Exposed/SqlD ...
- The ‘Microsoft.ACE.OLEDB.12.0′ provider is not registered on the local machine. (System.Data)
When you try to import Excel 2007 or later “.xlsx” files into an SQL Server 2008 database you may ge ...