參考文章 http://rapheal.sinaapp.com/2013/11/20/php_zend_hello_world/php
http://www.douban.com/note/337885681/html
5.3 與之前版本的不一樣 http://my.oschina.net/mickelfeng/blog/201382node
php添加新語法 http://www.it165.net/pro/html/201208/3477.htmlgit
http://nikic.github.io/2012/07/27/How-to-add-new-syntactic-features-to-PHP.htmlgithub
http://zsong.me/posts/1api
http://www.laruence.com/2009/02/21/662.html數組
http://www.verydemo.com/cm.jsp?c=14&u=shen-ru-li-jie-php-yuan-li-zhi數據結構
http://bbs.phpchina.com/thread-100825-1-1.htmlapp
static http://www.nowamagic.net/librarys/veda/detail/1402jsp
cli方式運行php腳本
$PHPSRC/sapi/cli/php_cli.c中的do_cli函數裏邊接收了命令行的參數輸入
zend_execute_scripts() $PHPSRC/main/main.c裏邊有定義
EG(active_op_array) = zend_compile_file(file_handle, type TSRMLS_CC);
zend_execute(EG(active_op_array) TSRMLS_CC);
經過zend_compile_file(實際上爲compile_file()<定義在Zend/zend_language_scanner.c的555行>)把腳本文件編譯出opcode,
ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...) /* {{{ */ { va_list files; int i; zend_file_handle *file_handle; zend_op_array *orig_op_array = EG(active_op_array); zval **orig_retval_ptr_ptr = EG(return_value_ptr_ptr); long orig_interactive = CG(interactive); va_start(files, file_count); for (i = 0; i < file_count; i++) { file_handle = va_arg(files, zend_file_handle *); if (!file_handle) { continue; } if (orig_interactive) { if (file_handle->filename[0] != '-' || file_handle->filename[1]) { CG(interactive) = 0; } else { CG(interactive) = 1; } } EG(active_op_array) = zend_compile_file(file_handle, type TSRMLS_CC); if (file_handle->opened_path) { int dummy = 1; zend_hash_add(&EG(included_files), file_handle->opened_path, strlen(file_handle->opened_path) + 1, (void *)&dummy, sizeof(int), NULL); } zend_destroy_file_handle(file_handle TSRMLS_CC); if (EG(active_op_array)) { EG(return_value_ptr_ptr) = retval ? retval : NULL; zend_execute(EG(active_op_array) TSRMLS_CC); zend_exception_restore(TSRMLS_C); if (EG(exception)) { if (EG(user_exception_handler)) { zval *orig_user_exception_handler; zval **params[1], *retval2, *old_exception; old_exception = EG(exception); EG(exception) = NULL; params[0] = &old_exception; orig_user_exception_handler = EG(user_exception_handler); if (call_user_function_ex(CG(function_table), NULL, orig_user_exception_handler, &retval2, 1, params, 1, NULL TSRMLS_CC) == SUCCESS) { if (retval2 != NULL) { zval_ptr_dtor(&retval2); } if (EG(exception)) { zval_ptr_dtor(&EG(exception)); EG(exception) = NULL; } zval_ptr_dtor(&old_exception); } else { EG(exception) = old_exception; zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } } else { zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } } destroy_op_array(EG(active_op_array) TSRMLS_CC); efree(EG(active_op_array)); } else if (type==ZEND_REQUIRE) { va_end(files); EG(active_op_array) = orig_op_array; EG(return_value_ptr_ptr) = orig_retval_ptr_ptr; CG(interactive) = orig_interactive; return FAILURE; } } va_end(files); EG(active_op_array) = orig_op_array; EG(return_value_ptr_ptr) = orig_retval_ptr_ptr; CG(interactive) = orig_interactive; return SUCCESS; } zend_compile_file = compile_file;
ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) { zend_lex_state original_lex_state; zend_op_array *op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); zend_op_array *original_active_op_array = CG(active_op_array); zend_op_array *retval=NULL; int compiler_result; zend_bool compilation_successful=0; znode retval_znode; zend_bool original_in_compilation = CG(in_compilation); retval_znode.op_type = IS_CONST; retval_znode.u.constant.type = IS_LONG; retval_znode.u.constant.value.lval = 1; Z_UNSET_ISREF(retval_znode.u.constant); Z_SET_REFCOUNT(retval_znode.u.constant, 1); zend_save_lexical_state(&original_lex_state TSRMLS_CC); retval = op_array; /* success oriented */
if (open_file_for_scanning(file_handle TSRMLS_CC)==FAILURE) { if (type==ZEND_REQUIRE) { zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); zend_bailout(); } else { zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); } compilation_successful=0; } else { init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); //初始化op_array CG(in_compilation) = 1; CG(active_op_array) = op_array; zend_stack_push(&CG(context_stack), (void *) &CG(context), sizeof(CG(context))); zend_init_compiler_context(TSRMLS_C); compiler_result = zendparse(TSRMLS_C);//對於非函數,都是設置opline的各個屬性,對於函數,要額外分配一個op_array zend_do_return(&retval_znode, 0 TSRMLS_CC); CG(in_compilation) = original_in_compilation; if (compiler_result==1) { /* parser error */ zend_bailout(); } compilation_successful=1; } if (retval) { CG(active_op_array) = original_active_op_array; if (compilation_successful) { pass_two(op_array TSRMLS_CC);//循環遍歷op_array中的opcodes,設置每個opline中的屬性hanlder zend_release_labels(TSRMLS_C); } else { efree(op_array); retval = NULL; } } zend_restore_lexical_state(&original_lex_state TSRMLS_CC); return retval; }
ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { zend_execute_data *execute_data; zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); if (EG(exception)) { return; } EG(in_execution) = 1; zend_vm_enter: /* Initialize execute_data */ execute_data = (zend_execute_data *)zend_vm_stack_alloc( ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2))); EX(fbc) = NULL; EX(called_scope) = NULL; EX(object) = NULL; EX(old_error_reporting) = NULL; EX(op_array) = op_array; EX(symbol_table) = EG(active_symbol_table); EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = execute_data; EX(nested) = nested; nested = 1; if (op_array->start_op) { ZEND_VM_SET_OPCODE(op_array->start_op); } else { ZEND_VM_SET_OPCODE(op_array->opcodes); } if (op_array->this_var != -1 && EG(This)) { Z_ADDREF_P(EG(This)); /* For $this pointer */
if (!EG(active_symbol_table)) { EX(CVs)[op_array->this_var] = (zval**)EX(CVs) + (op_array->last_var + op_array->this_var); *EX(CVs)[op_array->this_var] = EG(This); } else { if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX(CVs)[op_array->this_var])==FAILURE) { Z_DELREF_P(EG(This)); } } } EG(opline_ptr) = &EX(opline); EX(function_state).function = (zend_function *) op_array; EX(function_state).arguments = NULL; while (1) { int ret; #ifdef ZEND_WIN32 if (EG(timed_out)) { zend_timeout(0); } #endif
if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) { switch (ret) { case 1: EG(in_execution) = original_in_execution; return; case 2: op_array = EG(active_op_array); goto zend_vm_enter; case 3: execute_data = EG(current_execute_data); default: break; } } } zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); }
zendparse (LALR1 將代碼分隔成token,再解析後存放在opcode的數組中)
pass_two解析opcode的每一行,將其handler綁定到op上
complie以後會獲得一個op_array,在execute以前建立一個zend_execute_data的數據結構
struct _zend_execute_data {
struct _zend_op *opline;
zend_function_state function_state;
zend_op_array *op_array;
zval *object;
HashTable *symbol_table;
struct _zend_execute_data *prev_execute_data;
zval *old_error_reporting;
zend_bool nested;
zval **original_return_value;
zend_class_entry *current_scope;
zend_class_entry *current_called_scope;
zval *current_this;
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
call_slot *call_slots;
call_slot *call;
};
在zend_execute中最重要的一句
ret = OPLINE->handler(execute_data TSRMLS_CC)
調用在op上綁定的handler,完成對opcode的執行
//zend_opcode.c
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 0;
}
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, 1 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 0;
}
//zend_vm_execute.h
ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}