PHP Profiler - 使用 C++ 从 PHP 扩展获取客户端 IP 和 URL

PHP Profiler - Getting client IP and URL from PHP extension using C++

我在 c++ 中创建了一个 php 扩展,它现在跟踪每个 request.And 的调用图,我想获取每个请求的远程 IP 地址和 URL 详细信息。 在 CentOS 7.5PHP 5.6.36(禁用线程安全)中尝试 ,当用户给每个请求(浏览器)到达时,apache 将控制权交给 PHP 模块,当它被处理到响应阶段时。 PHP_RINIT_FUNCTION() 正在为来自 Zend 引擎的每个事务调用。 我试图在 PHP_RINIT_FUNCTION(apm) 函数中获取服务器名称和远程地址及其详细信息,如下所示,

zval *args;
char * name = "REMOTE_ADDR";
int len = sizeof("REMOTE_ADDR");
if ( (zend_hash_find(&EG(symbol_table), name,len, (void **) &args) != FAILURE) || PG(http_globals)[TRACK_VARS_SERVER] || zend_hash_find(PG(http_globals)[TRACK_VARS_SERVER]->value.ht, name,len, (void **) &args) != FAILURE)
 {
 // success
 //converts args(zval*) to string 
 }
else
{
    //failed
    //can't get REMOTE address
}

上面的做法对吗??如果是这样,每个请求都会失败。我哪里错了?如果有替代方案是什么?

我过去成功的做法是从$_SERVER超全局数组变量中读取这些值。为了使用 $_SERVER(尤其是在 RINIT 上下文中),您必须确保已调用其自动全局回调。 PHP 通常在第一次用户空间访问时调用它(换句话说,PHP lazy-loads 超全局变量)。

为此,您从 auto_globals 哈希表中查找 zend_auto_global 值并调用自动全局回调。您应该始终检查以确保 $_SERVER 没有事先加载;虽然在你的情况下你是从 RINIT 调用它所以它总是不会被加载。

调用自动全局回调后,您现在可以像访问 symbol_table 哈希表中的任何其他全局变量一样访问 $_SERVER

这是我的一个名为 uniauth 的扩展的示例(有关 Github 的完整上下文,请参阅 this link):

/* Make sure $_SERVER is auto loaded already. */
if (!zend_hash_exists(&EG(symbol_table),"_SERVER",8)) {
    if (zend_hash_find(CG(auto_globals),"_SERVER",8,(void**)&auto_global) != FAILURE) {
        auto_global->armed = auto_global->auto_global_callback(auto_global->name,
            auto_global->name_len TSRMLS_CC);
    }
    else {
        zend_throw_exception(NULL,"could not activate _SERVER",0 TSRMLS_CC);
        return 0;
    }
}

在代码库的这个特定部分,扩展正在使用 $_SERVER 中的信息生成重定向 URI。下面是显示服务器变量生成 URI 的用法的代码(有关 Github 的完整上下文,请参阅 this link):

/* Get information about the protocol, host and port number from the _SERVER
 * superglobal. We use HTTPS, HTTP_HOST and SERVER_PORT keys to resolve the
 * scheme, host and port. I know of no better way to do this unfortunately
 * with the PHP/ZEND API. The sapi globals just don't have what I need.
 */
if (zend_hash_find(&EG(symbol_table),"_SERVER",8,(void**)&arr) == FAILURE) {
    zend_throw_exception(NULL,"no _SERVER superglobal",0 TSRMLS_CC);
    return 0;
}
server = Z_ARRVAL_PP(arr);
if (zend_hash_find(server,"HTTPS",6,(void**)&val) != FAILURE) {
    if (Z_TYPE_PP(val) != IS_STRING || strcmp(Z_STRVAL_PP(val),"off") != 0) {
        https = 1;
    }
}
if (zend_hash_find(server,"HTTP_HOST",10,(void**)&val) != FAILURE
    && Z_TYPE_PP(val) == IS_STRING)
{
    host = Z_STRVAL_PP(val);
}
else {
    zend_throw_exception(NULL,"no HTTP_HOST within _SERVER found",0 TSRMLS_CC);
}

在评论中,我哀叹一个事实,即如果不模仿您在用户空间代码中所做的事情,就无法做到这一点。您可以在扩展中访问 SAPI 全局结构,但它没有我的项目(和您的)所需的所有信息。有关详细信息,请参阅 PHP headers 中的 the relevant structures