在将用户输入的PHP代码传递给eval()之前验证它

时间:2011-08-08 09:04:40

标签: php

在将字符串传递给eval()之前,我想确保语法正确并允许:

  1. 两个功能:a()和b()
  2. 四个运营商:/ * - +
  3. 括号:()
  4. 数字:1.2,-1,1
  5. 我怎么能这样做,也许它与PHP Tokenizer有关?

    我实际上是在尝试创建一个简单的公式解释器,因此a()和b()将被ln()和exp()替换。我不想从头开始编写tokenizer和parser。

5 个答案:

答案 0 :(得分:3)

就验证而言,以下字符标记有效:

operator: [/*+-]
funcs:    (a\(|b\()
brackets: [()]
numbers:  \d+(\.\d+)?
space:    [ ]

然后,简单的验证可以检查输入字符串是否匹配这些模式的任何组合。因为funcs令牌非常精确并且与其他令牌没有太大冲突,所以这种验证应该非常稳定,而不需要实现任何语法/语法:

$tokens = array(
    'operator' => '[/*+-]',
    'funcs' => '(a\(|b\()',
    'brackets' => '[()]', 
    'numbers' => '\d+(\.\d+)?',
    'space' => '[ ]',
);

$pattern = '';
foreach($tokens as $token)
{
    $pattern .= sprintf('|(?:%s)', $token);
}
$pattern = sprintf('~^(%s)*$~', ltrim($pattern, '|'));

echo $pattern;

仅当整个输入字符串与基于令牌的模式匹配时,才会进行验证。它仍然可能在语法上是错误的PHP,你可以确保它只是建立在指定的标记上:

~^((?:[/*+-])|(?:(a\(|b\())|(?:[()])|(?:\d+(\.\d+)?)|(?:[ ]))*$~

如果您动态构建模式 - 如示例所示 - 您可以在以后更轻松地修改语言标记。

此外,这可能是您自己的tokenizer / lexer的第一步。然后,令牌流可以传递给解析器,解析器可以在语法上验证和解释它。这是user187291 wrote about部分。

除了编写完整的词法分析器+解析器,并且需要验证语法之外,您还可以基于标记来制定语法,然后在输入的标记表示上执行基于正则表达式的标记语法。

令牌是你在语法中使用的词。您需要在标记中更精确地描述括号和函数定义,并且标记生成器应遵循更明确的规则,该标记取代另一个标记。 another question of mine概述了这一概念。它也使用正则表达式进行语法表达和语法验证,但它仍然无法解析。在您的情况下,eval将是您正在使用的解析器。

答案 1 :(得分:2)

Parser生成器确实已经为PHP编写了,“LIME”特别带有典型的“计算器”示例,这对于您的“迷你语言”来说是一个明显的起点:http://sourceforge.net/projects/lime-php/

我上次玩LIME已经好几年了,但它已经成熟了。那么稳定。

注意:

1)使用全开解析器生成器为您提供了完全避免PHP eval()的优势 - 如果您愿意,可以使LIME发出一个解析器,它有效地为您的迷你语言编写的表达式提供“eval”函数(随着验证烘焙)。这为您提供了额外的优势,允许您根据需要添加对新功能的支持。

2)首先使用解析器生成器来完成这样一个看起来很小的任务似乎有些过分,但是一旦你得到了这些例子,你就会对修改和扩展它们的难易程度印象深刻。并且很容易低估从头开始编写无错误解析器(甚至是“琐碎的”解析器)的难度。

答案 2 :(得分:0)

是的,你需要Tokenizer或类似的东西,但它只是故事的一部分。标记化器(通常称为“词法分析器”)只能读取和解析表达式的元素,但无法检测到类似“foo()+ * bar”的内容无效。您需要第二部分,称为parser,它可以在一种树(称为“AST”)中排列令牌,或者在未能这样做时提供错误消息。具有讽刺意味的是,一旦你有了一棵树,就不再需要“eval”,你可以直接从树上评估你的表达。

我建议你手工编写一个解析器,因为它是一个非常有用的练习和很多乐趣。 Recursive descent parsers很容易编程。

答案 3 :(得分:0)

您可以使用token_get_all(),检查每个令牌,并在第一个无效令牌中止。

答案 4 :(得分:0)

hakre的答案,使用正则表达式是一个很好的解决方案,但是有点复杂。处理功能白名单变得相当混乱。如果这出错了,它可能会对您的系统产生非常恶劣的影响。

您是否有理由不使用javascript'eval'?