1. 程式人生 > >從原始碼區別include 與require 的不同

從原始碼區別include 與require 的不同

首先我們手動編譯好能檢視執行過程中對應的OPCODE的php,然後寫如下程式碼

<?php
include("t1.php");
include_once("t1.php");
require("t1.php");
require_once("t1.php");

echo "succ \n";

程式碼很簡單,然後我們 ./php test.php  執行下這個檔案,得到如下的OPCODE


遇上 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);<span style="font-family: Arial, Helvetica, sans-serif;">//*********************************</span>

							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);<span style="font-family: Arial, Helvetica, sans-serif;">//*********************************</span>

				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);<span style="font-family: Arial, Helvetica, sans-serif;">//*********************************</span>

            zend_bailout();
        } else {
            zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);<span style="font-family: Arial, Helvetica, sans-serif;">//*********************************</span>

        }
        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:<span style="font-family: Arial, Helvetica, sans-serif;">//*********************************</span>
            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 <span style="color:#ff0000;">ZMSG_FAILED_REQUIRE_FOPEN</span>:
            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編譯錯誤

相關推薦

原始碼區別include require不同

首先我們手動編譯好能檢視執行過程中對應的OPCODE的php,然後寫如下程式碼 <?php include("t1.php"); include_once("t1.php"); require("t1.php"); require_once("t1.php");

PHP中include()require()的區別說明

靜態頁 檢查 服務器 code 中斷 完全 變量 方式 返回值 require 的使用方法如 require("MyRequireFile.php"); 。這個函數通常放在 PHP 程序的最前面,PHP 程序在執行前,就會先讀入 require 所指定引入的文件,使它變成

includerequire區別

1、require 這個函式通常放在 PHP 程式的最前面,PHP 程式在執行前,就會先讀入 require 所指定引入的檔案,使它變成 PHP 程式網頁的一部份。常用的函式,亦可以這個方法將它引入網頁中。 2、include 這個函式一般是放在流程控制的處理部分中。PHP

終於弄明白了include()require()的聯絡區別

----------------------------------  1分多鐘,銀行卡里的3120元錢被人分4次從網上銀行取走,隨後又多次轉賬,讓你查詢不到這筆錢的去處……這些天來,市民向小姐一直在為自己網上銀行被盜一事煩心。在加入“網上工行受害者聯盟”後,她又發現該QQ群中與有著和自己同樣遭遇的人竟多達7

[PHP]PHP中include()require()的區別

今日看php文件,看到include和require區別的講解,感覺這對於初學者是一個很容易迷惑的地方,在此記錄下來,以便自己查閱,也希望對各位php朋友有所幫助。引用檔案的方法有兩種:require 及 include。兩種方式提供不同的使用彈性。 require 的使用方法如 require("MyReq

PHP中includerequire語句的區別

warning mar 處理 生成 區別 war 插入 php 通過 通過 include 或 require 語句,可以將 PHP 文件的內容插入另一個 PHP 文件(在服務器執行它之前)。 include 和 require 語句是相同的,除了錯誤處理方面:

PHP中includerequire區別詳解

for in repl 這不 語句 沒有 16px targe size something 1、概要    require()語句的性能與include()相類似,都是包括並運行指定文件。不同之處在於:對include()語句來說,在執行文件時每次都要進行讀取和評估;

php中includerequire區別(整理)

服務 條件 工作方式 作用 就會 php程序 兩個 發生 ref require 的使用方法如 require("MyRequireFile.php"); 。這個函數通常放在 PHP 程序的最前面,PHP 程序在執行前,就會先讀入 require 所指定引入的文

PHP 中includerequire區別

require 文件的 沒有 流程控制 nbsp 執行 文件 php文件 函數 PHP中引用文件的方法有兩種:require和include 他們的用途是完全一樣的。 require:require("########");這個函數一般放在PHP文件的最前面,程序在執行前就

includerequire區別

開源 加載失敗 流程 一次 class tail pap details 慢慢 include和require的區別 準備在一個PHP的開源程序上做點二次開發,借此機會想對PHP進行一下系統的學習。曾經也大概的了解過一點PHP,但因為工作中一直沒有用到這門語言,所以

PHP學習筆記 - includerequire、include_once 和 require_once的區別

重要 one 是的 進行 允許 pan 4.0 fop 其它 1.include:會將指定的檔案讀入並且執行裏面的程序。 被導入的檔案中的程序代碼都會被執行,而且這些程序在執行的時候會擁有和源文件中呼叫到 include() 函數的位置相同的變量範圍(vari

web---JSP中動態include靜態include區別

1. 動態include <jsp:include page="目標jsp"> 它的原理是使用了 request.getRequestDispatcher(目標jsp).include(request,response) 來實現頁面包含,其本質是將 源jsp 和 目標

PHP包含檔案函式includerequire區別

例如下面的程式碼: 複製程式碼 程式碼如下: include('hello.php'); echo 'include test final!';//include報錯,但是會繼續執行,顯示:include test final! require('hello.php'); echo 'r

importrequire區別學習筆記

文章摘抄 import和require的區別 問題來源 最近想深入學習webpack,然後想一步一步構建一個大的工程,希望把所有的知識點全部覆蓋到,然後在寫一個最簡單的打包功能的時候,發現編譯總是報錯,最終定位到原因是 export 語法錯誤,沒有搞明白requir

php中include()和require()的區別

1.引用檔案方式     對 include()來說,在include()執行時檔案每次都要進行讀取和評估;而對於require()來說,檔案只處理一次(實際上,檔案內容替換 了require()語句。這就意味著如果有包含這些指令之一的程式碼和可能執行多次的程式碼,則使用r

PHP函式include include_once require和require_once的區別

瞭解下include、include_once、require和require_once這4個函式: include函式:會將指定的檔案讀入並且執行裡面的程式; require函式:會將目標檔案的內容讀入,並且把自己本身代換成這些讀入的內容; include_once

php 中使用includerequire、include_once、require_once的區別

原文:https://blog.csdn.net/weixin_40910753/article/details/79398279  1.include:使用include引用外部檔案時,只有程式碼執行到include程式碼段時,呼叫的外部檔案才會被引用並讀取,當引用的檔案

HTTPHTTPS不同請求的區別

1. 我把所有的URL /與程式碼。我這個來自傑夫・德沃爾現在,它的精細工作:function request(const AUrl, AData: AnsiString; blnSSL: Boolean = True): AnsiString; var aBuffer

【原創】原始碼剖析IO流(一)輸入流輸出流--轉載請註明出處

InputStream與OutPutStream兩個抽象類,是所有的流的基礎,首先來看這兩個流的API InputStream: public abstract int read() throws IOException; 從輸入流中讀取資料的下個位元組

freemarker中includeimport的區別

問題顯示: 在inc1.ftl與inc2.ftl中的內容分別是: <#assign username="劉德華">與<#assign username="張學友"> 接著我在hello.ftl模版中用include將inc1.ftl包含進來 &