5

I want to dynamically access value of variable, let's say I have this array:

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

Standard approach to print the test key value would be:

print $aData['test'];

However, if I have to work with string representation of variable (for dynamic purposes)

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

how can I achieve to print aData key named test? Neither of examples provided below works

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

What would be the solution?

6 Answers 6

6

Your eval example is lacking the return value:

print eval("return $sItem;");

should do it:

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

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

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

But it's not recommended to use eval normally. You can go into hell's kitchen with it because eval is evil.

Instead just parse the string and return the value:

$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

This can be done with some other form of regular expression as well.

As your syntax is very close to PHP an actually a subset of it, there is some alternative you can do if you want to validate the input before using eval. The method is to check against PHP tokens and only allow a subset. This does not validate the string (e.g. syntax and if a variable is actually set) but makes it more strict:

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

You just give an array of valid tokens to that function. Those are the PHP tokens, in your case those are:

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

You then just can use it and it works with more complicated keys as well:

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

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

I used this in another answer of the question reliably convert string containing PHP array info to array.

Sign up to request clarification or add additional context in comments.

1 Comment

@all: thanks all of you guys, from what im planning to do (in big picture) it is best solution for me to use eval function ... i just forget to use return
5

No eval necessary if you have (or can get) the array name and key into separate variables:

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

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

print ${$arrayname}[$keyname]; // 123

Comments

3

You can just use it like an ordinary array:

$key = "test";

print $aData[$key];

Likewise $aData could itself be an entry in a larger array store.


As alternative, extracting the potential array keys using a regex and traversing an anonymous array (should have mentioned that in your question, if) with references would be possible. See Set multi-dimensional array by key path from array values? and similar topics.


Personally I'm using a construct like this to utilize dynamic variable paths like varname[keyname] instead (similar to how PHP interprets GET parameters). It's just an eval in sheeps clothing (do not agree with the eval scaremongering though):

$val = preg_replace("/^(\w)+(\[(\w+)])$/e", '$\1["\3"]', "aData[test]");

6 Comments

Apart from the fact that you probably mean print $aData[$key], and not $aData[$test], this is the best solution by far. +1
If you ignore what's being asked then it's the best solution, yes! :)
+1. I think this is the only reasonable, and working solution. No matter how the others have suggested to use eval, none of them will work to return the key test. Probably some regex string parsing would achieve to extract the key, but eval would never manage to do that based on the way the OP has the string.
@JanHanÄŤiÄŤ What is being asked is the key not the result. Neither of the eval examples work to retrieve the key. The string parsing solution from hakre does work, but it's a roundabout trip. Why would you be the one assigning the key to a substring of a string variable, then having to go over that entire string to fetch the key, when you could have the key ready all the times.
@JanHanÄŤiÄŤ Maybe, you might be right. If the question is to access the value, then you are right. However, we would have to wait for OP's response to see if I understand it or not. :)
|
1

The only solution in your case is to use Eval().

But please be very very very careful when doing this! Eval will evaluate (and execute) any argument you pass to it as PHP. So if you will feed it something that comes from users, then anyone could execute any PHP code on your server, which goes without saying is a security hole the size of Grand canyon!.

edit: you will have to put a "print" or "echo" inside your $sItem variable somehow. It will either have to be in $sItem ($sItem = 'echo $aData[\'test\']';) or you will have to write your Eval() like this: Eval ( 'echo ' . $sData ).

1 Comment

According to his question it is.
1
$sItem = '$aData[\'test\']';
eval('$someVar = '.$sItem.';');
echo $someVar;

Use eval() with high caution as others aldready explained.

Comments

1

You could use this method

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

5 Comments

That's a good approach. A single preg_match_all would however suffice to get the array keys at once, avoding the [ parsing ].
@mario thanks. Give me such please, with given that such key can not exist in array
Missed two +. You'd still need to probe if the keys exist. It would be just be a more convenient way to break out the potential keys. Something like preg_match_all('/((?<=^|\$)\w+|(?<=\[[\'"])[^\'"]+(?=[\'"]\]))/') for example breaks up foo["bar"] into foo and bar
@mario, do you test it? It returns foo and foo, and w+ I fix myself :)
Yes, tested it. You forgot the second + then.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.