在 PHP 中处理 include/require 指令
背景:我正在为 PHP 应用程序构建一个自动化测试框架,我需要一种方法来有效地 "stub out" 封装与外部系统通信的类.例如,当测试使用 DB 包装类 Y 的类 X 时,我希望能够在对类 X 运行自动化测试时换入"类 Y 的假"版本(这样我就不必这样做了)作为测试的一部分,完整设置 + 拆除真实数据库的状态).
Background: I'm building an automated test framework for a PHP application, and I need a way to efficiently "stub out" classes which encapsulate communication with external systems. For example, when testing class X that uses DB wrapper class Y, I would like to be able to "swap in" a "fake" version of class Y while running automated tests on class X (this way I don't have to do full setup + teardown of the state of the real DB as part of the test).
问题:PHP 允许条件包含",这意味着 include/require 指令基本上是作为处理文件主"逻辑的一部分来处理的,例如:
Problem: PHP allows "conditional includes", which means basically that include/require directives are handled as part of processing the "main" logic of a file, e.g.:
if (condition) {
require_once('path/to/file');
}
问题是我无法弄清楚当包含文件的主要"逻辑调用返回"时会发生什么.包含文件中的所有对象(定义、类、函数等)是否都导入到调用 include/require 的文件中?或者处理会随着退货而停止?
The problem is that I can't figure out what happens when the "main" logic of the included file calls "return". Are all of the objects (defines, classes, functions, etc.) in the included file imported into the file which calls include/require? Or does processing stop with the return?
示例:考虑这三个文件:
A.inc
define('MOCK_Z', true);
require_once('Z.inc');
class Z {
public function foo() {
print "This is foo() from a local version of class Z.
";
}
}
$a = new Z();
$a->foo();
B.inc
define('MOCK_Z', true);
require_once('Z.inc');
$a = new Z();
$a->foo();
Z.inc
if (defined ('MOCK_Z')) {
return true;
}
class Z {
function foo() {
print "This is foo() from the original version of class Z.
";
}
}
我观察到以下行为:
$ php A.inc
> This is foo() from a local version of class Z.
$ php B.inc
> This is foo() from the original version of class Z.
这很奇怪:如果 require_once() 包含所有定义的代码对象,那么php A.inc"应该用类似
Why This is Strange: If require_once() included all of the defined code objects, then "php A.inc" ought to complain with a message like
Fatal error: Cannot redeclare class Z
如果 require_once() 只包含定义的代码对象直到返回",那么php B.inc"应该抱怨如下消息:
And if require_once() included only the defined code objects up to "return", then "php B.inc" ought to complain with a message like:
Fatal error: Class 'Z' not found
问题:谁能在这里解释准确 PHP 正在做什么?这对我来说实际上很重要,因为我需要一个强大的习惯用法来处理模拟"类的包含.
Question: Can anyone explain exactly what PHP is doing, here? It actually matters to me because I need a robust idiom for handling includes for "mocked" classes.
推荐答案
我已经考虑了一段时间了,没有人能够为我指出一个清晰一致的 PHP 方式解释(最高 5.3无论如何)过程包括.
I've thought about this for a while now, and nobody has been able to point me to a clear and consistent explanation for the way PHP (up to 5.3 anyway) processes includes.
我的结论是,最好完全避免这个问题,并通过 自动加载:
I conclude that it would be better to avoid this issue entirely and achieve control over "test double" class substitution via autoloading:
spl-autoload-register
换句话说,用 require_once() 替换每个 PHP 文件顶部的包含,它引导"一个定义自动加载逻辑的类.并且在编写自动化测试时,在每个测试脚本的顶部为要模拟"的类注入"替代自动加载逻辑.
In other words, replace the includes at the top of each PHP file with a require_once() which "bootstraps" a class which defines the logic for autoloading. And when writing automated tests, "inject" alternative autoloading logic for the classes to be "mocked" at the top of each test script.
按照这种方法修改现有代码自然需要付出大量努力,但无论是提高可测试性还是减少代码库中的总行数,这些努力似乎都是值得的.
It will naturally require a good deal of effort to modify existing code to follow this approach, but the effort appears to be worthwhile both to improve testability and to reduce the total number of lines in the codebase.
相关文章