1. 程式人生 > >PHP函式原始碼之VLD實現原理

PHP函式原始碼之VLD實現原理

vld功能的實現要依賴 Zend引擎初始化(zend_startup)的時候 zend_execute和zend_compile_file定義為函式指標的功勞了  

預設的

zend_execute指向execute

zend_compile_file指向compile_file

試想如果我們在實際編譯和執行之前將zend_execute和zend_compile_file重寫為其他的編譯和執行函式,我們不就可以乾點什麼事嗎

vld正是由此思路實現的 ,在每次請求初始化的鉤子函式(PHP_RINIT_FUNCTION)中,將zend_execute和zend_compile_file替換成自己的vld_execute和vld_compile_file,這兩個函式其實是對原始函式進行了封裝,添加了輸出opcode資訊的附加功能,因為引擎初始化是發生在模組請求初始化之前,而模組請求初始化又是在編譯和執行之前,這樣就實現了檢視opcode的功能。

基本思路瞭解清楚,下面我們就看程式碼實現部分:

原本zend_compile_file zend_compile_string還是要執行的

static zend_op_array* (*old_compile_file)(zend_file_handle* file_handle, int type TSRMLS_DC);
static zend_op_array* vld_compile_file(zend_file_handle*, int TSRMLS_DC);

static zend_op_array* (*old_compile_string)(zval *source_string, char *filename TSRMLS_DC);
static zend_op_array* vld_compile_string(zval *source_string, char *filename TSRMLS_DC);

#if PHP_VERSION_ID >= 50500
static void (*old_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
static void vld_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
#else
static void (*old_execute)(zend_op_array *op_array TSRMLS_DC);
static void vld_execute(zend_op_array *op_array TSRMLS_DC);
#endif
接下來是請求初始化的鉤子函式
PHP_RINIT_FUNCTION(vld)
{
	/* 記錄下原本的zend_compile_file, zend_compile_string, zend_execute_ex */
	old_compile_file = zend_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
	old_compile_string = zend_compile_string;
#endif
#if PHP_VERSION_ID >= 50500
	old_execute_ex = zend_execute_ex;
#else
	old_execute = zend_execute;
#endif

	/* 如果啟用vld掛鉤 */
	if (VLD_G(active)) {
		/* 使用 自定義的 vld_compile_file */
		zend_compile_file = vld_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
		/* 使用 自定義的 vld_compile_string */
		zend_compile_string = vld_compile_string;
#endif
		/* 是否執行php指令碼 */
		if (!VLD_G(execute)) {
#if PHP_VERSION_ID >= 50500
			zend_execute_ex = vld_execute_ex;
#else
			zend_execute = vld_execute;
#endif
		}
	}

	/* 是否輸出至檔案 */
	if (VLD_G(save_paths)) {
		char *filename;

		filename = malloc(strlen("paths.dot") + strlen(VLD_G(save_dir)) + 2);
		sprintf(filename, "%s/%s", VLD_G(save_dir), "paths.dot");

		VLD_G(path_dump_file) = fopen(filename, "w");
		free(filename);

		if (VLD_G(path_dump_file)) {
			fprintf(VLD_G(path_dump_file), "digraph {\n");
		}
	}
	return SUCCESS;
}
接下來 在執行指令碼 php_execute_script 中 呼叫 

vld_compile_file---------------->old_compile_file--------------->vld_dump_oparray

vld_compile_string------------->old_compile_file--------------->vld_dump_oparray

我們來看看vld_dump_oparray

void vld_dump_oparray(zend_op_array *opa TSRMLS_DC)
{
	unsigned int i;
	vld_set *set;
	vld_branch_info *branch_info;
	unsigned int base_address = (unsigned int)(zend_intptr_t)&(opa->opcodes[0]);

	set = vld_set_create(opa->last);
	branch_info = vld_branch_info_create(opa->last);

	if (VLD_G(dump_paths)) {
		vld_analyse_oparray(opa, set, branch_info TSRMLS_CC);
	}
	if (VLD_G(format)) {
		vld_printf (stderr, "filename:%s%s\n", VLD_G(col_sep), ZSTRING_VALUE(opa->filename));
		vld_printf (stderr, "function name:%s%s\n", VLD_G(col_sep), ZSTRING_VALUE(opa->function_name));
		vld_printf (stderr, "number of ops:%s%d\n", VLD_G(col_sep), opa->last);
	} else {
		vld_printf (stderr, "filename:       %s\n", ZSTRING_VALUE(opa->filename));
		vld_printf (stderr, "function name:  %s\n", ZSTRING_VALUE(opa->function_name));
		vld_printf (stderr, "number of ops:  %d\n", opa->last);
	}
#ifdef IS_CV /* PHP >= 5.1 */
	vld_printf (stderr, "compiled vars:  ");
	for (i = 0; i < opa->last_var; i++) {
		vld_printf (stderr, "!%d = $%s%s", i, OPARRAY_VAR_NAME(opa->vars[i]), ((i + 1) == opa->last_var) ? "\n" : ", ");
	}
	if (!opa->last_var) {
		vld_printf(stderr, "none\n");
	}
#endif
	if (VLD_G(format)) {
		vld_printf(stderr, "line%s# *%s%s%sop%sfetch%sext%sreturn%soperands\n",VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep));
	} else {
		vld_printf(stderr, "line     #* E I O op                           fetch          ext  return  operands\n");
		vld_printf(stderr, "-------------------------------------------------------------------------------------\n");
	}
	/* 遍歷opcode */
	for (i = 0; i < opa->last; i++) {
		vld_dump_op(i, opa->opcodes, base_address, vld_set_in(set, i), vld_set_in(branch_info->entry_points, i), vld_set_in(branch_info->starts, i), vld_set_in(branch_info->ends, i), opa TSRMLS_CC);
	}
	vld_printf(stderr, "\n");

	if (VLD_G(dump_paths)) {
		vld_branch_post_process(opa, branch_info);
		vld_branch_find_paths(branch_info);
		vld_branch_info_dump(opa, branch_info TSRMLS_CC);
	}

	vld_set_free(set);
	vld_branch_info_free(branch_info);
}

就這樣opcode 資訊就出來了

最後在請求結束後 要恢復原本的指標函式

PHP_RSHUTDOWN_FUNCTION(vld)
{
	zend_compile_file = old_compile_file;
#if PHP_VERSION_ID >= 50500
	zend_execute_ex   = old_execute_ex;
#else
	zend_execute      = old_execute;
#endif

	if (VLD_G(path_dump_file)) {
		fprintf(VLD_G(path_dump_file), "}\n");
		fclose(VLD_G(path_dump_file));
	}

	return SUCCESS;
}


相關推薦

PHP函式原始碼VLD實現原理

vld功能的實現要依賴 Zend引擎初始化(zend_startup)的時候 將zend_execute和zend_compile_file定義為函式指標的功勞了   預設的 zend_execute指向execute zend_compile_file指向comp

PHP函式原始碼SESSION實現機制

Session是以擴充套件的形式嵌入到PHP核心的,所以我們可以把Session當成擴充套件來看待。 一般擴充套件被載入到PHP會經過下面幾個過程 #define PHP_MINIT_FUNCTION      ZEND_MODULE_STARTU

Java多執行緒Condition實現原理原始碼分析(四)

章節概覽、 1、概述 上面的幾個章節我們基於lock(),unlock()方法為入口,深入分析了獨佔鎖的獲取和釋放。這個章節我們在此基礎上,進一步分析AQS是如何實現await,signal功能。其功能上和synchronize的wait,notify一樣。

Spring原始碼學習IOC實現原理(二)-ApplicationContext

一.Spring核心元件結構      總的來說Spring共有三個核心元件,分別為Core,Context,Bean.三大核心元件的協同工作主要表現在 :Bean是包裝我們應用程式自定義物件Object的,Object中存有資料,而Context就是為了這些資料存放提供一個生存環境,儲存各個 bean之間的

05 printf函式可變引數的實現原理彙編分析

如實現一個像printf函式格式的函式: test.c void myprintf(char *line, ...) // line指標變數是區域性變數,在棧裡分配空間 { printf(line); //呼叫printf時,r0存放字串地

原始碼閱讀:Java併發synchronized實現原理

執行緒安全是併發程式設計中的重要關注點,應該注意到的是,造成執行緒安全問題的主要誘因有兩點,一是存在共享資料(也稱臨界資源),二是存在多條執行緒共同操作共享資料。因此為了解決這個問題,我們可能需要這樣一個方案,當存在多個執行緒操作共享資料時,需要保證同一時刻有且

Java多執行緒ThreadPoolExecutor實現原理原始碼分析(五)

章節概覽、 1、概述 執行緒池的顧名思義,就是執行緒的一個集合。需要用到執行緒,從集合裡面取出即可。這樣設計主要的作用是優化執行緒的建立和銷燬而造成的資源浪費的情況。Java中的執行緒池的實現主要是JUC下面的ThreadPoolExecutor類完成的。下面

String原始碼閱讀contains實現原理

本文將對String部分原始碼進行閱讀分析的記錄。 contains 對String中的contains方法進行分析,瞭解其採用的是什麼演算法進行匹配。 //用於判斷源字串是否包含目標字元序列 CharSequence s public bo

Java多執行緒ReentrantLock實現原理原始碼分析(二)

章節概覽、 1、ReentrantLock概述 ReentrantLock字面含義是可重入的互斥鎖,實現了和synchronize關鍵字一樣的獨佔鎖功能。但是ReentrantLock使用的是自旋鎖,通過CAS硬體原語指令實現的輕量級的鎖,不會引起上下文切換

原始碼解析HashMap實現原理

目錄 二,栗子 一,寫在前面 在日常開發中,HashMap因其可以儲存鍵值對的特點經常被使用,僅僅知道如何使用HashMap是遠遠不夠的。以知其然知其所以然的鑽研態度,本篇文章將以圖文,原始碼的方式去解析HashMap的實現原理。 二,栗子

php會話(session)實現原理

tro 兩個 open() php.ini clas too span etime handle 先考慮如下問題: 禁用了cookie,session能否使用? 如何把session存儲數據庫/memcached/redis 如何實現一個嚴格的30分鐘過期的會話

Spring事務實現原始碼事務實現以及Connection的繫結與獲取

PlatformTransactionManager是spring事務的高階抽象,事務的實現需要藉助PlatformTransactionManager完成,該管理主要方法如下: 當我們在使用事務的時候,需要呼叫如下方法獲取一個事務狀態物件。 TransactionS

Java8ScheduledThreadPoolExecutor實現原理

ScheduledThreadPoolExecutor是一個可實現定時任務的執行緒池,ScheduledThreadPoolExecutor內的任務既可以在設定的時間到達時執行一次,也可以相隔固定時間週期執行。 ScheduledThreadPoolExecutor繼承自T

NIO ByteBuffer實現原理

前言 Java NIO 主要由下面3部分組成: Buffer Channel Selector 在傳統IO中,流是基於位元組的方式進行讀寫的。 在NIO中,使用通道(Channel)基於緩衝區資料塊的讀寫。 流是基於位元組一個一個的讀取和寫入。 通道是基於塊的方

深入理解Java併發synchronized實現原理

關聯文章: 本篇主要是對Java併發中synchronized關鍵字進行較為深入的探索,這些知識點結合博主對synchronized的個人理解以及相關的書籍的講解(在結尾參考資料),如有誤處,歡迎留言。 執行緒安全是併發程式

原始碼分析HashMap實現原理

HashMap 基於雜湊表的 Map 介面的實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證對映的順序,特別是它不保證該順序恆久不變。另外,HashMap是非

面試題---ArrayList實現原理

單列集合圖 1. ArrayList是一個動態陣列,實現了List<E>, RandomAccess, Cloneable, java.io.Serializable,並允許包括null在內的所有元素。 1.1,實現了RandomAccess介面標識著

Spring AopCglib實現原理詳解

Spring Aop實現對目標物件的代理,AOP的兩種實現方式:Jdk代理和Cglib代理。這兩種代理的區別在於,Jdk代理與目標類都會實現同一個介面,並且在代理類中會呼叫目標類中被代理的方法,呼叫者實際呼叫的則是代理類的方法,通過這種方式我們就可以在代理類中織入切面邏輯;Jdk代理存在的問題在於目標類被代

虛擬函式的作用和實現原理

2、底層實現原理:先來看看C++物件模型 這個模型從結合上面2中模型的特點,並對記憶體存取和空間進行了優化。在此模型中,non static 資料成員被放置到物件內部,static資料成員, static and nonstatic 函式成員均被放到物件之外。對於虛擬函式的支援則分兩步完成:

深入理解 Java 併發 synchronized 實現原理

關聯文章深入理解Java型別資訊(Class物件)與反射機制深入理解Java列舉型別(enum)深入理解Java註解型別(@Annotation)深入理解Java併發之synchronized實現原理本篇主要是對Java併發中synchronized關鍵字進行較為深入的探索,這些知識點結合博主對synchro