从源码区别include 与require 的不同

首先我们手动编译好能查看执行过程中对应的OPCODE的php,然后写如下代码


代码很简单,然后我们 ./php test.php  运行下这个文件,得到如下的OPCODE

从源码区别include 与require 的不同_第1张图片

遇上 include或者require,编译器会递归执行,我们可以看到,include,include_once,require,require_once 对应的Opcode都是 INCLUDE_OR_EVAL,而 INCLUDE_OR_EVAL对应的执行函数是 ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER

从源码区别include 与require 的不同_第2张图片


在 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();
}


最终会通过 compile_file() 来编译文件,该函数在 Zend/zend_language_scanner.c 文件中定义

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;
}

可以看到如果文件不存在,对require和include 抛出的异常类型是不一样的

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;

在 main.c 中 php_module_startup() 函数中有这样一条语句说明最终的 message handler 函数是 php_message_handler_for_zend

zuf.message_handler = php_message_handler_for_zend;

在main.c中找到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编译错误

你可能感兴趣的:(Php源码)