Имитировать конструкцию языка php-массива или проанализировать с помощью регулярного выражения?

Из внешнего источника я получаю строки вроде

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 . Возможно, я напишу сценарий, который на самом деле это делает.