如何在调用者作用域中设置变量,例如 extract() 函数

发布于 2024-09-06 20:27:12 字数 214 浏览 3 评论 0原文

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

I know that directly setting a variable in the scope of caller is probably not a good idea.
However, the PHP extract() function does exactly that! I would like to write my own version of extract() but cannot figure out how to actually go about setting the variables in the caller. Any ideas?
The closest I have come is modifying the caller's args using debug_backtrace(), but this is not exactly the same thing...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

游魂 2024-09-13 20:27:12

您无法修改父作用域中的局部变量 - PHP 不公开 extract() 使用的方法。

此外,从 debug_stacktrace() 返回的内容并没有神奇地链接到真实的堆栈。您无法修改它,希望您的修改生效!

You can't modify local variables in a parent scope - the method which extract() uses is not exposed by PHP.

Also, what you get back from debug_stacktrace() isn't magically linked to the real stack. You can't modify it and hope your modifications are live!

梦旅人picnic 2024-09-13 20:27:12

您只能在 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);

但是,有一些细微差别。请参阅 extract 的实现,但请记住,执行您想要的操作的函数不需要那么复杂; extract 中的大部分代码都是为了处理它接受的几个选项。

You could only do it in a PHP extension. If you call an internal PHP function, it will not run in a new PHP scope (i.e., no new symbol table will be created). Therefore, you can modify the "parent scope" by changing the global EG(active_symbol_table).

Basically, the core of the function would do something like extract does, the core of which is:

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);

There are, however, a few nuances. See the implementation of extract, but keep in mind a function that did what you wanted wouldn't need to be as complex; most of the code in extract is there to deal with the several options it accepts.

骄兵必败 2024-09-13 20:27:12

您可以滥用 $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

You can abuse the $GLOBALS scope to read and write variables from the caller of your function. See below sample function, which reads and write variables from the caller scope.

And yes, I know its dirty to abuse the $GLOBAL scope, but hey, we're here to fix problems ain't we? :)

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');

The function returns the following output:

    <br>firstname was undefined and now is: John
    <br>firstname was John and now is: Michael
满天都是小星星 2024-09-13 20:27:12

这取决于你有多需要这样做。如果只是为了源美,那就另想办法。如果由于某种原因,您确实需要扰乱父作用域,那么总有办法。

解决方案 1

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

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

extract(backwardNames($myArray));

这里没有魔法。

解决方案 2

如果您需要的不仅仅是 extract 的功能,请使用 evalvar_export。是的,我知道,我知道,请大家冷静下来。不,评估并不邪恶。 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 中做更多的事情。当然,它的速度要慢得多。

然而,事实上,没有办法通过一次调用来做到这一点。

It depends on how badly you need to do this. If it's only for source beauty, find another way. If, for some reason, you really need to mess with parent scope, there's always a way.

SOLUTION 1

The safest method would be to actually use extract itself for this job, since it knows the trick. Say you want to make a function that extracts elements of an array but with all the names backwards - pretty weird! -, let's do this with a simple array-to-array transformation:

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

extract(backwardNames($myArray));

No magic here.

SOLUTION 2

If you need more than what extract does, use eval and var_export. YES I KNOW I KNOW everybody calm down please. No, eval is not evil. Eval is a power tool and it can be dangerous if you use it without care - so use it with care. (There is no way to go wrong if you only eval something that's been generated by var_export - it doesn't give any way to intrusions even if you put values in your array from an untrusted source. Array elements behave well.)

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

This is almost the same, except that you can do a lot more in eval. And it's significantly slower, of course.

However, indeed, there is no way to do it with a single call.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文