Имитация конструкции языка массива php или анализ с помощью регулярных выражений? - PullRequest
4 голосов
/ 16 июля 2010

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

array(1,2,3)

, но также и большие массивы, такие как

array("a", "b", "c", array("1", "2", array("A", "B")), array("3", "4"), "d")

Мне нужно, чтобы они были фактическим массивом в php. Я знаю, что мог бы использовать eval, но так как это ненадежные источники, я бы предпочел этого не делать. У меня также нет контроля над внешними источниками.

Должен ли я использовать некоторые регулярные выражения для этого (если да, что) или есть какой-то другой способ?

Ответы [ 3 ]

11 голосов
/ 17 июля 2010

Во время написания парсера с использованием 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.
6 голосов
/ 16 июля 2010

Вы можете сделать:

json_decode(str_replace(array('array(', ')'), array('[', ']'), $string)));

Заменить массив квадратными скобками.Тогда json_decode.Если строка является просто многомерным массивом со скалярными значениями в нем, то выполнение str_replace ничего не сломает, и вы можете json_decode это сделать.Если он содержит какой-либо код, он также заменит функциональные скобки, и тогда Json не будет действительным, и будет возвращено NULL.

Конечно, это довольно-таки творческий подход, но может работатьВы.

Редактировать: Кроме того, см. комментарии для некоторых дополнительных ограничений, указанных другими пользователями.

2 голосов
/ 16 июля 2010

Я думаю, вы должны использовать Tokenizer для этого. Может быть, позже я напишу сценарий, который действительно делает это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...