在使用标记器编写解析器时,我想到了另一个想法:为什么不使用标记器解析数组
eval
,但首先确认它不含任何有害物质?
因此,代码所做的是:它检查数组的令牌与一些允许的令牌和字符,然后执行eval。我真的希望我包括所有可能的无害代币,如果没有,只是添加它们(我故意不包括HEREDOC和NOWDOC,因为我认为它们不太可能被使用。)
function parseArray($code) {
$allowedTokens = array(
T_ARRAY => true,
T_CONSTANT_ENCAPSED_STRING => true,
T_LNUMBER => true,
T_DNUMBER => true,
T_DOUBLE_ARROW => true,
T_WHITESPACE => true,
);
$allowedChars = array(
'(' => true,
')' => true,
',' => true,
);
$tokens = token_get_all('<?php '.$code);
array_shift($tokens); // remove opening php tag
foreach ($tokens as $token) {
// char token
if (is_string($token)) {
if (!isset($allowedChars[$token])) {
throw new Exception('Disallowed token \''.$token.'\' encountered.');
}
continue;
}
// array token
// true, false and null are okay, too
if ($token[0] == T_STRING && ($token[1] == 'true' || $token[1] == 'false' || $token[1] == 'null')) {
continue;
}
if (!isset($allowedTokens[$token[0]])) {
throw new Exception('Disallowed token \''.token_name($token[0]).'\' encountered.');
}
}
// fetch error messages
ob_start();
if (false === eval('$returnArray = '.$code.';')) {
throw new Exception('Array couldn\'t be eval()\'d: '.ob_get_clean());
}
else {
ob_end_clean();
return $returnArray;
}
}
var_dump(parseArray('array("a", "b", "c", array("1", "2", array("A", "B")), array("3", "4"), "d")'));
我认为这是安全性和方便性之间的一个很好的折衷——不需要自己分析。
例如
parseArray('exec("haha -i -thought -i -was -smart")');
将引发异常:
Disallowed token 'T_STRING' encountered.