PHP get_defined_vars() 不会在函数内部打印超全局变量

标签 php

我正在调试一些遗留代码,并希望使用一个预建函数,它本质上是 get_defined_vars() 的包装器。

直接在调用文件中运行此代码会按预期打印变量数组:

print_r(get_defined_vars());

但是,将其包装在我的函数的简化版本中会打印一个空数组:

function debugAllVars() {
    print_r(get_defined_vars());
}
debugAllVars();

无论范围如何,我都希望输出中出现“超全局”变量,例如 $_POST

为什么输出完全是空的?

最佳答案

get_defined_vars() 打印调用它的作用域的“符号表”中的所有变量。当您尝试将其包装为 debugAllVars 时,您引入了一个新的作用域,它具有一个新的符号表。

对于像这样的独立函数,符号表包括:

  • 函数的参数
  • 使用 global 关键字导入当前作用域的任何全局变量
  • 任何static variables使用 static 关键字在当前范围内声明(即使未分配值)
  • 任何通过赋值隐式声明的变量
  • 任何通过引用隐式声明的变量(例如,$foo = &$bar 如果尚未定义,将隐式声明 $bar$foo = $bar 不会)

值得注意的是,此列表包括超全局变量,例如$_GET$_POST$GLOBALS。如果您在全局范围内(即在任何函数之外)运行 get_defined_vars(),您将看到这些 存在于那里的符号表中,这也是魔法变量$GLOBALS 指向。那么,为什么它们不存在于每个范围内,如果它们不存在,我们如何使用它们?

为此,我们需要深入了解实现的内部结构,其中这些变量被称为“自动全局变量”而不是“超全局变量”。

为什么 的答案是性能:“自动全局”的天真实现会表现得好像每个函数自动在顶部都有一行阅读 global $_GET , $_POST, ...;。然而,这意味着在运行每个函数之前将所有这些变量复制到符号表中,即使它们没有被使用也是如此。

因此,这些变量在编译器中是特殊情况,同时将您的 PHP 代码转换为执行代码的 VM 使用的内部“操作码”。

使用 a source code browser ,我们可以看到这是如何工作的。

关键函数是 zend_compile.c 中的 zend_is_auto_global(取自当前的 master,有效的 PHP 7.2):

zend_bool zend_is_auto_global(zend_string *name) /* {{{ */
{
    zend_auto_global *auto_global;

    if ((auto_global = zend_hash_find_ptr(CG(auto_globals), name)) != NULL) {
        if (auto_global->armed) {
            auto_global->armed = auto_global->auto_global_callback(auto_global->name);
        }
        return 1;
    }
    return 0;
}

这里,name是变量名,CG是“编译器全局变量”的意思,所以这个函数的主要工作就是说“如果变量名给定在一个名为 auto_globals 的编译器全局哈希中,返回 1”。对 auto_global_callback 的额外调用允许变量“延迟加载”,并且仅在首次引用时填充。

zend_compile_simple_var_no_cv 中,该函数的主要用法似乎是这个条件:

if (name_node.op_type == IS_CONST && 
    zend_is_auto_global(Z_STR(name_node.u.constant))) {

    opline->extended_value = ZEND_FETCH_GLOBAL;
} else {
    opline->extended_value = ZEND_FETCH_LOCAL;
}

换句话说,如果您引用的变量名在超全局列表中,编译器会将操作码切换到不同的模式,以便在执行时,它会在全局而不是本地查找变量。

关于PHP get_defined_vars() 不会在函数内部打印超全局变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47657759/

相关文章:

php - mySQL:比较当前密码,然后在 1 个查询中更新为新密码?

php - 分配表 INSERT INTO 防止重复

php - 成功插入数据库后,Mysqli_insert_id() 绝对不返回任何内容。为什么?

php - 如何使用 json_encode 操作输出?

更改 Div 样式的 Javascript 函数

php - undefined variable $submit

php - 如何从数据库中获取特定行?

php - html/php硬刷新

php - 如何将 MySQL 数据库的多行放入多维数组 (PHP) 中?

PHP 通过异常处理空值处理