首先我们手动编译好能查看执行过程中对应的OPCODE的php,然后写如下代码
遇上 include或者require,编译器会递归执行,我们可以看到,include,include_once,require,require_once 对应的Opcode都是 INCLUDE_OR_EVAL,而 INCLUDE_OR_EVAL对应的执行函数是 ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER
在 zend_vm_execute.h 找到 ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER 的定义
static int ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_op_array *new_op_array=NULL;
zval *inc_filename;
zval *tmp_inc_filename = NULL;
zend_bool failure_retval=0;
SAVE_OPLINE();
inc_filename = opline->op1.zv;
if (inc_filename->type!=IS_STRING) {
MAKE_STD_ZVAL(tmp_inc_filename);
ZVAL_COPY_VALUE(tmp_inc_filename, inc_filename);
zval_copy_ctor(tmp_inc_filename);
convert_to_string(tmp_inc_filename);
inc_filename = tmp_inc_filename;
}
if (opline->extended_value != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
if (opline->extended_value == ZEND_INCLUDE_ONCE || opline->extended_value == ZEND_INCLUDE) {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
} else {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
}
} else {
switch (opline->extended_value) {
case ZEND_INCLUDE_ONCE:
case ZEND_REQUIRE_ONCE: {
zend_file_handle file_handle;
char *resolved_path;
resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), Z_STRLEN_P(inc_filename) TSRMLS_CC);
if (resolved_path) {
failure_retval = zend_hash_exists(&EG(included_files), resolved_path, strlen(resolved_path)+1);
} else {
resolved_path = Z_STRVAL_P(inc_filename);
}
if (failure_retval) {
/* do nothing, file already included */
} else if (SUCCESS == zend_stream_open(resolved_path, &file_handle TSRMLS_CC)) {
if (!file_handle.opened_path) {
file_handle.opened_path = estrdup(resolved_path);
}
if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1)==SUCCESS) {
new_op_array = zend_compile_file(&file_handle, (opline->extended_value==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE) TSRMLS_CC);//*********************************
zend_destroy_file_handle(&file_handle TSRMLS_CC);
} else {
zend_file_handle_dtor(&file_handle TSRMLS_CC);
failure_retval=1;
}
} else {
if (opline->extended_value == ZEND_INCLUDE_ONCE) {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
} else {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
}
}
if (resolved_path != Z_STRVAL_P(inc_filename)) {
efree(resolved_path);
}
}
break;
case ZEND_INCLUDE:
case ZEND_REQUIRE:
new_op_array = compile_filename(opline->extended_value, inc_filename TSRMLS_CC);//*********************************
break;
case ZEND_EVAL: {
char *eval_desc = zend_make_compiled_string_description("eval()'d code" TSRMLS_CC);
new_op_array = zend_compile_string(inc_filename, eval_desc TSRMLS_CC);
efree(eval_desc);
}
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
}
if (tmp_inc_filename) {
zval_ptr_dtor(&tmp_inc_filename);
}
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
} else if (EXPECTED(new_op_array != NULL)) {
EX(original_return_value) = EG(return_value_ptr_ptr);
EG(active_op_array) = new_op_array;
if (RETURN_VALUE_USED(opline)) {
EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr;
EG(return_value_ptr_ptr) = EX_T(opline->result.var).var.ptr_ptr;
} else {
EG(return_value_ptr_ptr) = NULL;
}
EX(function_state).function = (zend_function *) new_op_array;
EX(object) = NULL;
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
zend_execute(new_op_array TSRMLS_CC);
}
EX(function_state).function = (zend_function *) EX(op_array);
EG(opline_ptr) = &EX(opline);
EG(active_op_array) = EX(op_array);
EG(return_value_ptr_ptr) = EX(original_return_value);
destroy_op_array(new_op_array TSRMLS_CC);
efree(new_op_array);
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL TSRMLS_CC);
HANDLE_EXCEPTION();
}
} else if (RETURN_VALUE_USED(opline)) {
zval *retval;
ALLOC_ZVAL(retval);
ZVAL_BOOL(retval, failure_retval);
INIT_PZVAL(retval);
EX_T(opline->result.var).var.ptr = retval;
}
ZEND_VM_NEXT_OPCODE();
}
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;
INIT_PZVAL(&retval_znode.u.constant);
ZVAL_LONG(&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);
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);
zend_do_return(&retval_znode, 0 TSRMLS_CC);
CG(in_compilation) = original_in_compilation;
if (compiler_result != 0) { /* 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);
zend_release_labels(0 TSRMLS_CC);
} else {
efree(op_array);
retval = NULL;
}
}
zend_restore_lexical_state(&original_lex_state TSRMLS_CC);
return retval;
}
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
接着追源码
ZEND_API void zend_message_dispatcher(long message, const void *data TSRMLS_DC) /* {{{ */
{
if (zend_message_dispatcher_p) {
zend_message_dispatcher_p(message, data TSRMLS_CC);
}
}
zend_message_dispatcher_p = utility_functions->message_handler;
zuf.message_handler = php_message_handler_for_zend;
static void php_message_handler_for_zend(long message, const void *data TSRMLS_DC)
{
switch (message) {
case ZMSG_FAILED_INCLUDE_FOPEN://*********************************
php_error_docref("function.include" TSRMLS_CC, E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_pass
wd((char *) data), STR_PRINT(PG(include_path)));
break;
case ZMSG_FAILED_REQUIRE_FOPEN:
php_error_docref("function.require" TSRMLS_CC, E_COMPILE_ERROR, "Failed opening required '%s' (include_path='%s')", php_strip_url_pas
swd((char *) data), STR_PRINT(PG(include_path)));
break;
可以看到,两种不同的消息,对应Php两种不同级别的错误,include 会报一个E_WARNING,而require会报一个E_COMPILE_ERROR编译错误