PHP динамически обращается к значению переменной - PullRequest
5 голосов
/ 02 декабря 2011

Я хочу получить динамический доступ к значению переменной, скажем, у меня есть этот массив:

$aData = array(
  'test' => 123
);

Стандартный способ печати test ключа будет следующим:

print $aData['test'];

Однако, если мне нужно работать со строковым представлением переменной (для динамических целей)

$sItem = '$aData[\'test\']';

как мне добиться печати aData ключ с именем test? Ни один из приведенных ниже примеров не работает

print $$sItem;
print eval($sItem);

Каким было бы решение?

Ответы [ 6 ]

6 голосов
/ 02 декабря 2011

В вашем примере eval отсутствует возвращаемое значение:

print eval("return $sItem;");

должен сделать это:

$aData['test'] = 'foo';

$sItem = '$aData[\'test\']';

print eval("return $sItem;"); # foo

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

Вместо этого просто проанализируйте строку и верните значение:

$aData['test'] = 'foo';

$sItem = '$aData[\'test\']';

$r = sscanf($sItem, '$%[a-zA-Z][\'%[a-zA-Z]\']', $vName, $vKey);
if ($r === 2)
{
    $result = ${$vName}[$vKey];
}
else
{
    $result = NULL;
}

print $result; # foo

Это можно сделать и с другой формой регулярного выражения.

Поскольку ваш синтаксис очень близок к PHP и фактически является его подмножеством, есть некоторая альтернатива, которую вы можете сделать, если хотите проверить ввод перед использованием eval. Метод заключается в проверке на соответствие токенам PHP и разрешении только подмножества. Это не проверяет строку (например, синтаксис и если переменная фактически установлена), но делает ее более строгой:

function validate_tokens($str, array $valid)
{
    $vchk = array_flip($valid);
    $tokens = token_get_all(sprintf('<?php %s', $str));
    array_shift($tokens);
    foreach($tokens as $token)
        if (!isset($vchk[$token])) return false;
    return true;
}

Вы просто передаете массив допустимых токенов этой функции. Это токены PHP, в вашем случае это:

T_LNUMBER (305) (probably)
T_VARIABLE (309)
T_CONSTANT_ENCAPSED_STRING (315)

Затем вы можете просто использовать его, и он работает с более сложными клавишами:

$aData['test'] = 'foo';
$aData['te\\\'[]st']['more'] = 'bar';

$sItem = '$aData[\'test\']';
$vValue = NULL;
if (validate_tokens($sItem, array(309, 315, '[', ']')))
{
    $vValue = eval("return $sItem;");
}

Я использовал это в другом ответе на вопрос , чтобы надежно преобразовать строку, содержащую информацию массива PHP, в массив .

4 голосов
/ 02 декабря 2011

Не требуется eval, если у вас есть (или вы можете получить) имя массива и ключ в отдельных переменных:

$aData = array(
  'test' => 123
);

$arrayname = 'aData';
$keyname = 'test';

print ${$arrayname}[$keyname]; // 123
3 голосов
/ 02 декабря 2011

Вы можете просто использовать его как обычный массив:

$key = "test";

print $aData[$key];

Аналогично $aData может сам по себе быть записью в большем хранилище массивов.


В качестве альтернативы, извлечениепотенциальные ключи массива, использующие регулярное выражение и обход анонимного массива (должен был упомянуть, что в вашем вопросе, если) со ссылками было бы возможно.См. Установить многомерный массив по ключевому пути из значений массива? и аналогичных тем.


Лично я использую такую ​​конструкцию, чтобы использовать пути динамических переменных, такие как varname[keyname]вместо этого (аналогично тому, как PHP интерпретирует параметры GET).Это просто эвал в овечьей шкуре (хотя не согласен с эвалой панибратством):

$val = preg_replace("/^(\w)+(\[(\w+)])$/e", '$\1["\3"]', "aData[test]");
1 голос
/ 02 декабря 2011

Вы можете использовать этот метод

function getRecursive($path, array $data) {
        // transform "foo['bar']" and 'foo["bar"]' to "foo[bar]"
        $path = preg_replace('@\[(?:"|\')(.+)(?:"|\')\]@Uis', '[\1]', $path);

        // get root
        $i = strpos($path, '[');
        $rootKey = substr($path, 0, $i);
        if (!isset($data[$rootKey])) {
            return null;
        }
        $value = $data[$rootKey];

        $length = strlen($path);
        $currentKey = null;
        for (; $i < $length; ++$i) {
            $char = $path[$i];

            switch ($char) {
                case '[':
                    if ($currentKey !== null) {
                        throw new InvalidArgumentException(sprintf('Malformed path, unexpected "[" at position %u', $i));
                    }
                    $currentKey = '';
                    break;
                case ']':
                    if ($currentKey === null) {
                        throw new InvalidArgumentException(sprintf('Malformed path, unexpected "]" at position %u', $i));
                    }

                    if (!isset($value[$currentKey])) {
                        return null;
                    }

                    $value = $value[$currentKey];
                    if (!is_array($value)) {
                        return $value;
                    }

                    $currentKey = null;
                    break;
                default:
                    if ($currentKey === null) {
                        throw new InvalidArgumentException(sprintf('Malformed path, unexpected "%s" at position %u', $char, $i));
                    }
                    $currentKey .= $char;
                    break;
            }
        }

        if ($currentKey !== null) {
            throw new InvalidArgumentException('Malformed path, must be and with "]"');
        }

        return $value;
    }
1 голос
/ 02 декабря 2011
$sItem = '$aData[\'test\']';
eval('$someVar = '.$sItem.';');
echo $someVar;

Используйте eval () с большой осторожностью, как уже объяснили другие.

1 голос
/ 02 декабря 2011

Единственным решением в вашем случае является использование Eval () .

Но, пожалуйста, будьте очень, очень, очень осторожны при этом!Eval оценит (и выполнит) любой аргумент, который вы передадите ему как PHP.Поэтому, если вы будете кормить его чем-то, что исходит от пользователей, тогда любой может выполнить любой код PHP на вашем сервере, что само собой разумеется, является дырой в безопасности размером с Гранд-Каньон!поместите «print» или «echo» в переменную $sItem.Он должен быть либо в $sItem ($sItem = 'echo $aData[\'test\']';), либо вам нужно будет написать свой Eval() так: Eval ( 'echo ' . $sData ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...