PHP 후크
PHP에서 제공하는 후크
PHP 및 Zend 엔진은 확장 개발자가 PHP 사용자 영역에서 제공할 수 없는 방식으로 PHP 런타임을 제어할 수 있도록 하는 다양한 확장 후크를 제공합니다.
이 장에서는 확장 후크의 다양한 후크와 일반적인 사용 사례를 보여줍니다.
PHP 기능에 연결하는 일반적인 패턴은 PHP 코어에서 제공하는 재정의 함수 포인터를 확장하는 것입니다. 그런 다음 확장 함수는 일반적으로 자체 작업을 수행하고 원래 PHP 핵심 함수를 호출합니다. 이 패턴을 사용하면 서로 다른 확장이 충돌을 일으키지 않고 동일한 후크를 재정의할 수 있습니다.
관련 학습 권장사항: 초보자부터 능숙한 PHP 프로그래밍
함수 실행에 푹 빠져 있음
사용자 영역 및 내부 함수의 실행은 Zend 엔진의 두 가지 함수로 처리되며, 이를 다음으로 대체할 수 있습니다. 이 두 가지 기능을 직접 구현해 보세요. 이 후크를 다루는 확장의 주요 사용 사례는 일반적인 기능 수준 프로파일링, 디버깅 및 관점 지향 프로그래밍입니다.
후크는 Zend/zend_execute.h
에 정의되어 있습니다: 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);
如果要覆盖这些函数指针,则必须在 Minit 中执行此操作,因为 Zend Engine 中的其他决策是根据指针是否被覆盖这一事实提前做出的。
覆盖的通常模式是这样的:
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
的一个缺点是它将 Zend Virtual Machine 运行时的行为更改为使用递归,而不是在不离开解释器循环的情况下处理调用。此外,没有覆盖zend_execute_ex
的 PHP 引擎也可以生成更优化的函数调用操作码。
这些挂钩对性能非常敏感,具体取决于原始函数封装代码的复杂性。
覆盖内部功能
在覆盖执行钩子时,扩展可以记录每个函数调用,你还可以覆盖用户域,核心和扩展函数(和方法)的各个函数指针。如果扩展仅需要访问特定的内部函数调用,则具有更好的性能特征。
#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){ // 如果我们想调用原始函数 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
上找到函数表:
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)
当 PHP 7编译 PHP 代码时,它会先将其转换为抽象语法树(AST),然后最终生成持久存储在 Opcache 中的操作码。zend_ast_process
钩子会被每个已编译的脚本调用,并允许你在解析和创建 AST 之后修改 AST。
这是要使用的最复杂的钩子之一,因为它需要完全了解 AST。在此处创建无效的 AST 可能会导致异常行为或崩溃。
最好看看使用此钩子的示例扩展:
- Google Stackdriver PHP调试器扩展
- 基于 Stackdriver 的带有 AST 的概念验证器
熟悉脚本/文件编译
每当用户脚本调用include
/require
或其对应的include_once
/require_once
时,PHP内核都会在指针zend_compile_file处调用该函数
处理此请求。参数是文件句柄,结果是zend_op_array
。
zend_op_array * my_extension_compile_file(zend_file_handle * file_handle,int类型);
PHP核心中有两个扩展实现了此挂钩:dtrace和opcache。
-如果您使用环境变量USE_ZEND_DTRACE
启动PHP脚本并使用dtrace支持编译了PHP,则dtrace_compile_file
用于Zend / zend_dtrace.c
。
-Opcache将操作数组存储在共享内存中以获得更好的性能,因此,每当脚本被编译时,其最终的操作数组都会从缓存中得到服务,而不是重新编译。您可以在ext / opcache / ZendAccelerator.c
中找到此实现。
-名为compile_file
的默认实现是Zend / zend_language_scanner.l
ZEND_API void(* zend_error_cb)(int类型,const char * error_filename,const uint32_t error_lineno,const char * format,va_list args);
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;}
zend_execute_ex
재정의의 한 가지 단점은 인터프리터 루프 Next 처리 호출을 떠나는 대신 재귀를 사용하도록 Zend 가상 머신 런타임의 동작을 변경한다는 것입니다. 또한 zend_execute_ex
를 재정의하지 않는 PHP 엔진은 더욱 최적화된 함수 호출 opcode를 생성할 수도 있습니다. 이 후크는 코드를 캡슐화하는 원래 기능의 복잡성에 따라 성능에 매우 민감합니다. 🎜🎜🎜🎜내부 함수 재정의🎜🎜 실행 후크를 재정의할 때 확장 프로그램은각 함수 호출을 기록할 수 있으며 사용자 영역, 핵심 및 확장 기능(및 메서드)에 대한 개별 함수 포인터를 재정의할 수도 있습니다. 확장이 특정 내부 함수 호출에만 액세스해야 하는 경우 성능 특성이 더 좋습니다. 🎜void my_throw_exception_hook(zval * exception){ if(original_zend_throw_exception_hook!= NULL){ original_zend_throw_exception_hook(exception); }}
zend_class_entry
에서 찾을 수 있습니다. 🎜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;}
zend_ast_process
후크는 모든 컴파일된 스크립트에 의해 호출되며 이를 통해 구문 분석 및 생성된 AST를 수정할 수 있습니다. 🎜🎜이것은 AST에 대한 완전한 이해가 필요하기 때문에 사용하기 가장 복잡한 후크 중 하나입니다. 여기서 잘못된 AST를 생성하면 예기치 않은 동작이나 충돌이 발생할 수 있습니다. 🎜🎜다음 후크를 사용하여 샘플 확장을 살펴보는 것이 좋습니다. 🎜- Google Stackdriver PHP 디버거 확장
- AST를 사용한 Stackdriver 기반 개념 증명
include
/require
또는 해당 include_once
/require_once
를 호출할 때마다 code>이면 PHP 커널은 zend_compile_file
포인터에서 이 함수를 호출하여 이 요청을 처리합니다. 인수는 파일 핸들이고 결과는 zend_op_array
입니다. 🎜extern ZEND_API zend_op_array *(* zend_compile_string)(zval * source_string,char * filename);
USE_ZEND_DTRACE
를 사용하여 PHP 스크립트를 시작하고 dtrace 지원으로 PHP를 컴파일하는 경우 dtrace_compile_file
이 Zend/zend_dtrace.c<에 사용됩니다. /코드> . <br/>-Opcache는 더 나은 성능을 위해 공유 메모리에 op 배열을 저장하므로 스크립트가 컴파일될 때마다 최종 op 배열은 재컴파일되지 않고 캐시에서 제공됩니다. 이 구현은 <code>ext/opcache/ZendAccelerator.c
에서 찾을 수 있습니다. -
compile_file
이라는 기본 구현은 Zend/zend_언어_scanner.l
에 있는 스캐너 코드의 일부입니다. 🎜🎜이 후크를 구현하는 사용 사례로는 Opcode 가속, PHP 코드 암호화/해독, 디버깅 또는 프로파일링이 있습니다. 🎜🎜PHP 프로세스가 실행되는 동안 언제든지 이 후크를 교체할 수 있으며 교체 후에 컴파일된 모든 PHP 스크립트는 후크 구현에 의해 처리됩니다. 🎜🎜항상 원래 함수 포인터를 호출하는 것이 매우 중요합니다. 그렇지 않으면 PHP가 더 이상 스크립트를 컴파일할 수 없고 Opcache가 더 이상 작동하지 않습니다. 🎜此处的扩展覆盖顺序也很重要,因为您需要知道是要在Opcache之前还是之后注册钩子,因为Opcache如果在其共享内存缓存中找到操作码数组条目,则不会调用原始函数指针。 Opcache将其钩子注册为启动后钩子,该钩子在扩展的minit阶段之后运行,因此默认情况下,缓存脚本时将不再调用该钩子。
调用错误处理程序时的通知
与PHP用户区set_error_handler()
函数类似,扩展可以通过实现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
变量对应于E _ *
错误常量,该常量在PHP用户区中也可用。
PHP核心和用户态错误处理程序之间的关系很复杂:
1.如果未注册任何用户级错误处理程序,则始终调用zend_error_cb
。
2.如果注册了userland错误处理程序,则对于E_ERROR
,E_PARSE
,E_CORE_ERROR
,E_CORE_WARNING
,E_COMPILE_ERROR的所有错误
和E_COMPILE_WARNING
始终调用zend_error_cb
挂钩。
3.对于所有其他错误,仅在用户态处理程序失败或返回false
时调用zend_error_cb
。
另外,由于Xdebug自身复杂的实现,它以不调用以前注册的内部处理程序的方式覆盖错误处理程序。
因此,覆盖此挂钩不是很可靠。
再次覆盖应该以尊重原始处理程序的方式进行,除非您想完全替换它:
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;}
该挂钩主要用于为异常跟踪或应用程序性能管理软件实施集中式异常跟踪。
引发异常时的通知
每当PHP Core或Userland代码引发异常时,都会调用zend_throw_exception_hook
并将异常作为参数。
这个钩子的签名非常简单:
void my_throw_exception_hook(zval * exception){ if(original_zend_throw_exception_hook!= NULL){ original_zend_throw_exception_hook(exception); }}
该挂钩没有默认实现,如果未被扩展覆盖,则指向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;}
如果实现此挂钩,请注意无论是否捕获到异常,都会调用此挂钩。将异常临时存储在此处,然后将其与错误处理程序挂钩的实现结合起来以检查异常是否未被捕获并导致脚本停止,仍然有用。
实现此挂钩的用例包括调试,日志记录和异常跟踪。
挂接到eval()
PHPeval
不是内部函数,而是一种特殊的语言构造。因此,您无法通过zend_execute_internal
或通过覆盖其函数指针来连接它。
挂钩到eval的用例并不多,您可以将其用于概要分析或出于安全目的。如果更改其行为,请注意可能需要评估其他扩展名。一个示例是Xdebug,它使用它执行断点条件。
extern ZEND_API zend_op_array *(* zend_compile_string)(zval * source_string,char * filename);
挂入垃圾收集器
当可收集对象的数量达到一定阈值时,引擎本身会调用gc_collect_cycles()
或隐式地触发PHP垃圾收集器。
为了使您了解垃圾收集器的工作方式或分析其性能,可以覆盖执行垃圾收集操作的函数指针挂钩。从理论上讲,您可以在此处实现自己的垃圾收集算法,但是如果有必要对引擎进行其他更改,则这可能实际上并不可行。
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;}
覆盖中断处理程序
当执行器全局EG(vm_interrupt)
设置为1时,将调用一次中断处理程序。在执行用户域代码期间,将在常规检查点对它进行检查。引擎使用此挂钩通过信号处理程序实现PHP执行超时,该信号处理程序在达到超时持续时间后将中断设置为1。
当更安全地清理或实现自己的超时处理时,这有助于将信号处理推迟到运行时执行的后期。通过设置此挂钩,您不会意外禁用PHP的超时检查,因为它具有自定义处理的优先级,该优先级高于对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 후크의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











PHP 8.4는 상당한 양의 기능 중단 및 제거를 통해 몇 가지 새로운 기능, 보안 개선 및 성능 개선을 제공합니다. 이 가이드에서는 Ubuntu, Debian 또는 해당 파생 제품에서 PHP 8.4를 설치하거나 PHP 8.4로 업그레이드하는 방법을 설명합니다.

숙련된 PHP 개발자라면 이미 그런 일을 해왔다는 느낌을 받을 것입니다. 귀하는 상당한 수의 애플리케이션을 개발하고, 수백만 줄의 코드를 디버깅하고, 여러 스크립트를 수정하여 작업을 수행했습니다.

VS Code라고도 알려진 Visual Studio Code는 모든 주요 운영 체제에서 사용할 수 있는 무료 소스 코드 편집기 또는 통합 개발 환경(IDE)입니다. 다양한 프로그래밍 언어에 대한 대규모 확장 모음을 통해 VS Code는

JWT는 주로 신분증 인증 및 정보 교환을 위해 당사자간에 정보를 안전하게 전송하는 데 사용되는 JSON을 기반으로 한 개방형 표준입니다. 1. JWT는 헤더, 페이로드 및 서명의 세 부분으로 구성됩니다. 2. JWT의 작업 원칙에는 세 가지 단계가 포함됩니다. JWT 생성, JWT 확인 및 Parsing Payload. 3. PHP에서 인증에 JWT를 사용하면 JWT를 생성하고 확인할 수 있으며 사용자 역할 및 권한 정보가 고급 사용에 포함될 수 있습니다. 4. 일반적인 오류에는 서명 검증 실패, 토큰 만료 및 대형 페이로드가 포함됩니다. 디버깅 기술에는 디버깅 도구 및 로깅 사용이 포함됩니다. 5. 성능 최적화 및 모범 사례에는 적절한 시그니처 알고리즘 사용, 타당성 기간 설정 합리적,

문자열은 문자, 숫자 및 기호를 포함하여 일련의 문자입니다. 이 튜토리얼은 다른 방법을 사용하여 PHP의 주어진 문자열의 모음 수를 계산하는 방법을 배웁니다. 영어의 모음은 A, E, I, O, U이며 대문자 또는 소문자 일 수 있습니다. 모음이란 무엇입니까? 모음은 특정 발음을 나타내는 알파벳 문자입니다. 대문자와 소문자를 포함하여 영어에는 5 개의 모음이 있습니다. a, e, i, o, u 예 1 입력 : String = "Tutorialspoint" 출력 : 6 설명하다 문자열의 "Tutorialspoint"의 모음은 u, o, i, a, o, i입니다. 총 6 개의 위안이 있습니다

이 튜토리얼은 PHP를 사용하여 XML 문서를 효율적으로 처리하는 방법을 보여줍니다. XML (Extensible Markup Language)은 인간의 가독성과 기계 구문 분석을 위해 설계된 다목적 텍스트 기반 마크 업 언어입니다. 일반적으로 데이터 저장 AN에 사용됩니다

정적 바인딩 (정적 : :)는 PHP에서 늦은 정적 바인딩 (LSB)을 구현하여 클래스를 정의하는 대신 정적 컨텍스트에서 호출 클래스를 참조 할 수 있습니다. 1) 구문 분석 프로세스는 런타임에 수행됩니다. 2) 상속 관계에서 통화 클래스를 찾아보십시오. 3) 성능 오버 헤드를 가져올 수 있습니다.

PHP의 마법 방법은 무엇입니까? PHP의 마법 방법은 다음과 같습니다. 1. \ _ \ _ Construct, 객체를 초기화하는 데 사용됩니다. 2. \ _ \ _ 파괴, 자원을 정리하는 데 사용됩니다. 3. \ _ \ _ 호출, 존재하지 않는 메소드 호출을 처리하십시오. 4. \ _ \ _ get, 동적 속성 액세스를 구현하십시오. 5. \ _ \ _ Set, 동적 속성 설정을 구현하십시오. 이러한 방법은 특정 상황에서 자동으로 호출되어 코드 유연성과 효율성을 향상시킵니다.
