• 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.

      

      

      

     

  • 相关阅读:
    数据结构一
    MVC5.0(一)
    异步多线程(六)lock锁
    异步多线程(五)多线程异常处理
    异步多线程(四)Task
    paypal payflow设置视频教程
    Java栈Stack知识点
    Java知识树梳理
    js定时器
    jdk环境变量配置改变不生效的问题
  • 原文地址:https://www.cnblogs.com/zoro/p/2240818.html
Copyright © 2020-2023  润新知