如何模拟内置的 php 套接字函数


How to mock built-in php socket functions?

我正在处理一些从套接字读取的代码,当它获得某个大输入时它会出错。在修复它之前,我为此添加了一个单元测试,但由于我无法模拟fread(以及我正在使用的其他 PHP 内置函数,如 fsockopenfeof 等)而卡住了。

简单来说,我的问题是此代码失败并显示"致命错误:无法重新声明 fgets() ...":

function fgets($fp){
return "xxx";
}
我意识到我可以创建一个

套接字包装器类,我的真实代码使用它,然后我可以为该包装类创建一个模拟对象。但那是摇尾巴的狗,我可以想到为什么这是一个坏主意,而不仅仅是事情的原理。(例如,使代码更复杂,效率更高,必须重构尚未测试的代码。

所以,我的问题是如何在单元测试中用我自己的实现替换内置fgets()实现?(或者,如果你想跳出框框思考,这个问题可以表述为:当$socket是调用fsockopen的返回值时,我如何控制调用fgets($socket)返回的字符串?


旁白

按照正确答案的要求安装 apd 是一项艰苦的工作;它最后一次发布是在 2004 年,并且不支持开箱即用的 php 5.3。没有 Ubuntu 软件包,也pecl install apd失败了。所以这里是安装它的过程(这些是针对 ubuntu 10.04 的)(全部以 root 身份完成):

pecl download apd
tar xzf apd-1.0.1.tgz
cd apd-1.0.1
phpize
./configure
# Do edits (see below)
make install

请参阅 https://bugs.php.net/bug.php?id=58798 了解您需要执行的修补程序。 注意:只有一行你真正需要更改,所以你可以手动完成,如下所示: 在文本编辑器中打开 php_apd.c,转到第 967 行,然后将CG(extended_info) = 1行替换为这一行:

CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;

最后,您需要添加一个 php.ini 条目来激活它。请参阅 http://php.net/manual/en/apd.installation.php

如果您无法访问 APD 或 Runkit 但正在使用命名空间,请尝试以下操作: https://stackoverflow.com/a/5337635/664108 (链接中的答案是指 time(),但没有区别)

看看这些:

布尔rename_function (字符串 $original_名称 , 字符串 $new_名称 )

bool override_function ( 字符串 $function_name , 字符串 $function_

args , 字符串 $function_code )

fsockopen更改为fopen进行模拟,并且不更改任何其他函数。

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);

$fp = fopen("/path/to/your/dummy_data_file");

我在博客上写了我的经验,其中包含完整的工作代码,展示了如何使用override_function来实现预期目标;文件读取和套接字读取。我不会在这里重复整篇文章,而只是指出您必须如何使用这些功能:

  1. 使用 rename_function 为旧的原始函数命名,因此我们可以稍后找到它。
  2. 使用override_function来定义新行为
  3. 使用rename_function__overridden__指定一个虚拟名称

如果您希望之后能够恢复原始行为,则第 1 步至关重要。

在最后的"思考的食粮"部分,我展示了一种我认为更健壮的替代方法,因此我认为在使用phpUnit时替换文件函数更安全。它创建一个永久的函数替换,其默认行为是转发到内置函数。然后,它会检查参数(在本例中为资源$handle),以确定是否在我们希望不同行为的流上调用它。(我认为你可以称之为责任链设计模式的一个例子。