虽然网上面的文章很多,为了自己查阅方便,我还是“可耻的”的自己写一篇。
在手册中是这样描述的:
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.