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