amorpro / soft-mocks
用于模拟真实源代码的有用工具
Requires
- php: >=5.5
- nikic/php-parser: 2.*
This package is auto-updated.
Last update: 2024-09-13 21:35:35 UTC
README
"Soft Mocks" 的理念(与在 PHP 解释器级别工作的 "hardcore" 模拟器相对)是就地重写类代码,以便可以插入任何位置。它通过在文件包含期间动态重写代码,而不是使用 runkit 或 uopz 等扩展来实现。
用法
使 SoftMocks 与众不同的东西(也是限制其使用的因素)是它们需要在应用程序启动的最早阶段启动(包括所有依赖项)。这样做的原因是您不能重新定义已经加载到 PHP 内存中的类和函数。有关 PHPUnit 引导预设的示例,请参阅 src/bootstrap.php 和 example-phpunit/bootstrap.php。
SoftMocks 不重写以下系统部分
- 自己的代码
- PHPUnit 代码(有关详细信息,请参阅 setPhpunitPath())
- PHP-Parser 代码(有关详细信息,请参阅 setPhpParserPath())
- 已重写的代码
要将外部依赖项(例如,vendor/autoload.php)添加到引导文件中,您需要使用包装器
require_once (\QA\SoftMocks::rewrite('vendor/autoload.php'));
require_once (\QA\SoftMocks::rewrite('path/to/external/lib.php'));
在您通过 SoftMocks::rewrite() 添加文件后,所有嵌套的包含调用都已经由系统本身 "包装"。
您可以通过执行以下命令来查看更详细的示例
[~/Work/soft-mocks]-> php example/run_me.php
Result before applying SoftMocks = array (
'TEST_CONSTANT_WITH_VALUE_42' => 42,
'someFunc(2)' => 84,
'Example::doSmthStatic()' => 42,
'Example->doSmthDynamic()' => 84,
'Example::STATIC_DO_SMTH_RESULT' => 42,
)
Result after applying SoftMocks = array (
'TEST_CONSTANT_WITH_VALUE_42' => 43,
'someFunc(2)' => 57,
'Example::doSmthStatic()' => 'Example::doSmthStatic() redefined',
'Example->doSmthDynamic()' => 'Example->doSmthDynamic() redefined',
'Example::STATIC_DO_SMTH_RESULT' => 'Example::STATIC_DO_SMTH_RESULT value changed',
)
Result after reverting SoftMocks = array (
'TEST_CONSTANT_WITH_VALUE_42' => 42,
'someFunc(2)' => 84,
'Example::doSmthStatic()' => 42,
'Example->doSmthDynamic()' => 84,
'Example::STATIC_DO_SMTH_RESULT' => 42,
)
API(简短描述)
初始化 Soft Mocks(设置 phpunit 注入,定义内部模拟,获取内部函数列表等)
\QA\SoftMocks::init();
默认情况下,缓存文件创建在 /tmp/mocks。如果您想选择不同的路径,可以按照以下方式重新定义
\QA\SoftMocks::setMocksCachePath($cache_path);
重新定义常量
您可以将新值分配给 $constantName 或创建一个新常量(如果尚未声明)。由于它不是使用 define() 调用来创建的,因此可以取消操作。
支持 "常规常量" 和类常量,如 "className::CONST_NAME"。
\QA\SoftMocks::redefineConstant($constantName, $value)
重新定义函数
Soft Mocks 允许您重新定义用户定义的函数和内置函数,但除外那些依赖于当前上下文(如果您想查看完整的列表,请参阅 \QA\SoftMocksTraverser::$ignore_functions 属性)或具有内置模拟的函数(debug_backtrace、call_user_func* 以及一些其他函数)。
定义
\QA\SoftMocks::redefineFunction($func, $functionArgs, $fakeCode)
用法示例(重新定义 strlen 函数并调用原始的 trim 字符串)
\QA\SoftMocks::redefineFunction(
'strlen',
'$a',
'return \\QA\\SoftMocks::callOriginal("strlen", [trim($a)]));'
);
var_dump(strlen(" a ")); // int(1)
重新定义方法
目前,仅支持用户定义方法的重新定义。对于内置类,不支持此功能。
定义
\QA\SoftMocks::redefineMethod($class, $method, $functionArgs, $fakeCode, $strict = true)
参数与 redefineFunction 相同,但引入了 $class 参数,并且可以在非严格模式下工作($strict = false)。如果选择非严格模式,则在重新定义类方法时将模拟 runkit 的行为,因此除了 $class 方法本身外,其祖先方法也将被重新定义。
参数 $class 接受类名或特质名。
重新定义生成器函数
此方法允许您将生成器函数调用替换为另一个 \Generator。生成器与常规函数不同,您不能使用 "return" 返回值;您必须使用 "yield"。
\QA\SoftMocks::redefineGenerator($class, $method, \Generator $replacement)
恢复值
以下函数取消使用上述描述的任何重新定义方法所做的模拟。
\QA\SoftMocks::restoreAll()
// You can also undo only chosen mocks:
\QA\SoftMocks::restoreConstant($constantName)
\QA\SoftMocks::restoreAllConstants()
\QA\SoftMocks::restoreFunction($func)
\QA\SoftMocks::restoreMethod($class, $method)
\QA\SoftMocks::restoreGenerator($class, $method)
常见问题解答
问题:如何防止重新定义特定的函数/类/常量?
答案:使用 \QA\SoftMocks::ignore(Class|Function|Constant) 方法。
问题:我无法覆盖某些函数调用:call_user_func(_array)?、defined 等。
答案:有许多函数具有其自己的内置模拟,默认情况下无法拦截。以下是不完整的列表
- call_user_func_array
- call_user_func
- is_callable
- function_exists
- constant
- defined
- debug_backtrace
因此,您可以在需要时通过调用 \QA\SoftMocks::setRewriteInternal(true)
来启用对它们的拦截,但在启用时要小心。例如,如果 strlen 和 call_user_func(_array) 被重新定义,那么您可能会得到不同的 strlen 结果
\QA\SoftMocks::redefineFunction('call_user_func_array', '', 'return 20;'); \QA\SoftMocks::redefineFunction('strlen', '', 'return 5;'); ... strlen('test'); // will return 5 call_user_func_array('strlen', ['test']); // will return 20 call_user_func('strlen', 'test'); // will return 5
问:我是如何使用 Soft Mocks 与 PHPUnit 一起工作的?
答:您需要将我们的 pull request sebastianbergmann/phpunit#2116 合并到您的 phpunit 版本中,或者直接使用这个分支。即使没有对 phpunit 进行任何补丁,Soft Mocks 也能正常工作,但您将看到失败的测试的“不可读”堆栈跟踪,并且您无法重新定义测试本身中定义的类和方法。
问:Soft Mocks 与 PHP7 兼容吗?
答:是的。Soft Mocks 整个理念是,它将在所有后续的 PHP 版本中继续工作,而无需像 runkit 和 uopz 那样进行全面系统重写。
问:Soft Mocks 与 HHVM 兼容吗?
答:在撰写此问答时,似乎 Soft Mocks 在使用 HHVM(HipHop VM 3.12.1 (rel))时确实可以工作。我们内部不使用 HHVM,因此可能存在一些未涵盖的边缘情况。我们感谢任何有关 HHVM 支持的问题/pull request。
问:为什么我会得到解析错误或类似于“PhpParser::pSmth is undefined”的致命错误?
答:Soft Mocks 使用自定义的 PHP Parser 美化打印机,它似乎与所有 PHP Parser 版本都不兼容。请使用我们提供的版本,直到我们找到解决这个问题的方法。