我知道在调用者范围内直接设置变量可能不是一个好主意。
但是,PHP extract()
函数正是这样做的!我想编写自己的extract()
版本,但无法弄清楚如何在调用者中设置变量。有任何想法吗?
我最接近的是使用args
修改来电者debug_backtrace()
,但这不是完全相同的事情......
答案 0 :(得分:3)
您不能修改父作用域中的局部变量 - 提取()使用的方法不会被PHP公开。
另外,你从debug_stacktrace()得到的东西并没有神奇地链接到真正的堆栈。你不能修改它,希望你的修改是实时的!
答案 1 :(得分:1)
您只能在PHP扩展程序中执行此操作。如果调用内部PHP函数,它将不会在新的PHP作用域中运行(即,不会创建新的符号表)。因此,您可以通过更改全局EG(active_symbol_table)
来修改“父作用域”。
基本上,函数的核心会像extract
那样做,核心是:
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
//loop through the given array
ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table),
Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
然而,有一些细微差别。请参阅implementation of extract
,但请记住,执行您想要的功能并不需要如此复杂; extract
中的大部分代码都是为了处理它接受的几个选项。
答案 2 :(得分:1)
您可以滥用$ GLOBALS范围来从函数的调用者读取和写入变量。请参阅下面的示例函数,它从调用者范围读取和写入变量。
是的,我知道肮脏滥用$ GLOBAL范围,但是,嘿,我们来这里解决问题不是吗? :)
function set_first_name($firstname) {
/* check if $firstname is defined in caller */
if(array_key_exists('firstname', $GLOBALS)) {
$firstname_was = $GLOBALS['firstname'];
} else {
$firstname_was = 'undefined';
}
/* set $firstname in caller */
$GLOBALS['firstname'] = $firstname;
/* show onscreen confirmation for debugging */
echo '<br>firstname was ' . $firstname_was . ' and now is: ' . $firstname;
}
set_first_name('John');
set_first_name('Michael');
该函数返回以下输出:
<br>firstname was undefined and now is: John
<br>firstname was John and now is: Michael
答案 3 :(得分:0)
这取决于你需要做多少这么糟糕。如果它仅用于源美,请找另一种方式。如果出于某种原因,你真的需要混淆父范围,总会有办法。
最安全的方法是实际使用提取本身来完成这项工作,因为它知道这个技巧。假设您想创建一个提取数组元素但向后反转所有名称的函数 - 非常奇怪! - ,让我们通过一个简单的数组到数组转换来做到这一点:
function backwardNames($x) {
$out = [];
foreach($x as $key=>$val) {
$rev = strrev($key);
$out[$rev] = $val;
}
return $out;
}
extract(backwardNames($myArray));
这里没有魔力。
如果您需要的不仅仅是提取,请使用 eval 和 var_export 。是的,我知道我知道每个人都请冷静下来。不,eval不是邪恶的。 Eval是一种动力工具,如果您不小心使用它可能会很危险 - 因此请小心使用。 (如果你只评估由var_export生成的东西,那么没有办法出错 - 即使你从不受信任的源中将值放入数组中,它也不会给入侵带来任何影响。数组元素表现得很好。)
function makeMyVariables() {
$vars = [
"a" => 4,
"b" => 5,
];
$out = var_export($vars,1);
$out = "extract(".$out.");";
return $out;
}
eval(makeMyVariables()); // this is how you call it
// now $a is 4, $b is 5
这几乎是相同的,除了你可以在eval中做更多的事情。当然,它的速度要慢得多。
然而,实际上,只需一次通话就无法做到。