代码之家  ›  专栏  ›  技术社区  ›  Nin

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

  •  4
  • Nin  · 技术社区  · 14 年前

    从一个外部来源我得到的字符串

    array(1,2,3)
    

    还有一个更大的数组

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

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

    3 回复  |  直到 14 年前
        1
  •  11
  •   NikiC    14 年前

    在使用标记器编写解析器时,我想到了另一个想法:为什么不使用标记器解析数组 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.
    
        2
  •  6
  •   Gordon Haim Evgi    14 年前

    你可以做:

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

    用方括号替换数组。那么 json_decode str_replace 不会弄坏任何东西你可以 json解码 是的。如果它包含任何代码,它还将替换函数括号,那么Json将无效且无效 NULL

    当然,这是一个相当,嗯,创造性的方法,但可能适合你。

    另外,请参阅评论以了解其他用户指出的一些进一步限制。

        3
  •  2
  •   NikiC    14 年前

    我认为你应该使用 Tokenizer