include 和 require 比较

  虽然网上面的文章很多,为了自己查阅方便,我还是“可耻的”的自己写一篇。

  在手册中是这样描述的:  

  include() 语句包含并运行指定文件。

  以下文档也适用于 require()。这两种结构除了在如何处理失败之外完全一样。include() 产生一个警告require() 则导致一个致命错误。换句话说,如果想在遇到丢失文件时停止处理页面就用 require()include() 就不是这样,脚本会继续运行。同时也要确认设置了合适的 include_path。注意在 PHP 4.3.5 之前,包含文件中的语法错误不会导致程序停止,但从此版本之后会。

  在zend_language_scanner.l中require和require_once 会有如下:  

<ST_IN_SCRIPTING>"require" {
	return T_REQUIRE;
}

<ST_IN_SCRIPTING>"require_once" {
	return T_REQUIRE_ONCE;
}

<ST_IN_SCRIPTING>"include" {
	return T_INCLUDE;
}

<ST_IN_SCRIPTING>"include_once" {
	return T_INCLUDE_ONCE;
}

   随后,在zend_language_scanner.c中有:

  

internal_functions_in_yacc:
		T_ISSET '(' isset_variables ')' { $$ = $3; }
	|	T_EMPTY '(' variable ')'	{ zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
	|	T_INCLUDE expr 			{ zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
	|	T_INCLUDE_ONCE expr 	{ zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
	|	T_EVAL '(' expr ')' 	{ zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
	|	T_REQUIRE expr			{ zend_do_include_or_eval(ZEND_REQUIRE, &$$, &$2 TSRMLS_CC); }
	|	T_REQUIRE_ONCE expr		{ zend_do_include_or_eval(ZEND_REQUIRE_ONCE, &$$, &$2 TSRMLS_CC); }
;

   四种包含函数都是用同一个函数处理,只是参数不一样:  

#define ZEND_EVAL				(1<<0)
#define ZEND_INCLUDE			(1<<1)
#define ZEND_INCLUDE_ONCE		(1<<2)
#define ZEND_REQUIRE			(1<<3)
#define ZEND_REQUIRE_ONCE		(1<<4)

   在函数zend_do_include_or_eval中会将opcode 赋值为 ZEND_INCLUDE_OR_EVAL:

  

void zend_do_include_or_eval(int type, znode *result, const znode *op1 TSRMLS_DC) /* {{{ */
{
	zend_do_extended_fcall_begin(TSRMLS_C);
	{
		zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);

		opline->opcode = ZEND_INCLUDE_OR_EVAL;
		opline->result.op_type = IS_VAR;
		opline->result.u.var = get_temporary_variable(CG(active_op_array));
		opline->op1 = *op1;
		SET_UNUSED(opline->op2);
		Z_LVAL(opline->op2.u.constant) = type;
		*result = opline->result;
	}
	zend_do_extended_fcall_end(TSRMLS_C);
}

    在处理opcode 的时候,在几个不同的handler 中都会有如下关键代码:

  

switch (Z_LVAL(opline->op2.u.constant)) {
		case ZEND_INCLUDE_ONCE:
		case ZEND_REQUIRE_ONCE: {
				zend_file_handle file_handle;
。。。
                new_op_array = compile_filename(Z_LVAL(opline->op2.u.constant), inc_filename TSRMLS_CC);


。。。。。

   在函数compile_filename中会调用zend_compile_file函数,而zend_compile_file是一个函数指针,apache启动时,会将PHP模块挂钩启动,同时启动zend,在zend_startup中会将zend_compile_file = compile_file;进行赋值。

  在zend_language_scanner.c或者说是zend_language_scanner.l文件中,compile_file函数关键代码:  

	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;

   为扫描文件打开失败的时候,会进入上面的逻辑,type==ZEND_REQUIR的时候,最显著的区别就是会zend_bailout();

  在zend_message_dispatcher函数中,就调用zend_message_dispatcher_p函数,同样的zend_message_dispatcher_p也是一个函数指针,在zend_startup的时候,会将初始化功能函数的一个成员函数赋值给他zend_message_dispatcher_p = utility_functions->message_handler;往前追踪的话在php_module_startup中:  

php_output_startup();

	zuf.error_function = php_error_cb;
	zuf.printf_function = php_printf;
	zuf.write_function = php_body_write_wrapper;
	zuf.fopen_function = php_fopen_wrapper_for_zend;
	zuf.message_handler = php_message_handler_for_zend;
	zuf.block_interruptions = sapi_module.block_interruptions;
	zuf.unblock_interruptions = sapi_module.unblock_interruptions;
	zuf.get_configuration_directive = php_get_configuration_directive_for_zend;
	zuf.ticks_function = php_run_ticks;
	zuf.on_timeout = php_on_timeout;
	zuf.stream_open_function = php_stream_open_for_zend;
	zuf.vspprintf_function = vspprintf;
	zuf.getenv_function = sapi_getenv;
	zuf.resolve_path_function = php_resolve_path_for_zend;
	zend_startup(&zuf, NULL TSRMLS_CC);

   最终调用的是php_message_handler_for_zend函数,其中错误就是我们经常会见到的:  

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_passwd((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_passwd((char *) data), STR_PRINT(PG(include_path)));
			break;
		case ZMSG_FAILED_HIGHLIGHT_FOPEN:
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd((char *) data));
			break;

   一个是E_WARNING,而require类型的是编译时错误E_COMPILE_ERROR。

#define E_ERROR				(1<<0L)
#define E_WARNING			(1<<1L)
#define E_PARSE				(1<<2L)
#define E_NOTICE			(1<<3L)
#define E_CORE_ERROR		(1<<4L)
#define E_CORE_WARNING		(1<<5L)
#define E_COMPILE_ERROR		(1<<6L)
#define E_COMPILE_WARNING	(1<<7L)
#define E_USER_ERROR		(1<<8L)
#define E_USER_WARNING		(1<<9L)
#define E_USER_NOTICE		(1<<10L)
#define E_STRICT			(1<<11L)
#define E_RECOVERABLE_ERROR	(1<<12L)
#define E_DEPRECATED		(1<<13L)
#define E_USER_DEPRECATED	(1<<14L)

   OK,OVER.

  

  

  

 

原文地址:https://www.cnblogs.com/zoro/p/2240818.html