我正在通过调试一些旧代码进行努力,并希望使用预先构建的功能,该功能本质上是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
中的所有变量。它不能从呼叫者的范围中给您变量。
另外,请参阅参考:什么是变量范围,哪些变量可以从何处和什么是"不确定的变量"访问。错误?。