Из внешнего источника я получаю строки вроде
array(1,2,3)
но и более крупные массивы вроде
array("a", "b", "c", array("1", "2", array("A", "B")), array("3", "4"), "d")
Мне нужно, чтобы они были фактическим массивом в php. Я знаю, что могу использовать eval, но поскольку это ненадежные источники, я бы предпочел не делать этого. Я также не контролирую внешние источники.
Должен ли я использовать некоторые регулярные выражения для этого (если да, что) или есть какой-то другой способ?
При написании парсера с использованием 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.
Вы можете сделать:
json_decode(str_replace(array('array(', ')'), array('[', ']'), $string)));
Замените массив квадратными скобками. Затем json_decode
. Если строка представляет собой просто многомерный массив со скалярными значениями в нем, то выполнение str_replace
ничего не сломает, и вы можете json_decode
. Если он содержит какой-либо код, он также заменит скобки функций, а затем Json не будет действителен и возвращается NULL
.
Конечно, это довольно, umm, творческий подход, но может работать для вас.
Изменить: Также см. Комментарии для некоторых дальнейших ограничений, отмеченных другими пользователями.
Я думаю, для этого вам следует использовать Tokenizer . Возможно, я напишу сценарий, который на самом деле это делает.