pinpoint-php-aop 是一个支持pinpoint-php agent 的库
- 自动注入PHP内置函数,比如redis,pdo,mysqli
- 自动注入用户类,比如 guzzlehttp, predis
怎样处理内置函数
内置函数解释:
PHP comes standard with many functions and constructs. There are also functions that require specific
PHP extensions compiled in, otherwise fatal "undefined function" errors will appear. For example, to
use image functions such as imagecreatetruecolor(), PHP must be compiled with GD support. Or, to use
mysqli_connect(), PHP must be compiled with MySQLi support. There are many core functions that are
included in every version of PHP, such as the string and variable functions. A call to phpinfo()
or get_loaded_extensions() will show which extensions are loaded into PHP. Also note that many
extensions are enabled by default and that the PHP manual is split up by extension. ...
> https://www.php.net/manual/en/functions.internal.php#functions.internal
通过修改PHP内核中 CG(class_table)
Inspired by https://www.phpinternalsbook.com/php7/extensions_design/hooks.html#overwriting-an-internal-function
PHP内核中提供了全局的 class_table
,用户可以可以用来替换原始的函数,然后达到包装该函数的目的:比如插入一些安全的插件代码。
步骤
- ext_pinpoint-php 提供内置函数替换功能
// https://github.com/pinpoint-apm/pinpoint-c-agent/blob/9c544f139665dde3a9cee2a244a9c3be2f32bff9/src/PHP/pinpoint_php.cpp#L887 zend_function *func = (zend_function *)zend_hash_str_find_ptr( CG(function_table), ZSTR_VAL(name), ZSTR_LEN(name)); if (func != NULL && func->internal_function.handler == pinpoint_interceptor_handler_entry) { pp_trace("function `%s` interceptor already added", ZSTR_VAL(name)); } else if (func != NULL) { pp_interceptor_v_t *interceptor = make_interceptor(name, before, end, exception, func); // insert into hash if (!zend_hash_add_ptr(PPG(interceptors), name, interceptor)) { free_interceptor(interceptor); pp_trace("added interceptor on `function`: %s failed. reason: already " "exist ", ZSTR_VAL(name)); return; } func->internal_function.handler = pinpoint_interceptor_handler_entry; pp_trace("added interceptor on `function`: %s success", ZSTR_VAL(name));
- 基于第一步的功能,在插入点添加pinpoint的业务逻辑插件
// https://github.com/pinpoint-apm/pinpoint-php-aop/blob/5994253869d516c38d528a8ef784a5c1c18b20f3/lib/Pinpoint/Plugins/SysV2/_curl/curl.php#L78 pinpoint_join_cut( ["curl_close"], function ($ch) use (&$ch_res) { unset($ch_res[(int) $ch]); pinpoint_start_trace(); pinpoint_add_clue(PP_INTERCEPTOR_NAME, "curl_close"); pinpoint_add_clue(PP_SERVER_TYPE, PP_PHP_METHOD); }, function ($ret) { pinpoint_end_trace(); }, function ($e) { } );
- 根据需要,启用插件
// https://github.com/pinpoint-apm/pinpoint-php-aop/blob/5994253869d516c38d528a8ef784a5c1c18b20f3/lib/Pinpoint/Plugins/PinpointPerRequestPlugins.php#L126C12-L126C58 if(sampled){ require_once __DIR__ . "/SysV2/__init__.php"; }else{ require_once __DIR__ . "/SysV2/_curl/__init__.php"; }
怎样处理用户定义的类
在此之前,你需要了解类加载器
By registering autoloaders, PHP is given a last chance to load the class or interface before it fails with an error.
> https://www.php.net/manual/en/language.oop5.autoload.php
对于PHP,当用户通过 use
等来加载类或者函数的时候,内核会检查这个类是否已经被加载。如果没有,它就会调用auto_loader去调用对应的文件。pinpoint-php-aop
也就是在这个时候开始拦截类的。
-
当PHP的类加载器初始完成后,pinpoint-php-aop 的类加载器会拦截所有的加载的类和函数。当发现一个需要被拦截的类被加载的时候,它会把这个类指向一个添加了pinpoint插件的类。
-
当pinpoint 加载器发现这个文件没有被pinpoint的插件拦截,它就会生成一个添加了pinpoint插件的类,然后注册到类加载器里面。更重要的是,这些类会被缓存到cache_dir中,当后续的请求到来的时候,这些类文件会被重新使用。这样的好处是,可以节约很多请求时间。
可能有些晕
标签:__,function,PHP,pinpoint,aop,interceptor,php From: https://www.cnblogs.com/eeliu/p/18388648