【问题标题】:Why use output buffering in PHP?为什么在 PHP 中使用输出缓冲?
【发布时间】:2011-01-10 00:36:29
【问题描述】:

我在 Internet 上阅读了很多资料,其中不同的作者建议使用输出缓冲。有趣的是,大多数作者只支持它的使用,因为它允许将响应标头与实际内容混合。坦率地说,我认为负责任的 Web 应用程序不应该混合输出标头和内容,Web 开发人员应该在其脚本中寻找可能导致标头在生成输出后发送的逻辑缺陷。这是我反对ob_* 输出缓冲API 的第一个论点。即使您获得的一点点便利——将标头与输出混合——也不是使用它的充分理由,除非需要快速破解脚本,这通常不是严肃的 Web 应用程序的目标,也不是方法。

另外,我认为大多数处理输出缓冲 API 的人并没有考虑这样一个事实,即即使没有启用显式输出缓冲,PHP 与它所插入的 Web 服务器相结合,仍然会做一些内部无论如何缓冲。很容易检查 - 对一些短字符串进行回显,休眠 10 秒,然后再进行回显。使用浏览器请求您的脚本并观察空白页暂停 10 秒钟,然后两行都出现。之前有人说它是渲染人工制品,而不是流量,跟踪客户端和服务器之间的实际流量表明服务器已经为整个输出生成了具有适当值的 Content-Length 标头 - 表明未发送输出随着每个echo 调用逐步进行,但在某个缓冲区中累积,然后在脚本终止时发送。这是我对显式输出缓冲的抱怨之一——为什么我们需要两个不同的输出缓冲实现?可能是因为内部(不可访问的)PHP/Web 服务器输出缓冲受到 PHP 开发人员无法控制的条件的影响,因此无法真正使用?

无论如何,我开始认为应该避免显式输出缓冲(ob_* 函数系列)并依赖隐式输出缓冲,必要时使用良好的flush 函数辅助它。也许如果 Web 服务器能保证每次 echo/print 调用时实际向客户端发送输出,那么设置显式缓冲会很有用——毕竟人们不想用大约 100 向客户端发送响应字节块。但是有两个缓冲区的替代方案似乎是一个有点无用的抽象层。

那么,最终,严肃的 Web 应用程序是否需要输出缓冲?

【问题讨论】:

  • 我印象深刻,第一个答案大约在 3 分钟内出现。在被问到问题之后。这是一些快速阅读!
  • @Chacha102: 和@troelskn: 哇,互联网真的摧毁了你的阅读能力,不是吗?读起来真的不多。在我看来,“文字墙”并没有像断句这样的好东西。我不想给你们两个(和支持者)带来困难,但我们应该赞扬那些花时间详细阐述他们的问题而不是嘲笑他们的人。如果你的注意力这么短,为什么要回应?
  • 我认为 Stack Overflow 是针对有答案的问题,而不是辩论...?
  • 我写的可能有点太多了,你是对的。为了我的辩护,我会说我宁愿在一个问题中过度解释自己,也不愿在问题列表中发送一个过于模糊且需要澄清的问题。无论如何,Chacha102,对不起,你浪费了你生命中的2分钟。下次判​​断力好点,毕竟没人让你看我的文字墙。
  • @eyelid,鉴于我已经阅读了两遍,这可能说明我的注意力不集中。我并不是说该评论是贬义的,但显然您无法在互联网上发现讽刺或幽默。也许你发现幽默的能力已经被互联网破坏了。

标签: php http


【解决方案1】:

是的

严重的 Web 应用程序在一种特定情况下需要输出缓冲:

您的应用程序希望控制某些第 3 方的输出 代码,但没有 API 来控制该代码发出的内容。

在这种情况下,您可以在交接前致电ob_start() 控制该代码,弄乱所写的内容(理想情况下 回调,或者如果必须检查缓冲区内容),以及 然后打电话给ob_flush()

最终,PHP 的 ob_函数 是一种机制,用于将一些 other 代码的作用捕获到缓冲区中 可以搞砸

如果您不需要检查或修改写入缓冲区的内容,则使用ob_start()没有任何好处

很可能,您的“严肃应用”实际上是某种框架。


反正你已经有输出缓冲了

您不需要ob_start()使用输出缓冲。您的网络服务器已经确实缓冲了您的输出。

使用ob_start() 不会让您更好输出缓冲 - 事实上,它可能会通过“囤积”网络服务器发送给客户端的数据来增加应用程序的内存使用和延迟已经。


也许ob_start() ...

...为了方便冲洗

在某些情况下,您可能希望根据您的应用程序最了解的某些标准控制何时网络服务器刷新其缓冲区。大多数时候,您知道您刚刚完成了客户端可以使用的逻辑“单元”的编写,并且您正在告诉 Web 服务器刷新 now 而不是等待输出缓冲区填满。为此,只需像平常一样发出输出,并用flush() 标点它。

在更少的情况下,您会希望保留来自网络服务器的数据,直到您有足够的数据可以发送。用一半的新闻打断客户是没有意义的,特别是如果剩下的新闻需要一些时间才能获得。一个简单的ob_start 后来由一个ob_end_flush() 结束可能确实是最简单和合适的事情。

...如果您对某些标题负责

如果您的应用程序负责计算只有在完整响应可用后才能确定的标头,那么它可能是可以接受的。

但是,即使在这里,如果您不能比通过检查完整的输出缓冲区来导出标头更好,您也可以让网络服务器来做(如果可以的话)。网络服务器的代码是经过编写、测试和编译的——你不太可能改进它。

例如,如果您的应用程序在计算响应正文之前知道响应正文的长度,那么设置 Content-Length 标头才有用。


没有解决不良做法的灵丹妙药

您不应该ob_start() 避免以下纪律:

  • 打开使用和快速关闭资源,例如内存、线程和数据库连接
  • 首先发出标头,然后是正文
  • 在开始响应之前进行所有计算和错误处理

如果你这样做,它们会导致技术债务,让你有一天哭泣。

【讨论】:

    【解决方案2】:

    好的,这是真正的原因:直到一切都完成后才开始输出。 想象一个应用程序打开一个 SQL 连接并且在开始输出之前没有关闭它。发生的情况是您的脚本获得连接,开始输出,等待客户端获得所需的一切,然后最后关闭连接。 Woot,2 秒的连接,而 0.3 秒的连接就足够了。

    现在,如果您缓冲,您的脚本会连接,将所有内容放入缓冲区,最后自动断开连接,然后开始将生成的内容发送到客户端。

    【讨论】:

    • 诶!?如果加载结构需要 2 秒钟,则您的页面存在严重问题。与数据库的开放连接也几乎没有影响。重要的是你建立这些联系的频率以及你用它们做什么。 -1
    • 再次,一个很好的理由,谢谢。另一方面,您可以选择 - 更短的数据库连接时间跨度或更渐进的用户响应反馈。我想说,这尤其适用于具有大结果的查询,渐进式用户反馈会胜出。除非你达到连接限制,当然,这是一个硬限制。
    • 感谢您的投票,但是是的,有些页面可能非常繁重,需要浏览器“长时间”下载(大量数据包含嵌入的 javascript 内容,例如,2MB 页面是不例外)。您至少有一个无用的打开数据库连接的时间(如果不是多个,不同的文件处理程序会阻止其他脚本或应用程序编辑这些文件等)。
    • Amn,确保渐进式用户反馈很好。但是,如果您的脚本是一个要求提供大型报告的 Web 服务,您就必须这样做。只是为了把事情放在眼里。要加载此页面,萤火虫说我的火狐在下载阶段浪费了 515 毫秒(不是请求发送和等待时间)。
    • 你有一个有效的观点,我将你的答案标记为有用。然而,我认为,当一个人想要快速获取查询结果并释放连接时,一个更好的解决方案是将结果卸载到内部 PHP 变量(数组、列表等)中,这可能与输出有任何关系,也可能没有任何关系.之后,您关闭连接并开始将格式化数据发送到客户端,隐式缓冲。这是你的论点,只是稍作修改。
    【解决方案3】:

    如果您想将报告输出到屏幕,但也通过电子邮件发送,输出缓冲让您不必重复处理以输出报告两次。

    【讨论】:

    • 这是一个很好的理由。谢谢。错过了,不久前在一个脚本中完成了,虽然不是用电子邮件,而是在实际发送给客户端之前对数据进行后处理,比如在 HTML 文本中修复 URL。
    • 这是我们在工作中经常做的事情,因为我们有很多报表应用程序,用户希望将输出留给后代。显然,节省了重复。 :)
    【解决方案4】:

    最明显的用例是:

    1. 一个输出过滤器(例如ob_gzhandler 或您可以自己设计的任何数量的过滤器); 我已经使用仅支持输出(而不是返回值)的 API 完成了此操作,我想使用 phpQuery 之类的库进行后续解析
    2. 维护(而不是重写)包含您讨论的所有问题的代码;这包括在输出开始后发送标头(感谢 Don Dickinson)或抑制已经生成的某些输出。
    3. 交错输出(此处归功于 Tom 和 Langdon);请注意,您的测试可能已失败,因为它与 PHP/Apache 的默认内部缓冲区冲突,但它可以做到的,它只需要在 PHP 发送任何内容之前刷新一定数量 - PHP 仍然会保持连接打开。

    【讨论】:

      【解决方案5】:

      我使用输出缓冲有一个原因......它允许我在开始处理请求后发送“位置”标头。

      【讨论】:

      • 这里的“处理请求”是什么意思?当然,您可以处理,只要您不在标头之前发送任何数据。我仍然不相信在发送数据后发送位置标头的充分理由。也许存在一个极端案例?
      • 处理请求可能意味着将一些 sql 数据发送回客户端或设置 cookie。稍后在调用中,sql 调用失败。您可能不希望用户看到第一个东西……cookie 或其他 sql 数据。在这种情况下,取消缓冲区并将位置发送到通用错误页面或任何可能需要的东西。理论上不发送数据会很好,但您永远不知道何时会发生错误,并且可能有很好的理由不将缓冲区发送给客户端。
      • 底线是您不应该在仍有评估时将数据发送到输出。应在输出组合之前评估所有条件。这就是 MVC 架构的美妙之处(好吧,无论如何,它的 VC 部分)——该架构使得隔​​离逻辑操作和按步骤排序事物变得容易,其中发送到输出是最后一个。
      【解决方案6】:

      输出缓冲在 IIS 上至关重要,它自己没有内部缓冲。关闭输出缓冲后,PHP 脚本的运行速度似乎比在 Apache 上慢很多。打开它,它们的运行速度会快很多倍。

      【讨论】:

      • 除了它“看起来”更慢的事实之外,你能提供任何证据吗?
      • @Langdon,公平地说,从用户的角度来看,感知到的性能绝对是真实的性能。
      • 我可以,但我现在不想重新启动我的 IIS 服务器 :-) 尝试在 IIS 中运行 phpBB3 有和没有输出缓冲,并使用 Firebug 记录接收HTML。
      • 我没有丰富的 IIS 经验。您的意思是服务器发送带有每个“回声”等的输出数据块,没有“内容长度”?在这种情况下,确实有一些输出缓冲总比没有缓冲要好,尤其是在发送短字符串的情况下,我猜。但是,我反对两种输出缓冲方案并行工作的情况,这似乎是一种浪费。
      【解决方案7】:

      这是一个老问题,但没有人说输出缓冲的一个重要特性是过滤。可以在将缓冲区发送到客户端之前对其进行预处理。

      这是一个非常强大的概念,并开启了许多有趣的可能性。在一个项目中,我同时使用了两个 过滤器

      1. 临时翻译术语(替换短文本)
      2. HTML、CSS 和 Javascript 的混淆(不要问我为什么)

      启用输出过滤调用ob_start("callback") 其中callback 是过滤函数的名称。有关详细信息,请参阅ob_start 的 PHP 手册:http://php.net/manual/en/function.ob-start.php

      【讨论】:

        【解决方案8】:

        我使用输出缓冲是为了避免通过字符串连接生成 HTML,当我需要知道渲染操作的结果以创建一些输出时在使用渲染之前

        【讨论】:

          【解决方案9】:

          我们过去常常将它用于具有非常长的表格的页面,表格中充满了来自数据库的数据。您将每 x 行刷新一次缓冲区,以便用户知道该页面实际上正在工作。然后有人听说可用性和类似的页面得到了分页和搜索。

          【讨论】:

          • 嗯,那是隐式输出缓冲,对吧?无论数据长度如何,无论可用性和分页,您都可以在没有 ob_ 系列函数的情况下做得很好。
          【解决方案10】:

          如果您尝试在需要一些时间来处理的页面期间显示进度条,这将非常有用。由于 PHP 代码不是多线程的,因此如果处理挂起执行 1 个函数,则无法执行此操作。

          【讨论】:

          • 嗯,我已经对此进行了测试,您可以在没有输出缓冲 API 的情况下做到这一点吗?!在冗长的操作开始时回显您的进度消息,调用“flush”,然后开始您的冗长处理。服务器将切换到“分块”传输编码,您的进度消息将已经在客户端,而您的脚本仍在执行。
          【解决方案11】:

          如果您正在执行大量数据库事务和处理,请使用输出缓冲将数据缓存在文件中,以用于其他类似的请求。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-10-25
            • 2021-04-08
            • 2012-04-01
            相关资源
            最近更新 更多