我目前正在实现一个PHP类,它可以获取图像文件并在本地缓存它们。这些图像可能来自其他本地来源,通过HTTP或使用Guzzle客户端通过HTTP。使用PHP流包装器,我应该能够以相同的方式处理所有源。
如果没有数据通过流传输,我现在要做的就是实现超时。这应该处理以下情况:
fopen
调用时处理,而不是超时。我想我可以用stream_set_timeout
完成所有这些工作,但我并不清楚这实际上是做什么的。如果流上的任何操作花费的时间超过允许的时间,那么超时是否适用,即我可以做一些需要0.5秒并且超时为0.75秒的事情?或者它是否仅适用于没有数据通过流传输的时间超过允许的时间?
我尝试使用这个简短的脚本测试行为:
<?php
$in = fopen('https://reqres.in/api/users?delay=5', 'r');
$out = fopen('out', 'w');
stream_set_timeout($in, 1);
stream_copy_to_stream($in, $out);
var_dump(stream_get_meta_data($in)['timed_out']);
虽然reqres.in
的响应延迟了5秒,但我总是得到false
,超时为1秒。请有人解释一下吗?
答案 0 :(得分:5)
我建议您使用file_get_contents
和file_put_contents
而不是流,它们支持所有包装器,您可以将上下文传递给它们,就像fopen
一样。它们通常更容易使用,因为它们返回并接受字符串而不是流。话虽这么说,我不知道你的缓存机制的性质,如果流对你的用例更好,对你更有力量:))
这里的问题似乎是误解了fopen
如何在阻塞模式下使用http
流包装器(在我尝试之前我还没有完全理解)。对于GET(the default),fopen
似乎在通话时执行HTTP请求,在读取流时不。这可以解释为什么stream_set_timeout
无法按预期运行,因为它会在调用fopen
后修改流上下文。
幸运的是,有一种方法可以在调用fopen
之前修改超时,而不是;您可以使用上下文调用fopen
。对于所有三种情况,将stream_context_create
(与Sammitch链接)返回的上下文正确地传递给fopen
超时。作为参考,这是您的脚本的修改方式:
<?php
$ctx = stream_context_create(['http' => [
'timeout' => 1.0,
]]);
$in = fopen('https://reqres.in/api/users?delay=5', 'r', false, $ctx);
$out = STDOUT;
stream_copy_to_stream($in, $out);
var_dump(stream_get_meta_data($in)['timed_out']);
fclose($in);
注意:我假设您打算将流复制到stdout而不是“out”,这不是我平台上的有效流(Darwin)。我也在脚本结尾处输入了流,这总是很好的做法。
这将创建一个超时为1的流,从调用fopen
开始。现在来测试你的三个条件。
- 首先无法建立流。这可能应该在fopen调用时处理,而不是超时。
醇>
这样可以正常工作 - 如果无法建立连接(服务器脱机等),fopen
调用会立即触发警告。只需将脚本指向localhost上的任意端口,即没有任何内容正在侦听。 请注意,如果未成功建立连接,fopen
将返回false。您必须在代码中检查该内容,以避免将false用作流。
- 已建立流,但未传输任何数据。
醇>
这种情况也适用,只需使用普通网址运行脚本即可。这也使fopen
返回false并触发警告(另一个)。
- 建立流,传输数据但在传输过程中停止一段时间。
醇>
这是一个有趣的案例。要测试这个,你可以编写一个脚本,发送Content-Length
和其他一些标题以及一些部分数据,然后等到超时,即:
<?php
header('Content-Type: text/plain');
header('Content-Length: 10');
echo "hi";
ob_flush();
sleep(10);
在睡眠和脚本退出之前,ob_flush
是使PHP写入输出(不关闭连接)所必需的。您可以使用php -S localhost:port
投放此邮件,然后将其他脚本指向localhost:port
。在这种情况下,客户端脚本不会发出警告,fopen
实际上会将元数据集中timed_out
的流返回true。
stream_set_timeout
无法使用HTTP GET请求,fopen
处于阻止模式,因为fopen
在调用请求时执行请求,而不是等待读取这样做。您可以使用超时将上下文传递给fopen
以解决此问题。
答案 1 :(得分:2)
“读取超时”与“连接超时”之间存在差异 ..
连接超时是进行初始连接的超时(完成TCP连接握手)。 读取超时是等待读取数据的超时。如果服务器在最后一个字节后XX秒没有发送字节,则会生成读取超时错误。
即使您看到延迟(响应时间)为5秒 - 这可能发生在初始连接(DNS查找,连接等)期间,而不是在您阅读期间。