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);이러한 함수 포인터를 덮어쓰려면 Minit에서 해야 합니다. Zend 엔진의 다른 결정은 포인터가 포인터인지 여부에 따라 결정되기 때문입니다. 이 사실을 취재하는 것은 사전에 이루어집니다. 일반적인 재정의 패턴은 다음과 같습니다.
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;}🎜🎜🎜추상 구문 트리(AST)를 수정하세요. 🎜🎜PHP 7이 PHP 코드를 컴파일할 때 먼저 AST(추상 구문 트리)로 변환한 후 최종적으로 Opcache에 유지되는 opcode를 생성했습니다.
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);🎜이 후크를 구현하는 PHP 코어에는 dtrace와 opcache라는 두 가지 확장이 있습니다. 🎜🎜 - 환경 변수
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

PHP는 현대적인 프로그래밍, 특히 웹 개발 분야에서 강력하고 널리 사용되는 도구로 남아 있습니다. 1) PHP는 사용하기 쉽고 데이터베이스와 완벽하게 통합되며 많은 개발자에게 가장 먼저 선택됩니다. 2) 동적 컨텐츠 생성 및 객체 지향 프로그래밍을 지원하여 웹 사이트를 신속하게 작성하고 유지 관리하는 데 적합합니다. 3) 데이터베이스 쿼리를 캐싱하고 최적화함으로써 PHP의 성능을 향상시킬 수 있으며, 광범위한 커뮤니티와 풍부한 생태계는 오늘날의 기술 스택에 여전히 중요합니다.

PHP에서는 약한 참조가 약한 회의 클래스를 통해 구현되며 쓰레기 수집가가 물체를 되 찾는 것을 방해하지 않습니다. 약한 참조는 캐싱 시스템 및 이벤트 리스너와 같은 시나리오에 적합합니다. 물체의 생존을 보장 할 수 없으며 쓰레기 수집이 지연 될 수 있음에 주목해야합니다.

\ _ \ _ 호출 메소드를 사용하면 객체를 함수처럼 호출 할 수 있습니다. 1. 객체를 호출 할 수 있도록 메소드를 호출하는 \ _ \ _ 정의하십시오. 2. $ obj (...) 구문을 사용할 때 PHP는 \ _ \ _ invoke 메소드를 실행합니다. 3. 로깅 및 계산기, 코드 유연성 및 가독성 향상과 같은 시나리오에 적합합니다.

섬유는 PHP8.1에 도입되어 동시 처리 기능을 향상시켰다. 1) 섬유는 코 루틴과 유사한 가벼운 동시성 모델입니다. 2) 개발자는 작업의 실행 흐름을 수동으로 제어 할 수 있으며 I/O 집약적 작업을 처리하는 데 적합합니다. 3) 섬유를 사용하면보다 효율적이고 반응이 좋은 코드를 작성할 수 있습니다.

PHP 커뮤니티는 개발자 성장을 돕기 위해 풍부한 자원과 지원을 제공합니다. 1) 자료에는 공식 문서, 튜토리얼, 블로그 및 Laravel 및 Symfony와 같은 오픈 소스 프로젝트가 포함됩니다. 2) 지원은 StackoverFlow, Reddit 및 Slack 채널을 통해 얻을 수 있습니다. 3) RFC에 따라 개발 동향을 배울 수 있습니다. 4) 적극적인 참여, 코드에 대한 기여 및 학습 공유를 통해 커뮤니티에 통합 될 수 있습니다.

PHP와 Python은 각각 고유 한 장점이 있으며 선택은 프로젝트 요구 사항을 기반으로해야합니다. 1.PHP는 간단한 구문과 높은 실행 효율로 웹 개발에 적합합니다. 2. Python은 간결한 구문 및 풍부한 라이브러리를 갖춘 데이터 과학 및 기계 학습에 적합합니다.

PHP는 죽지 않고 끊임없이 적응하고 진화합니다. 1) PHP는 1994 년부터 새로운 기술 트렌드에 적응하기 위해 여러 버전 반복을 겪었습니다. 2) 현재 전자 상거래, 컨텐츠 관리 시스템 및 기타 분야에서 널리 사용됩니다. 3) PHP8은 성능과 현대화를 개선하기 위해 JIT 컴파일러 및 기타 기능을 소개합니다. 4) Opcache를 사용하고 PSR-12 표준을 따라 성능 및 코드 품질을 최적화하십시오.

PHP의 미래는 새로운 기술 트렌드에 적응하고 혁신적인 기능을 도입함으로써 달성 될 것입니다. 1) 클라우드 컴퓨팅, 컨테이너화 및 마이크로 서비스 아키텍처에 적응, Docker 및 Kubernetes 지원; 2) 성능 및 데이터 처리 효율을 향상시키기 위해 JIT 컴파일러 및 열거 유형을 도입합니다. 3) 지속적으로 성능을 최적화하고 모범 사례를 홍보합니다.


핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.
