草庐IT

如果没有数据传输,PHP 流超时

coder 2024-04-12 原文

我目前正在实现一个 PHP 类,它可以获取图像文件并将它们缓存在本地。这些图像可能来自其他本地源,通过 HTTP 或使用 Guzzle 客户端的 HTTP。使用 PHP 流包装器,我应该能够以相同的方式处理所有源。

我现在要做的是在没有数据通过流传输时实现超时。这应该处理以下情况:

  1. 首先无法建立流。这可能应该在 fopen 调用时处理,而不是超时。
  2. 流已建立但没有数据传输。
  3. 流已建立,数据已传输,但在传输过程中停止了一段时间。

我想我可以用 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 秒。请问有人可以解释一下吗?

最佳答案

我建议您使用 file_get_contentsfile_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 时开始。现在来测试你的三个条件。

验证行为

  1. The stream cannot be established in the first place. This should probably be handled at the fopen call and not with a timeout.

这按原样正常工作——如果无法建立连接(服务器离线等),fopen 调用会立即触发警告。只需将脚本指向本地主机上没有任何监听的任意端口。 请注意,如果连接未成功建立,fopen 将返回 false。您必须在代码中检查这一点,以避免将 false 用作流。

  1. The stream is established but no data is transferred.

这种情况也适用,只需使用您的普通 URL 运行脚本即可。这也会使 fopen 返回 false 并触发警告(不同的警告)。

  1. The stream is established, data is transferred but it stops some time during transfer.

这是一个有趣的案例。要对此进行测试,您可以编写一个脚本来发送 Content-Length 和一些其他 header 以及一些部分数据,然后等到超时,即:

<?php
header('Content-Type: text/plain');
header('Content-Length: 10');
echo "hi";
ob_flush();
sleep(10);

ob_flush 是使 PHP 在 sleep 和脚本退出之前写入输出(不关闭连接)所必需的。您可以使用 php -S localhost:port 提供此服务,然后将另一个脚本指向 localhost:port。在这种情况下,客户端脚本不会抛出警告,fopen 实际上会返回元数据中的 timed_out 设置为 true 的流。

结论

stream_set_timeout 不适用于 HTTP GET 请求和 fopen 阻塞模式 因为 fopen 执行请求时它被调用而不是等待读取来这样做。您可以通过超时将上下文传递给 fopen 来解决此问题。

关于如果没有数据传输,PHP 流超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49054102/

有关如果没有数据传输,PHP 流超时的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - rails 目前在重启后没有安装 - 2

    我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  8. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  9. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  10. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

随机推荐