如何在调用者范围中设置变量,如extract()函数

时间:2010-06-27 15:33:15

标签: php extract inject

我知道在调用者范围内直接设置变量可能不是一个好主意。 但是,PHP extract()函数正是这样做的!我想编写自己的extract()版本,但无法弄清楚如何在调用者中设置变量。有任何想法吗? 我最接近的是使用args修改来电者debug_backtrace(),但这不是完全相同的事情......

4 个答案:

答案 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)

这取决于你需要做多少这么糟糕。如果它仅用于源美,请找另一种方式。如果出于某种原因,你真的需要混淆父范围,总会有办法。

解决方案1 ​​

最安全的方法是实际使用提取本身来完成这项工作,因为它知道这个技巧。假设您想创建一个提取数组元素但向后反转所有名称的函数 - 非常奇怪! - ,让我们通过一个简单的数组到数组转换来做到这一点:

function backwardNames($x) {
    $out = [];
    foreach($x as $key=>$val) {
        $rev = strrev($key);
        $out[$rev] = $val;
    }
    return $out;
}

extract(backwardNames($myArray));

这里没有魔力。

解决方案2

如果您需要的不仅仅是提取,请使用 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中做更多的事情。当然,它的速度要慢得多。

然而,实际上,只需一次通话就无法做到。