为什么不将opcode 域和functecharts地图区域合并并成域

与《》相关:function test()
echo "abc";
&&& 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
  T_FUNCTION { $$.u.op.opline_num = CG(zend_lineno); }
unticked_function_declaration_statement:
  function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
'(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 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=1; zend_do_receive_arg(ZEND_RECV, &$2, &$$, NULL, &$1, 0 TSRMLS_CC); } //$$.u.op.num=1 说明是第一个参数,也只存在一个参数
optional_class_type '&' T_VARIABLE
{ $$.op_type = IS_UNUSED; $$.u.op.num=1; zend_do_receive_arg(ZEND_RECV, &$3, &$$, NULL, &$1, 1 TSRMLS_CC); } //这种方法已经取消了,估计是向下兼容
optional_class_type '&' T_VARIABLE '=' static_scalar
{ $$.op_type = IS_UNUSED; $$.u.op.num=1; zend_do_receive_arg(ZEND_RECV_INIT, &$3, &$$, &$5, &$1, 1 TSRMLS_CC); }
optional_class_type T_VARIABLE '=' static_scalar
{ $$.op_type = IS_UNUSED; $$.u.op.num=1; zend_do_receive_arg(ZEND_RECV_INIT, &$2, &$$, &$4, &$1, 0 TSRMLS_CC); }
non_empty_parameter_list ',' optional_class_type T_VARIABLE
{ $$=$1; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV, &$4, &$$, NULL, &$3, 0 TSRMLS_CC); } // $$.u.op.num++递增,多个参数,用逗号分隔
non_empty_parameter_list ',' optional_class_type '&' T_VARIABLE
{ $$=$1; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV, &$5, &$$, NULL, &$3, 1 TSRMLS_CC); }
non_empty_parameter_list ',' optional_class_type '&' T_VARIABLE
'=' static_scalar { $$=$1; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV_INIT, &$5, &$$, &$7, &$3, 1 TSRMLS_CC); }
non_empty_parameter_list ',' optional_class_type T_VARIABLE '=' static_scalar
{ $$=$1; $$.u.op.num++; zend_do_receive_arg(ZEND_RECV_INIT, &$4, &$$, &$6, &$3, 0 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_
char *name = function_name-&u.constant.value.str.
int name_len = function_name-&u.constant.value.str.
int function_begin_line = function_token-&u.op.opline_
zend_uint fn_
const char *
zend_bool orig_
ALLOCA_FLAG(use_heap)
if (is_method) {
//忽略,这是针对类方法的
fn_flags = Z_LVAL(fn_flags_znode-&u.constant); /* must be done *after* the above check */
fn_flags = 0;
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) = 0;
init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
CG(interactive) = orig_
op_array.function_name = //填充functio_name字段
if (return_reference) {
op_array.fn_flags |= ZEND_ACC_RETURN_REFERENCE;
op_array.fn_flags |= fn_
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) {
//忽略,这是针对类方法的处理
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
if (CG(current_namespace)) { //针对命名空间的
/* Prefix function name with current namespace name */
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);
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, 0); //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*)
register unsigned char *result = (unsigned char*)
register unsigned char *end = str +
while (str & end) {
*result++ = zend_tolower((int)*str++);
*result = '\0';
ZEND_API char *zend_str_tolower_dup(const char *source, unsigned int length) /* {{{ */
return zend_str_tolower_copy((char *)emalloc(length+1), source, length);
static void build_runtime_defined_function_key(zval *result, const char *name, int name_length TSRMLS_DC) /* {{{ */
char char_pos_buf[32];
uint char_pos_
const char *
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 = "-";
/* NULL, name length, filename length, last accepting char position length */
result-&value.str.len = 1+name_length+strlen(filename)+char_pos_
/* must be binary safe */
result-&value.str.val = (char *) safe_emalloc(result-&value.str.len, 1, 1);
result-&value.str.val[0] = '\0';
sprintf(result-&value.str.val+1, "%s%s%s", name, filename, char_pos_buf);
result-&type = IS_STRING;
Z_SET_REFCOUNT_P(result, 1);
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*)
Z_STRVAL_P(z) = (char*)zend_new_interned_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv) + 1, 1 TSRMLS_CC);
CONSTANT_EX(op_array, literal_position) = *
Z_SET_REFCOUNT(CONSTANT_EX(op_array, literal_position), 2);
Z_SET_ISREF(CONSTANT_EX(op_array, literal_position));
op_array-&literals[literal_position].hash_value = 0;
op_array-&literals[literal_position].cache_slot = -1;
/* 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()). */
zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC) /* {{{ */
int i = op_array-&last_
op_array-&last_literal++;
if (i &= CG(context).literals_size) {
while (i &= CG(context).literals_size) {
CG(context).literals_size += 16; /* 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);
zend_op *get_next_op(zend_op_array *op_array TSRMLS_DC)
zend_uint next_op_num = op_array-&last++;
zend_op *next_
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 *= 4;
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_
对函数体进行语法分析,主要是往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[16];
zend_do_extended_info(TSRMLS_C);
zend_do_return(NULL, 0 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);
/* 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)-1));
lcname[sizeof(lcname)-1] = '\0'; /* zend_str_tolower_copy won't necessarily set the zero byte */
if (name_len == sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)) && CG(active_op_array)-&num_args != 1) {
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_
/* 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, *
if (op_array-&type!=ZEND_USER_FUNCTION && op_array-&type!=ZEND_EVAL_CODE) {
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_
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-&
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_
opline = op_array-&
end = opline + op_array-&
while (opline & end) {
if (opline-&op1_type == IS_CONST) {
opline-&op1.zv = &op_array-&literals[opline-&op1.constant].
if (opline-&op2_type == IS_CONST) {
opline-&op2.zv = &op_array-&literals[opline-&op2.constant].
switch (opline-&opcode) {
case ZEND_GOTO:
if (Z_TYPE_P(opline-&op2.zv) != IS_LONG) {
zend_resolve_goto_label(op_array, opline, 1 TSRMLS_CC);
/* break omitted intentionally */
case ZEND_JMP:
opline-&op1.jmp_addr = &op_array-&opcodes[opline-&op1.opline_num];
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];
ZEND_VM_SET_OPCODE_HANDLER(opline);
op_array-&fn_flags |= ZEND_ACC_DONE_PASS_TWO;
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
/* 16 = IS_CV
return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op-&op1_type] * 5 + zend_vm_decode[op-&op2_type]]; //通过 zend_op中的opcode 和左右值类型 ,计算出实际的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), 0);
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 *
zval *op1, *op2;
if (compile_time) {
op1 = &CONSTANT_EX(op_array, opline-&op1.constant);
op2 = &CONSTANT_EX(op_array, opline-&op2.constant);
op1 = opline-&op1. //这里的op1.zv的值是通过上面的pass_two这个函数中得到的,他会遍历zend_op_array中的opcodes,如果op1_type为 IS_CONST,就会进行把zend_op_array中的literals相应的值赋给op1.zv
op2 = opline-&op2. //同上
//这里的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)+1, Z_HASH_P(op2), function, sizeof(zend_function), NULL)==FAILURE) {
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
zend_function *old_
if (zend_hash_quick_find(function_table, Z_STRVAL_P(op2), Z_STRLEN_P(op2)+1, Z_HASH_P(op2), (void *) &old_function)==SUCCESS
&& old_function-&type == ZEND_USER_FUNCTION
&& old_function-&op_array.last & 0) {
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[0].lineno);
zend_error(error_level, "Cannot redeclare %s()", function-&common.function_name);
return FAILURE;
(*function-&op_array.refcount)++;
function-&op_array.static_variables = NULL; /* NULL out the unbound function */
return SUCCESS;
对于 调用函数时的编译
function_call:
namespace_name '(' { $2.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
T_NAMESPACE T_NS_SEPARATOR namespace_name '(' { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant);
zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $4.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$1, &$$, &$6, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
T_NS_SEPARATOR namespace_name '(' { $3.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$2, &$$, &$5, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { $4.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$6, $4.u.op.opline_num, $4.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(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
variable_without_objects
'(' { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list ')'
{ zend_do_end_function_call(&$1, &$$, &$4, 0, 1 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 *
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, 1 TSRMLS_CC);
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+1, (void **) &function)==FAILURE) ||
((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) &&
(function-&type == ZEND_INTERNAL_FUNCTION))) {
zend_do_begin_dynamic_function_call(function_name, 0 TSRMLS_CC);
efree(lcname);
return 1; /* Dynamic */
efree(function_name-&u.constant.value.str.val);
function_name-&u.constant.value.str.val =
zend_stack_push(&CG(function_call_stack), (void *) &function, sizeof(zend_function *));
zend_do_extended_fcall_begin(TSRMLS_C);
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 {
void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */
if (is_method && function_name && function_name-&op_type == IS_UNUSED) { //类方法
/* clone */
if (Z_LVAL(argument_list-&u.constant) != 0) {
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);
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_ \
if ((src)-&op_type == IS_CONST) { \
target.constant = zend_add_literal(CG(active_op_array), &(src)-&u.constant TSRMLS_CC); \
} else { \
target = (src)-&u. \
} while (0)
#define GET_NODE(target, src) do { \
(target)-&op_type = src ## _ \
if ((target)-&op_type == IS_CONST) { \
(target)-&u.constant = CONSTANT(src.constant); \
} else { \
(target)-&u.op = \
(target)-&EA = 0; \
} while (0)
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_
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 *= 4;
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_
void init_op(zend_op *op TSRMLS_DC)
memset(op, 0, sizeof(zend_op)); //初始化
op-&lineno = CG(zend_lineno);
SET_UNUSED(op-&result);
typedef struct _zend_file_handle {
zend_stream_
const char
zend_bool free_
} zend_file_
void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size TSRMLS_DC)
op_array-&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 = 1;
op_array-&last = 0;
op_array-&opcodes = NULL;
op_array_alloc_ops(op_array, initial_ops_size);
op_array-&last_var = 0;
op_array-&vars = NULL;
op_array-&T = 0;
op_array-&function_name = NULL;
op_array-&filename = zend_get_compiled_filename(TSRMLS_C);
op_array-&doc_comment = NULL;
op_array-&doc_comment_len = 0;
op_array-&arg_info = NULL;
op_array-&num_args = 0;
op_array-&required_num_args = 0;
op_array-&scope = NULL;
op_array-&brk_cont_array = NULL;
op_array-&try_catch_array = NULL;
op_array-&last_brk_cont = 0;
op_array-&static_variables = NULL;
op_array-&last_try_catch = 0;
op_array-&this_var = -1;
op_array-&fn_flags = CG(interactive)?ZEND_ACC_INTERACTIVE:0;
op_array-&early_binding = -1;
op_array-&last_literal = 0;
op_array-&literals = NULL;
op_array-&run_time_cache = NULL;
op_array-&last_cache_slot = 0;
memset(op_array-&reserved, 0, 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.
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)+1, 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);
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 = 0;
zend_function *fbc = EX(function_state).
SAVE_OPLINE();
if (UNEXPECTED((fbc-&common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
if (UNEXPECTED((fbc-&common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
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) != 0)) {
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);
/* 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 = 1;
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=0;
zval **p = (zval**)EX(function_state).
ulong arg_count = opline-&extended_
while (arg_count&0) {
zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 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.
ret-&var.fcall_returned_reference = (fbc-&common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
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);
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_
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.
ret-&var.ptr_ptr = &ret-&var.
ret-&var.fcall_returned_reference = (fbc-&common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
if (EXPECTED(zend_execute == execute)) {
if (EXPECTED(EG(exception) == NULL)) {
ZEND_VM_ENTER();
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));
/* 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);
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);
Z_UNSET_ISREF_P(EX_T(opline-&result.var).var.ptr);
Z_SET_REFCOUNT_P(EX_T(opline-&result.var).var.ptr, 1);
EX_T(opline-&result.var).var.fcall_returned_reference = 0;
EX_T(opline-&result.var).var.ptr_ptr = &EX_T(opline-&result.var).var.
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)) == 1) {
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);
EG(return_value_ptr_ptr) = NULL;
EG(symtable_cache_ptr) = EG(symtable_cache) - 1;
EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1;
EG(no_extensions) = 0;
EG(function_table) = CG(function_table); //很重要
EG(class_table) = CG(class_table);
EG(in_execution) = 0;
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), 50, NULL, ZVAL_PTR_DTOR, 0);
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), 5, NULL, NULL, 0);
EG(ticks_count) = 0;
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), 1024);
EG(full_tables_cleanup) = 0;
#ifdef ZEND_WIN32
EG(timed_out) = 0;
EG(exception) = NULL;
EG(prev_exception) = NULL;
EG(scope) = NULL;
EG(called_scope) = NULL;
EG(This) = NULL;
EG(active_op_array) = NULL;
EG(active) = 1;
EG(start_op) = NULL;
struct _zend_vm_stack {
static zend_always_inline void zend_vm_stack_push(void *ptr TSRMLS_DC)
ZEND_VM_STACK_GROW_IF_NEEDED(1);
*(EG(argument_stack)-&top++) =
函数传递参数入栈的原理
传值,传引用
本文主要参考&风雪之域&Laruence的文章。转载:
我们刚学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:由相关的规范决定
阅读(...) 评论()}

我要回帖

更多关于 halcon 区域合并 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信