为什么在PHP中使用输出缓冲?

时间:2010-01-27 15:28:31

标签: php http

我在互联网上阅读了相当多的资料,其中不同的作者建议使用输出缓冲。有趣的是,大多数作者仅仅因为它允许将响应头与实际内容混合而论证其使用。坦率地说,我认为负责任的Web应用程序不应该混合输出标题和内容,Web开发人员应该在其脚本中查找可能的逻辑缺陷,这会导致在生成输出后发送标头。这是我对ob_*输出缓冲API的第一个参数。即使你得到的一点点便利 - 将标题与输出混合 - 使用它也不是一个足够的理由,除非你需要快速破解脚本,这通常不是严肃的Web应用程序中的目标或方式。

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

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

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

11 个答案:

答案 0 :(得分:12)

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

  

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

     

在这种情况下,您可以在处理之前致电ob_start()   控制该代码,弄乱所写的内容(理想情况下是   回调,或者如果你必须检查缓冲区内容),和   然后调用ob_flush()

最终,PHP的ob_ 函数是一种机制,用于捕获一些其他位代码进入缓冲区可以做什么搞乱

如果您不需要检查或修改写入缓冲区的内容,则使用ob_start() 无法获得

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


你已经有了输出缓冲,无论如何

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

使用ob_start()不会让你更好的输出缓冲 - 它实际上可以通过“囤积”数据来增加应用程序的内存使用和延迟,否则这些数据将被发送到Web服务器客户已经。


也许ob_start() ...

...为方便起来冲洗

在某些情况下,您可能需要根据应用程序最了解的某些条件,在 Web服务器刷新其缓冲区时控制。大多数情况下,您知道您刚刚编写了一个客户端可以使用的逻辑“单元”,并且您告诉Web服务器现在刷新 而不是等待输出填充缓冲区。要做到这一点,只需要正常发出输出,并用flush()标记它。

更少见的是,在您有足够的数据发送之前,您需要保留来自Web服务器的数据。没有必要用一半的新闻来打断客户,特别是如果剩下的新闻需要一些时间才能获得。后来由ob_start得出的简单ob_end_flush()可能确实是最简单和最合适的事情。

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

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

但是,即使在这里,如果你做的不是通过检查完整的输出缓冲区来获得头文件,那么你也可以让web-server这样做(如果可以的话)。 Web服务器的代码经过编写,测试和编译 - 您不太可能改进它。

例如,如果应用程序在计算响应主体之前知道响应主体的长度,那么设置Content-Length标头只会很有用。


没有做坏事的灵丹妙药

你不应该ob_start()来避免以下原则:

  • 打开使用并快速关闭资源,例如内存,线程和数据库连接
  • 首先发出标题,然后是第二个
  • 在开始回复之前,您可以进行所有计算和错误处理

如果你这样做,他们会造成技术债务,这会让你有一天哭。

答案 1 :(得分:9)

好的,这是真正的原因:输出一切都没有开始。 想象一个应用程序打开SQL连接,并在开始输出之前不关闭它。会发生什么是您的脚本获得连接,开始输出,等待客户端获得它所需要的所有内容,最后,关闭连接。 Woot,2s连接,0.3s就足够了。

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

答案 2 :(得分:5)

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

答案 3 :(得分:5)

最明显的用例是:

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

答案 4 :(得分:3)

我出于某种原因使用输出缓冲...它允许我在开始处理请求后发送“位置”标题。

答案 5 :(得分:3)

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

答案 6 :(得分:3)

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

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

  1. 术语的临时翻译(替换短文)
  2. HTML,CSS和Javascript的混淆(不要问我为什么)
  3. 启用输出过滤调用ob_start("callback"),其中callback是过滤功能的名称。有关更多详细信息,请参阅ob_start的PHP手册:http://php.net/manual/en/function.ob-start.php

答案 7 :(得分:2)

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

答案 8 :(得分:1)

我们过去常常使用它来填充数据库中填充数据的长表的页面。你每隔x行刷一次缓冲区,这样用户就知道页面实际上正在工作。然后有人听说了可用性和页面,如有分页和搜索。

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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