Как установить переменную в области видимости вызывающей стороны, например функцию extract () - PullRequest
2 голосов
/ 27 июня 2010

Я знаю, что прямая установка переменной в области действия вызывающей стороны, вероятно, не очень хорошая идея. Однако функция PHP extract() делает именно это! Я хотел бы написать свою собственную версию extract(), но не могу понять, как на самом деле установить переменные в вызывающей программе. Есть идеи? Самое близкое, что я пришел, - это изменение args вызывающего абонента с помощью debug_backtrace(), но это не совсем то же самое ...

Ответы [ 4 ]

3 голосов
/ 27 июня 2010

Вы не можете изменять локальные переменные в родительской области действия - метод, который использует extract (), не предоставляется PHP.

Кроме того, то, что вы получаете от debug_stacktrace (), магически не связано с реальным стеком. Вы не можете изменить его и надеяться, что ваши изменения будут доступны!

1 голос
/ 26 августа 2012

Вы можете злоупотреблять областью действия $ 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
1 голос
/ 27 июня 2010

Вы можете сделать это только в расширении 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 предназначена для работы с несколькими вариантами, которые он принимает.

0 голосов
/ 18 июля 2017

Это зависит от того, насколько сильно вам нужно это сделать.Если это только для красоты источника, найдите другой путь.Если по какой-то причине вам действительно нужно связываться с родительской областью действия, всегда есть способ.

РЕШЕНИЕ 1

Самый безопасный способ - это на самом деле используйте сам экстракт для этой работы, так как он знает хитрость.Скажем, вы хотите создать функцию, которая извлекает элементы массива, но со всеми именами в обратном направлении - довольно странно!-, давайте сделаем это с помощью простого преобразования массива в массив:

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

extract(backwardNames($myArray));

Никакой магии здесь.

РЕШЕНИЕ 2

Если вам нужно больше чем extract делает, используйте eval и var_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.И это значительно медленнее, конечно.

Однако, действительно, нет способа сделать это с помощью одного вызова.

...