成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

PHP之鉤子

創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、硯山網(wǎng)絡(luò)推廣、成都微信小程序、硯山網(wǎng)絡(luò)營銷、硯山企業(yè)策劃、硯山品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們大的嘉獎;創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供硯山建站搭建服務(wù),24小時服務(wù)熱線:18982081108,官方網(wǎng)址:jinyejixie.comPHP 提供的鉤子

PHP 和 Zend Engine 為擴(kuò)展提供了許多不同的鉤子,這些擴(kuò)展允許擴(kuò)展開發(fā)人員以 PHP userland 無法提供的方式控制 PHP 運(yùn)行時。

本章將展示各種鉤子和從擴(kuò)展鉤子到它們的常見用例。

鉤子到 PHP 功能的一般模式是 PHP 核心提供的擴(kuò)展覆蓋函數(shù)指針。然后擴(kuò)展函數(shù)通常執(zhí)行自己的工作并調(diào)用原始 PHP 核心函數(shù)。使用此模式,不同的擴(kuò)展可以覆蓋同一個鉤子而不會導(dǎo)致沖突。

掛鉤到函數(shù)的執(zhí)行

userland和內(nèi)部函數(shù)的執(zhí)行由Zend引擎中的兩個函數(shù)處理,您可以用自己的實(shí)現(xiàn)替換這兩個函數(shù)。覆蓋此鉤子的擴(kuò)展的主要用例是通用函數(shù)級評測、調(diào)試和面向方面的編程。

鉤子在 Zend/zend_execute.h中定義:

ZEND_API extern void (*zend_execute_ex)(zend_execute_data *execute_data);ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);

如果要覆蓋這些函數(shù)指針,則必須在 Minit 中執(zhí)行此操作,因?yàn)?Zend Engine 中的其他決策是根據(jù)指針是否被覆蓋這一事實(shí)提前做出的。

覆蓋的通常模式是這樣的:

static void (*original_zend_execute_ex) (zend_execute_data *execute_data);static void (*original_zend_execute_internal) (zend_execute_data *execute_data, zval *return_value);void my_execute_internal(zend_execute_data *execute_data, zval *return_value);void my_execute_ex (zend_execute_data *execute_data);PHP_MINIT_FUNCTION(my_extension){
    REGISTER_INI_ENTRIES();

    original_zend_execute_internal = zend_execute_internal;
    zend_execute_internal = my_execute_internal;

    original_zend_execute_ex = zend_execute_ex;
    zend_execute_ex = my_execute_ex;

    return SUCCESS;}PHP_MSHUTDOWN_FUNCTION(my_extension){
    zend_execute_internal = original_zend_execute_internal;
    zend_execute_ex = original_zend_execute_ex;

    return SUCCESS;}

覆蓋 zend_execute_ex的一個缺點(diǎn)是它將 Zend Virtual Machine 運(yùn)行時的行為更改為使用遞歸,而不是在不離開解釋器循環(huán)的情況下處理調(diào)用。此外,沒有覆蓋zend_execute_ex的 PHP 引擎也可以生成更優(yōu)化的函數(shù)調(diào)用操作碼。

這些掛鉤對性能非常敏感,具體取決于原始函數(shù)封裝代碼的復(fù)雜性。

覆蓋內(nèi)部功能

在覆蓋執(zhí)行鉤子時,擴(kuò)展可以記錄每個函數(shù)調(diào)用,你還可以覆蓋用戶域,核心和擴(kuò)展函數(shù)(和方法)的各個函數(shù)指針。如果擴(kuò)展僅需要訪問特定的內(nèi)部函數(shù)調(diào)用,則具有更好的性能特征。

#if PHP_VERSION_ID < 70200typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);#endif
zif_handler original_handler_var_dump;ZEND_NAMED_FUNCTION(my_overwrite_var_dump){
    // 如果我們想調(diào)用原始函數(shù)
    original_handler_var_dump(INTERNAL_FUNCTION_PARAM_PASSTHRU);}PHP_MINIT_FUNCTION(my_extension){
    zend_function *original;

    original = zend_hash_str_find_ptr(EG(function_table), "var_dump", sizeof("var_dump")-1);

    if (original != NULL) {
        original_handler_var_dump = original->internal_function.handler;
        original->internal_function.handler = my_overwrite_var_dump;
    }}

覆蓋類方法時,可以在 zend_class_entry上找到函數(shù)表:

zend_class_entry *ce = zend_hash_str_find_ptr(CG(class_table), "PDO", sizeof("PDO")-1);if (ce != NULL) {
    original = zend_hash_str_find_ptr(&ce->function_table, "exec", sizeof("exec")-1);

    if (original != NULL) {
        original_handler_pdo_exec = original->internal_function.handler;
        original->internal_function.handler = my_overwrite_pdo_exec;
    }}
修改抽象語法樹(AST)

當(dāng) PHP 7編譯 PHP 代碼時,它會先將其轉(zhuǎn)換為抽象語法樹(AST),然后最終生成持久存儲在 Opcache 中的操作碼。zend_ast_process鉤子會被每個已編譯的腳本調(diào)用,并允許你在解析和創(chuàng)建 AST 之后修改 AST。

這是要使用的最復(fù)雜的鉤子之一,因?yàn)樗枰耆私?AST。在此處創(chuàng)建無效的 AST 可能會導(dǎo)致異常行為或崩潰。

最好看看使用此鉤子的示例擴(kuò)展:

Google Stackdriver PHP調(diào)試器擴(kuò)展基于 Stackdriver 的帶有 AST 的概念驗(yàn)證器熟悉腳本/文件編譯

每當(dāng)用戶腳本調(diào)用include/require或其對應(yīng)的include_once/require_once時,PHP內(nèi)核都會在指針zend_compile_file處調(diào)用該函數(shù)處理此請求。參數(shù)是文件句柄,結(jié)果是zend_op_array。

zend_op_array * my_extension_compile_file(zend_file_handle * file_handle,int類型);

PHP核心中有兩個擴(kuò)展實(shí)現(xiàn)了此掛鉤:dtrace和opcache。

-如果您使用環(huán)境變量USE_ZEND_DTRACE啟動PHP腳本并使用dtrace支持編譯了PHP,則dtrace_compile_file用于Zend / zend_dtrace.c
-Opcache將操作數(shù)組存儲在共享內(nèi)存中以獲得更好的性能,因此,每當(dāng)腳本被編譯時,其最終的操作數(shù)組都會從緩存中得到服務(wù),而不是重新編譯。您可以在ext / opcache / ZendAccelerator.c中找到此實(shí)現(xiàn)。
-名為compile_file的默認(rèn)實(shí)現(xiàn)是Zend / zend_language_scanner.l中掃描程序代碼的一部分。

實(shí)施此掛鉤的用例是Opcode Accelerating,PHP代碼加密/解密,調(diào)試或概要分析。

您可以隨時在執(zhí)行PHP進(jìn)程時替換該掛鉤,并且替換后編譯的所有PHP腳本都將由該掛鉤的實(shí)現(xiàn)處理。

始終調(diào)用原始函數(shù)指針非常重要,否則PHP將無法再編譯腳本,并且Opcache將不再起作用。

此處的擴(kuò)展覆蓋順序也很重要,因?yàn)槟枰朗且贠pcache之前還是之后注冊鉤子,因?yàn)镺pcache如果在其共享內(nèi)存緩存中找到操作碼數(shù)組條目,則不會調(diào)用原始函數(shù)指針。 Opcache將其鉤子注冊為啟動后鉤子,該鉤子在擴(kuò)展的minit階段之后運(yùn)行,因此默認(rèn)情況下,緩存腳本時將不再調(diào)用該鉤子。

調(diào)用錯誤處理程序時的通知

與PHP用戶區(qū)set_error_handler()函數(shù)類似,擴(kuò)展可以通過實(shí)現(xiàn)zend_error_cb鉤子將自身注冊為錯誤處理程序:

ZEND_API void(* zend_error_cb)(int類型,const char * error_filename,const uint32_t error_lineno,const char * format,va_list args);

type變量對應(yīng)于E _ *錯誤常量,該常量在PHP用戶區(qū)中也可用。

PHP核心和用戶態(tài)錯誤處理程序之間的關(guān)系很復(fù)雜:

1.如果未注冊任何用戶級錯誤處理程序,則始終調(diào)用zend_error_cb
2.如果注冊了userland錯誤處理程序,則對于E_ERROR,E_PARSEE_CORE_ERROR,E_CORE_WARNINGE_COMPILE_ERROR的所有錯誤E_COMPILE_WARNING始終調(diào)用zend_error_cb掛鉤。
3.對于所有其他錯誤,僅在用戶態(tài)處理程序失敗或返回false時調(diào)用zend_error_cb。

另外,由于Xdebug自身復(fù)雜的實(shí)現(xiàn),它以不調(diào)用以前注冊的內(nèi)部處理程序的方式覆蓋錯誤處理程序。

因此,覆蓋此掛鉤不是很可靠。

再次覆蓋應(yīng)該以尊重原始處理程序的方式進(jìn)行,除非您想完全替換它:

void(* original_zend_error_cb)(int類型,const char * error_filename,const uint error_lineno,const char * format,va_list args);void my_error_cb(int類型,const char * error_filename,const uint error_lineno,const char * format,va_list args){
    //我的特殊錯誤處理

    original_zend_error_cb(type,error_filename,error_lineno,format,args);}PHP_MINIT_FUNCTION(my_extension){
    original_zend_error_cb = zend_error_cb;
    zend_error_cb = my_error_cb;

    return SUCCESS;}PHP_MSHUTDOWN(my_extension){
    zend_error_cb = original_zend_error_cb;}

該掛鉤主要用于為異常跟蹤或應(yīng)用程序性能管理軟件實(shí)施集中式異常跟蹤。

引發(fā)異常時的通知

每當(dāng)PHP Core或Userland代碼引發(fā)異常時,都會調(diào)用zend_throw_exception_hook并將異常作為參數(shù)。

這個鉤子的簽名非常簡單:

void my_throw_exception_hook(zval * exception){
    if(original_zend_throw_exception_hook!= NULL){
        original_zend_throw_exception_hook(exception);
    }}

該掛鉤沒有默認(rèn)實(shí)現(xiàn),如果未被擴(kuò)展覆蓋,則指向NULL。

static void(* original_zend_throw_exception_hook)(zval * ex);void my_throw_exception_hook(zval * exception);PHP_MINIT_FUNCTION(my_extension){
    original_zend_throw_exception_hook = zend_throw_exception_hook;
    zend_throw_exception_hook = my_throw_exception_hook;

    return SUCCESS;}

如果實(shí)現(xiàn)此掛鉤,請注意無論是否捕獲到異常,都會調(diào)用此掛鉤。將異常臨時存儲在此處,然后將其與錯誤處理程序掛鉤的實(shí)現(xiàn)結(jié)合起來以檢查異常是否未被捕獲并導(dǎo)致腳本停止,仍然有用。

實(shí)現(xiàn)此掛鉤的用例包括調(diào)試,日志記錄和異常跟蹤。

掛接到eval()

PHPeval不是內(nèi)部函數(shù),而是一種特殊的語言構(gòu)造。因此,您無法通過zend_execute_internal或通過覆蓋其函數(shù)指針來連接它。

掛鉤到eval的用例并不多,您可以將其用于概要分析或出于安全目的。如果更改其行為,請注意可能需要評估其他擴(kuò)展名。一個示例是Xdebug,它使用它執(zhí)行斷點(diǎn)條件。

extern ZEND_API zend_op_array *(* zend_compile_string)(zval * source_string,char * filename);
掛入垃圾收集器

當(dāng)可收集對象的數(shù)量達(dá)到一定閾值時,引擎本身會調(diào)用gc_collect_cycles()或隱式地觸發(fā)PHP垃圾收集器。

為了使您了解垃圾收集器的工作方式或分析其性能,可以覆蓋執(zhí)行垃圾收集操作的函數(shù)指針掛鉤。從理論上講,您可以在此處實(shí)現(xiàn)自己的垃圾收集算法,但是如果有必要對引擎進(jìn)行其他更改,則這可能實(shí)際上并不可行。

int(* original_gc_collect_cycles)(無效);int my_gc_collect_cycles(無效){
    original_gc_collect_cycles();}PHP_MINIT_FUNCTION(my_extension){
    original_gc_collect_cycles = gc_collect_cycles;
    gc_collect_cycles = my_gc_collect_cycles;

    return SUCCESS;}
覆蓋中斷處理程序

當(dāng)執(zhí)行器全局EG(vm_interrupt)設(shè)置為1時,將調(diào)用一次中斷處理程序。在執(zhí)行用戶域代碼期間,將在常規(guī)檢查點(diǎn)對它進(jìn)行檢查。引擎使用此掛鉤通過信號處理程序?qū)崿F(xiàn)PHP執(zhí)行超時,該信號處理程序在達(dá)到超時持續(xù)時間后將中斷設(shè)置為1。

當(dāng)更安全地清理或?qū)崿F(xiàn)自己的超時處理時,這有助于將信號處理推遲到運(yùn)行時執(zhí)行的后期。通過設(shè)置此掛鉤,您不會意外禁用PHP的超時檢查,因?yàn)樗哂凶远x處理的優(yōu)先級,該優(yōu)先級高于對zend_interrupt_function的任何覆蓋。

ZEND_API void(* original_interrupt_function)(zend_execute_data * execute_data);void my_interrupt_function(zend_execute_data * execute_data){
    if(original_interrupt_function!= NULL){
        original_interrupt_function(execute_data);
    }}PHP_MINIT_FUNCTION(my_extension){
    original_interrupt_function = zend_interrupt_function;
    zend_interrupt_function = my_interrupt_function;

    return SUCCESS;}

##替換操作碼處理程序

TODO

本文題目:PHP之鉤子
URL標(biāo)題:http://jinyejixie.com/article14/cheode.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)網(wǎng)站改版、網(wǎng)站設(shè)計、企業(yè)建站手機(jī)網(wǎng)站建設(shè)、建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
河西区| 朝阳县| 五莲县| 余姚市| 崇州市| 县级市| 泽普县| 泽普县| 迭部县| 田东县| 新建县| 景泰县| 华坪县| 舞阳县| 陕西省| 宝山区| 襄樊市| 库车县| 闸北区| 偃师市| 仁布县| 阜平县| 东至县| 盐源县| 张掖市| 武安市| 德惠市| 安阳市| 乐平市| 丹江口市| 唐海县| 德清县| 云安县| 普定县| 封丘县| 旬邑县| 花垣县| 普陀区| 汉阴县| 荥经县| 东乡县|