用regexp模拟php数组语言构造或解析?

Nin*_*Nin 4 php regex parsing

从外部来源我得到的字符串就像

array(1,2,3)
Run Code Online (Sandbox Code Playgroud)

但也有更大的阵列

array("a", "b", "c", array("1", "2", array("A", "B")), array("3", "4"), "d")
Run Code Online (Sandbox Code Playgroud)

我需要它们是php中的实际数组.我知道我可以使用eval,但由于它是不受信任的来源,我宁愿不这样做.我也无法控制外部资源.

我应该为此使用一些正则表达式(如果是这样,是什么)还是有其他方法?

Nik*_*kiC 11

虽然使用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")'));
Run Code Online (Sandbox Code Playgroud)

我认为这是安全性和便利性之间的良好比较 - 无需解析自己.

例如

parseArray('exec("haha -i -thought -i -was -smart")');
Run Code Online (Sandbox Code Playgroud)

会抛出异常:

Disallowed token 'T_STRING' encountered.
Run Code Online (Sandbox Code Playgroud)


Gor*_*don 6

你可以这样做:

json_decode(str_replace(array('array(', ')'), array('[', ']'), $string)));
Run Code Online (Sandbox Code Playgroud)

用方括号替换数组.然后json_decode.如果字符串只是一个带有标量值的多维数组,那么执行该操作str_replace不会破坏任何内容,您可以使用json_decode它.如果它包含任何代码,它也将替换函数括号,然后Json将无效并NULL返回.

当然,这是一种相当,嗯,创造性的方法,但可能对你有用.

编辑:另外,请参阅其他用户指出的一些进一步限制的注释.