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



我正在通过调试一些旧代码进行努力,并希望使用预先构建的功能,该功能本质上是get_defined_vars()的包装器。

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

print_r(get_defined_vars());

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

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

无论范围如何,我都希望在输出中存在诸如$_POST之类的"超级全局"变量。

为什么输出完全空?

get_defined_vars()在范围的"符号表"中打印所有变量。当您尝试将其包装为debugAllVars时,您会引入一个新范围,该范围具有新的符号表。

对于这样的独立函数,符号表由:

组成
  • 函数的参数
  • 使用global关键字导入到当前范围中的任何全局变量
  • 使用static关键字在当前范围中声明的任何静态变量(即使未分配值也未分配)
  • 通过为其分配值分配值
  • ,任何变量暗中声明了
  • 通过对它们进行引用暗中声明的任何变量(例如,$foo = &$bar将隐式声明$bar如果尚未定义; $foo = $bar不会)

值得注意的是,此列表确实不包括包括 $_GET$_POST$GLOBALS的超级全局。如果您在全局范围中运行get_defined_vars()(即任何功能外部),则会看到这些中存在中的符号表中,这也是魔术变量$GLOBALS点。那么,为什么它们不在每个范围中都存在,如果不存在它们,我们该如何使用它们?

为此,我们需要深入研究实施的内部,其中这些变量称为"自动全球"而不是"超级全球"。

为什么是性能的答案:"自动全球"的幼稚实现将是一个起作用的,好像每个函数都会自动在顶部阅读global $_GET, $_POST, ...;上具有一条线。但是,这意味着在运行每个函数之前将所有这些变量复制到符号表中,即使它们不使用。

因此,这些变量在编译器中是特殊限制的,同时将PHP代码转换为执行代码的VM使用的内部" OPCODES"。

使用源代码浏览器,我们可以看到它的工作原理。

关键功能是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表示"编译器Globals",因此此功能的主要作业是说"如果给出的变量名称是在Compiler-Global Hash中,称为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;
}

换句话说,如果您引用的变量名称在超级全球列表中,则编译器将OPCODE切换为其他模式,因此当执行时,它在全球范围内而不是本地查找变量。

get_defined_vars获取所有变量定义在被称为in的范围中。您的 debugAllVars函数介绍了新的 scope ,因此get_defined_vars将在大多数为您提供debugAllVars中的所有变量。它不能从呼叫者的范围中给您变量。

另外,请参阅参考:什么是变量范围,哪些变量可以从何处和什么是"不确定的变量"访问。错误?。

最新更新