什么时候(如果有的话) eval 不是邪恶的?
我听说过很多放置 PHP 的eval
函数是 通常不是答案。根据 PHP 5.3 的 LSB 和 < a href="http://php.net/manual/en/functions.anonymous.php" rel="nofollow noreferrer">闭包 我们已经没有理由依赖 eval 或
create_function
。
是否存在任何可能的情况,其中eval
是 PHP 5.3 中最好(唯一?)的答案?
这个问题不是关于eval
总体上是否是邪恶的,因为它显然不是。
答案摘要:
- 评估数值表达式(或 PHP 的其他“安全”子集)
- 单元测试
- 交互式 PHP “shell”
- 受信任的
var_export
反序列化 - 一些模板语言
- 为管理员和/或黑客创建后门
- 与
var_export
的兼容性PHP 5.3 - 检查语法(可能不安全)
I've heard many places that PHP's eval
function is often not the answer. In light of PHP 5.3's LSB and closures we're running out of reasons to depend on eval
or create_function
.
Are there any conceivable cases where eval
is the best (only?) answer in PHP 5.3?
This question is not about whether eval
is evil in general, as it obviously is not.
Summary of Answers:
- Evaluating numerical expressions (or other "safe" subsets of PHP)
- Unit testing
- Interactive PHP "shell"
- Deserialization of trusted
var_export
- Some template languages
- Creating backdoors for administers and/or hackers
- Compatibility with < PHP 5.3
- Checking syntax (possibly not safe)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
如果您正在编写恶意软件,并且想让试图清理您的系统管理员的日子不好过。根据我的经验,这似乎是最常见的用例。
If you're writing malware and you want to make life hard for the sysadmin who's trying to clean up after you. That seems to be the most common usage case in my experience.
Eric Lippert 总结了三篇博客文章的评估。这是一本非常有趣的读物。
据我所知,以下是使用 eval 的一些唯一原因。
Eric Lippert sums eval up over three blog posts. It's a very interesting read.
As far as I'm aware, the following are some of the only reasons eval is used.
eval
的主要问题是它成为恶意代码的网关。因此,您永远不应该在可以从外部利用它的上下文中使用它,例如用户提供的输入。一种有效的用例是模拟框架。
来自
PHPUnit_Framework_TestCase::getMock()
的示例实际上,generate 方法中发生了很多事情。通俗地说:PHPUnit 将采用
generate
的参数并从中创建一个类模板。然后它将eval
该类模板以使其可用于实例化。当然,这样做的目的是让 TestDoubles 模拟 UnitTests 中的依赖关系。The main problem with
eval
is it being a gateway for malicious code. Thus you should never use it in a context where it can be exploited from the outside, e.g. user provided input.One valid UseCase would be in Mocking Frameworks.
Example from
PHPUnit_Framework_TestCase::getMock()
There is actually a lot of things happening in the generate method. In laymens terms: PHPUnit will take the arguments to
generate
and create a class template from it. It will theneval
that class template to make it available for instantiation. The point of this is to have TestDoubles to mock dependencies in UnitTests of course.如果您正在编写一个解释和执行 PHP 代码的网站,就像交互式 shell 一样。
...
我是一个系统人员,这就是我所拥有的一切。
If you are writing a site that interprets and executes PHP code, like an interactive shell would.
...
I'm a systems guy, that's all I got.
您可以使用 eval 来创建临时类:
当然,尽管使用非常有限(一些 API 或可能是 DI 容器、测试框架、必须处理具有动态结构的数据库的 ORM、代码游乐场)
You can use eval to create ad-hoc classes:
Although, of course, usage is quite limited (some API's or maybe DI containers, testing frameworks, ORMs which have to deal with databases with dynamic structure, code playgrounds)
eval
是一个可用于检查语法错误的构造。假设您有以下两个 PHP 脚本:
script1.php
script2.php
您可以使用
eval
检查语法错误:与 php_check_syntax (无论如何已弃用并删除),代码将不会被执行。
编辑:
另一个(首选)替代方案是 php -l 。如果您无权访问 system() 或 shell 执行命令,则可以使用上述解决方案。
此方法可以在代码中注入类/函数。执行此操作之前,请务必强制执行
preg_replace
调用或namespace
,以防止它们在后续调用中执行。至于OP主题:什么时候(如果有的话)eval不是邪恶的?
eval
根本就不是邪恶的。程序员无缘无故地使用eval
是邪恶的。eval
可以缩短您的代码(例如数学表达式评估)。eval
is a construct that can be used to check for syntax errors.Say you have these two PHP scripts:
script1.php
script2.php
You can check for syntax errors using
eval
:Unlike php_check_syntax (which is deprecated and removed anyway), the code will not be executed.
EDIT:
The other (preferred) alternative being
php -l
. You can use the solution above if you don't have access to system() or shell execution commands.This method can inject classes/functions in your code. Be sure to enforce a
preg_replace
call or anamespace
before doing so, to prevent them from being executed in subsequent calls.As for the OP topic: When (if ever) is eval NOT evil?
eval
is simply not evil. Programmers are evil for usingeval
for no reason.eval
can shorten your code (mathematical expression evaluation, per example).我发现有时一种语言的大多数功能都是有用的。毕竟,即使
GOTO
有其支持者。 Eval 在许多框架中都有使用,并且使用得很好。例如,CodeIgniter 使用eval
来区分 PHP 4 和 PHP 5 实现的类层次结构。允许执行 PHP 代码的博客插件肯定需要它(这是 Expression Engine、Wordpress 等中可用的功能)。我还将它用于一个网站,其中一系列视图几乎相同,但每个视图都需要自定义代码,并且创建某种疯狂的规则引擎要复杂得多且速度慢得多。虽然我知道这不是 PHP,但我发现 Python 的 eval 使基本计算器的实现变得更加简单。
基本上,问题是:
其他考虑因素:
I've found that there are times when most features of a language are useful. After all, even
GOTO
has had its proponents. Eval is used in a number of frameworks and it is used well. For example, CodeIgniter useseval
to distinguish between class hierarchy of PHP 4 and PHP 5 implementations. Blog plugins which allow for execution of PHP code definitely need it (and that is a feature available in Expression Engine, Wordpress, and others). I've also used it for one website where a series of views are almost identical, but custom code was needed for each and creating some sort of insane rules engine was far more complicated and slower.While I know that this isn't PHP, I found that Python's eval makes implementation of a basic calculator much simpler.
Basically, here's the question:
eval
(or variable variables, or any other form of string look-up or reflection syntax), there is another way to do it. Have you exhausted your other options? Do you have a reasonably limitted input set? Can a switch statement be used?Other considerations:
合适的场合(考虑到缺乏简单的替代方案)是使用
var_export
序列化可信数据并且有必要对其进行反序列化。当然,它不应该以这种方式序列化,但有时错误已经完成。An appropriate occasion (given the lack of easy alternatives) would be when trusted data was serialized with
var_export
and it's necessary to unserialize it. Of course, it should never have been serialized in that fashion, but sometimes the error is already done.我想,应该在代码实际需要编译的地方使用 eval。我的意思是这样的情况,比如模板文件编译(为了性能而将模板语言编译成 PHP)、插件钩子编译、出于性能原因的编译等。
I suppose, eval should be used where the code is actually needs to be compiled. I mean such cases like template file compilations (template language into PHP for the sake of performance), plugin hook compilation, compilations for performance reasons etc.
您可以使用 eval 创建一个设置,以便在安装系统后添加代码。通常,如果您想更改服务器上的代码,则必须添加/更改现有的 PHP 文件。另一种方法是将代码存储在数据库中并使用 eval 来执行它。不过,您必须确保添加的代码是安全的。
将其视为一个插件,只是一个可以做任何事情的插件...
您可以想到一个网站,该网站允许人们贡献代码片段,然后用户可以将这些代码片段动态添加到他们的网页中 - 而无需他们实际将代码保留在网站上。网络服务器文件系统。不过,您需要的是批准流程......
You could use eval to create a setup for adding code after the system installed. Normally if you would want to change the code on the server you would have to add/change existing PHP files. An alternative to this would be to store the code in a database and use eval to execute it. You'd have to be sure that the code added is safe though.
Think of it like a plugin, just one that can do about anything...
You could think of a site that would allow people to contribute code snippets that the users could then dynamically add into their web pages - without them actually persisting code on the webservers filesystem. What you would need is an approval process though...
这个 eval 争论实际上是对 php 的一个很大的误解。人们被洗脑认为 eval 是邪恶的,但通常他们使用
include
没有问题,尽管 include 本质上是同一件事。 Include foo 与 eval file_get_contents foo 相同,因此每次包含某些内容时,您都会犯下 eval 的大罪。This eval debate is actually one big misunderstanding in context of php. People are brainwasched about eval being evil, but usually they have no problem using
include
, although include is essentially the same thing. Include foo is the same as eval file_get_contents foo, so everytime you're including something you commit the mortal sin of eval.兼容性。提供 PHP4 后备方案是很常见的。但同样,也可能希望在 5.3 中模拟 PHP5.4 功能,例如
SplString
。虽然简单地提供两个包含变体(include.php4 与 include.php5)很常见,但有时使用 eval() 会更有效或更具可读性:在这种情况下,代码可以在 PHP4 上工作,但仅在 PHP5 上公开更好的 API/语法。请注意,该示例是虚构的。
Compatibility. It's quite frequent to provide PHP4 fallbacks. But likewise it's a possible desire to emulate PHP5.4 functionality in 5.3, as example
SplString
. While simply providing two include variants (include.php4 vs. include.php5) is frequent, it's sometimes more efficient or readable to resort to eval():Where in this case the code would work on PHP4, but expose the nicer API/syntax only on PHP5. Note that the example is fictional.
当我有一个 php 引擎的机器人与我通信时,我使用了 eval,我可以告诉它通过
EVAL:此处的 php 命令执行命令
。仍然是邪恶的,但如果您的代码不知道会发生什么(如果您从数据库中提取一段 PHP 代码),那么 eval 是唯一的解决方案。I've used eval when I had a php-engined bot that communicated with me and I could tell it to do commands via
EVAL: php commands here
. Still evil, but if your code has no idea what to expect (in case you pull a chunk of PHP code from a database) eval is the only solution.因此,这对于所有带有
eval
的语言都适用:基本上,除了少数例外,如果您正在构建传递给
eval
的值或从非可信源获取它你做错了什么。如果您在静态字符串上调用 eval,同样如此。除了在运行时初始化解析器的性能问题和安全问题之外,您通常还会弄乱类型系统。
更严重的是,事实证明,在绝大多数情况下,有更优雅的解决方案。然而,与其完全禁止该构造,不如将其视为人们可能
goto
的做法。两者都有合法的用途,但这是一个很好的危险信号,应该让您思考是否以正确的方式解决问题。根据我的经验,我只发现属于插件和特权用户(例如网站管理员,而不是此类用户)扩展类别的合法用途。基本上,充当来自可信来源的代码的东西。
So, this should hold true for all languages with
eval
:Basically, with few exceptions, if you are building the value passed to
eval
or getting it from a non-truested source you are doing something wrong. The same holds true if you are calling eval on a static string.Beyond the performance problems with initializing the parser at runtime, and the security issues, You generally mess with the type system.
More seriously, it's just been shown that in the vast majority of cases, there are much more elegant approaches to the solution. However, instead of banning the construct outright, it's nice to think of it as one might
goto
. There are legitimate uses for both, but it is a good red flag that should get you thinking about if you are approaching the problem the correct way.In my experience, I've only found legitimate uses that fall in the categories of plugins and privileged user (for instance, the administrator of a website, not the user of such) extensions. Basically things that act as code coming from trusted sources.
不是直接使用,而是 preg_replace 的 /e 修饰符利用 eval 并且非常方便。请参阅 http://php.net/preg_replace 上的示例 #4。
它是否是邪恶/坏是主观的,完全取决于你在特定背景下认为“好”的东西。当处理不受信任的输入时,它通常被认为是不好的。但是,在其他情况下它可能很有用。想象一下在极端的截止日期压力下编写一次性数据转换脚本。在这种情况下,如果 eval 起作用并且使事情变得更容易,我就很难称其为邪恶。
Not direct use but the /e modifier to preg_replace utilizes eval and can be quite handy. See example #4 on http://php.net/preg_replace.
Whether or not it's evil/bad is subjective and depends entirely on what you consider "good" in a specific context. When dealing with untrusted inputs it is usually considered bad. However, in other situations it can be useful. Imagine writing a one-time data conversion script under extreme deadline pressure. In this situation, if eval works and makes things easier, I would have trouble calling it evil.