2011-12-02 143 views
5

我想动态可变的访问值,让我们说我有这样的数组:PHP动态访问变量值

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

标准的方法来打印test关键值将是:

print $aData['test']; 

但是,如果我必须使用变量的字符串表示形式(用于动态目的)

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

我该如何实现打印aData密钥名为test?下面提供的两个示例都没有工作

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

会是什么解决方案?

回答

6

你的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;"); 
} 

我的问题another answer使用该reliably convert string containing PHP array info to array

+0

@all:感谢你们所有人,从我计划做的事情(大图)最好的解决方案,我使用eval函数...我只是忘记使用返回 – nabizan

1

您的情况唯一的解决方案是使用Eval()

但这样做时请非常非常小心! Eval将评估(并执行)您以PHP身份传递给它的任何参数。因此,如果您想提供来自用户的东西,那么任何人都可以在您的服务器上执行任何PHP代码,这不言而喻是大峡谷尺寸的安全漏洞!

编辑:你将不得不在你的$sItem变量中设置一个“print”或“echo”。它将不得不在$sItem$sItem = 'echo $aData[\'test\']';),否则你将不得不编写你的​​像这样:Eval ('echo ' . $sData)

+0

不仅EVAL可能 – azat

+0

根据他的问题是。 –

+0

看到我的评论http://stackoverflow.com/a/8356802/328260 – azat

3

你可以使用它像一个普通的数组:

$key = "test"; 

print $aData[$key]; 

同样$aData本身可能是一个更大的阵列存储中的条目。


作为替代,提取使用正则表达式和遍历一个匿名数组的数组潜力键(应该在你的问题曾提到,如果)与引用是可能的。请参阅Set multi-dimensional array by key path from array values?及类似主题。


个人我使用的构建体像这样利用像varname[keyname]代替动态可变的路径(类似于PHP如何解释GET参数)。这只是在绵羊服装一个eval(不与EVAL危言耸听同意虽然):

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

除了可能意味着打印$ aData [$ key]而不是$ aData [$ test],这是迄今为止最好的解决方案。 +1 –

+1

如果你忽略了问题,那么这是最好的解决方案,是的! :) –

+0

+1。我认为这是唯一合理的工作解决方案。无论其他人如何使用'eval',他们都不会返回_key_'test'。可能有一些正则表达式字符串解析会实现提取关键字,但eval永远不会设法根据OP的字符串的方式做到这一点。 – Shef

1
$sItem = '$aData[\'test\']'; 
eval('$someVar = '.$sItem.';'); 
echo $someVar; 

使用eval()高度谨慎,因为其他人已经解释过。

4

没有EVAL必要的,如果你有(或可以获得)数组名称和主要为独立的变量:

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

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

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

您可以使用此方法

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

这是一个好方法。然而,一个'preg_match_all'只需一次获取数组键,就可以解析'''解析']'。 – mario

+0

@mario谢谢。给我这样的请给,因为这样的密钥不能存在阵列 – azat

+0

@mario,你pcre不工作 – azat