我正在调试一些遗留代码,并希望使用预构建的函数,它本质上是get_defined_vars()
的包装器。
直接在调用文件中运行此代码会按预期打印一组变量:
print_r(get_defined_vars());
但是,在我的函数的简化版本中包装它会打印一个空数组:
function debugAllVars() {
print_r(get_defined_vars());
}
debugAllVars();
无论范围如何,我都期望"超全球"诸如$_POST
之类的变量将出现在输出中。
为什么输出完全为空?
答案 0 :(得分:4)
get_defined_vars()
打印"符号表中的所有变量"它被调用的范围。当您尝试将其包装为debugAllVars
时,您将引入一个新的范围,该范围具有新的符号表。
对于像这样的独立函数,符号表包含:
global
关键字static
关键字声明的任何static variables(即使未分配值)$foo = &$bar
将隐式声明$bar
如果尚未定义; $foo = $bar
则不会)值得注意的是,此列表不包含超级全局,例如$_GET
,$_POST
和$GLOBALS
。如果你在全局范围内运行get_defined_vars()
(即在任何函数之外),你会看到这些 存在于那里的符号表中,这也是魔术变量$GLOBALS
指着。那么,为什么它们并不存在于每个范围内,如果它们不存在,我们如何使用它们?
为此,我们需要深入了解实现的内部,其中这些变量被称为" auto-globals"而不是" superglobals"。
为什么的答案是绩效:"自动全球"的天真实施将是一个行为,好像每个函数自动在顶部读数global $_GET, $_POST, ...;
有一条线。但是,这意味着在运行每个函数之前将所有这些变量复制到符号表中,即使它们没有被使用。
相反,这些变量在编译器中是特殊的,同时将PHP代码转换为内部"操作码"由执行代码的VM使用。
使用a source code browser,我们可以看到它是如何工作的。
关键功能是zend_is_auto_global
中的zend_compile.c
(取自当前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;
}
换句话说,如果您引用的变量名称位于超全局列表中,则编译器会将操作码切换为不同的模式,以便在执行时,它会全局查找变量而不是本地变量。
答案 1 :(得分:3)
get_defined_vars
将所有变量定义为在其调用的范围内。您的debugAllVars
函数引入了新的范围,因此{{1最多会给你get_defined_vars
中的所有变量。它无法从调用者的范围中为您提供变量。