xml - 使用PHP构建虚拟xml文件时,消除错误并避免报头已发送错误

标签 xml php

这是我的第一个问题,所以请原谅我犯的任何错误。
问题:
假设我使用的是php cms,比如wordpress、drupal等。
我想编写一个插件/模块来输出虚拟XML文件,例如http://example.com/page.xml,为此,我使用了一个名为send_headers(或类似的)的函数,在该函数中,我向访问者的浏览器发送类似于Content-Type: text/xml; charset=UTF-8的内容。
如果我有php错误(或其他东西),比如代码中某个地方的未定义索引,并且在我发送上面的内容类型头之前,它们被输出,那么访问者将收到一个神秘的Content Encoding Error错误,或者在某些情况下是header already sent错误。
当然,我可以清除代码中的所有错误以及其他意外输出的东西,但是那些cms有很多钩子,很多插件可以由其他开发人员开发,我对此没有任何控制权。php错误和意外输出可能无处不在。
我的方法:
使用error_reporting函数和/或ini_set('display_errors', 0)禁用错误报告。这种方法有效,但有一些局限性:
如果错误报告已经由那些cms的内部函数设置,并且在加载我的插件之前加载了其他有错误的插件,那么php错误仍然会显示出来。
ini_set也有同样的限制,更糟糕的是它可能无法启用。
如果头已经被意外的输出发送了,这两个函数就帮不上忙了。
ob_end_clean()中发送任何新标题之前,使用send_headers()清除所有输出和标题,例如:
$ob_level=@ob_get_level();
如果($ob_level)
而($ob_level>0)
{
$ob_level-=1;
@结束清洁(ob_end_clean)
}
这种方法也有同样的缺点,但是如果我为那些cms(可以是一个配置文件)的引导文件添加类似于ob_start()的内容,ob_end_clean几乎可以清除所有试图弄乱虚拟xml文件头的东西。这种方法的真正问题是,它可能会导致其他意想不到的行为。
例如,在我正在测试我的插件的服务器(apache/2.2.16 ubuntu,php 5.3.5)上,ob_end_clean似乎甚至会影响调用它之后发送的头:
$ob_level=@ob_get_level();
如果($ob_level)
而($ob_level>0)
{
$ob_level-=1;
@结束清洁(ob_end_clean)
}
//当我查看响应头时,没有设置内容类型,
//header()函数设置的其他头也被丢弃。
//但是服务器设置的头不受影响。
header('content-type:text/xml;charset=utf-8');
问题是:
有没有更好的方法使用php构建虚拟xml文件,而不必担心php错误和发送的头会损坏这些文件的头?
谢谢你的帮助。
编辑1-回复Hakre的回答:
因此,如果请求实际上是针对XML的,最好是只安装输出缓冲区。关于这个插件和平台(WordPress,Drupal,…)你有点不清楚。实现输出缓冲区通常需要注意不要破坏平台及其附加组件已经在使用的输出缓冲区的顺序。当然,任何想要使用输出缓冲区的插件都会试图达到最高级别。你也会的。所以在这里找到合适的工作并不容易。
好吧,让我们考虑一下这个wordpress插件。我计划在wordpress用来加载我的插件的文件中放一个ob_start,由于大多数插件使用init action hook来初始化,我认为这是我们可以为插件放置顶级输出缓冲的地方。你怎么认为?
您已经在检查代码中的输出缓冲区级别。我不知道你为什么把@放在函数前面,如果你希望那些函数不起作用,你必须自己检查它,而不是像现在这样继续。所以我首先要删除错误抑制操作符。
实际上,这些ob函数可能会发出警告或类似的东西,即使它们不起作用,也很有可能没有什么要清除的,所以我的插件仍然可以正常工作。但是,如果它们发出任何输出,我将有效地弄乱我自己的头。
例如,我为wordpress做了一个插件,负责处理激活时输出的主题:主题餐巾纸。
很像wordpress对激活时插件意外输出的警告,对吧?但是在前端发出错误的插件呢?
“友好消息”和“输出缓冲”方法都应该可以很好地工作,但我在问题中提到的输出缓冲方法确实会导致意外行为(它似乎丢弃了php在某些特定服务器配置上设置的所有头)。所以,也许最好只是向用户输出一条友好的消息,这使得他们更难去责怪那些不是真正罪魁祸首的东西。

最佳答案

在这种情况下你无能为力。但我不会说永远。其他插件开发人员也做到了这一点。但归根结底:即使不是你的代码导致了这种情况,也值得让事情得到正确的修复。
因此,如果不满足所需的先决条件,建议您的插件给出一个有用的错误描述,说明为什么事情不起作用。作为安全地点,我会先这么做:

if (header_sent($file, $line))
{
  $message = sprintf('Premature output is preventing the XML Plugin from working properly. Output has started in %s on line %d.', $file, $line);
  echo '<div style="border: 1em solid red; background: #fff; color: #f00; margin:2em; padding: 1em;">', htmlspecialchars($message), '</div>';
  trigger_error($message);
  die();
}

这将确保用户收到一条与他们交谈的信息,并使您能够更好地进行辩论。此外,它还特别指出了为什么事情没有按预期进行。当然,这条消息只是我在这里快速键入的内容,但这是一个你可以与用户预先交流的地方,你的赌注。
然而,即使你这样做,它也不是在所有情况下都有用。但你不能总是做很多“正确的事情”。所以这是我能给出的最基本的建议。
还有什么可以做的?一些窍门:
请求的快捷方式
如果您知道哪个请求属于您的插件,请使用插件提供的内容建立一个重写,并自行处理输出。这将防止加载整个框架,并防止其他插件/加载项获得输出。最常见的情况是,这是最简单的方法,它可以完全控制输出。
输出缓冲
输出缓冲可以防止发送头。您可以使用它来丢弃(丢弃、丢弃)已经完成的输出。不过,这是有代价的。
我能想到的最好的事情是在顶层安装一个输出缓冲区。由于输出缓冲区是可堆栈的,这确保您可以完全控制。问题是:是否可以在顶层安装输出缓冲区?另一个问题是:如果请求没有产生xml,那么输出缓冲区会发生什么?
在建立顶级输出缓冲区时,了解默认输出缓冲区是什么很有用。通常PHP itself is configured to have an output buffer already running(参见what is output buffering?)是出于性能原因,因为它降低了I/O使用率。因此,保持默认的级别号不破坏该级别是值得的。
因此,如果请求实际上是针对XML的,最好是只安装输出缓冲区。关于这个插件和平台(WordPress,Drupal,…)你有点不清楚。实现输出缓冲区通常需要注意不要破坏平台及其附加组件已经在使用的输出缓冲区的顺序。当然,任何想要使用输出缓冲区的插件都会试图达到最高级别。你也会的。所以在这里找到合适的工作并不容易。
您已经在检查代码中的输出缓冲区级别。我不知道你为什么把@放在函数前面,如果你希望那些函数不起作用,你必须自己检查它,而不是按原样继续。所以我首先要删除错误抑制操作符。
其次,请记住,您正在销毁其他插件输出缓冲区。那不友好。我可以理解你需要在这里全力以赴,但请记住,你实际上可能会引入更多的问题,你解决了。其他插件作者可能会像你一样抱怨。那你为什么要犯和别人一样的错误呢?这仅仅是你用输出缓冲所付出的代价。
# installing at the toplevel
$my_default_level = ob_get_level(); # learn about already set output buffers
$my_has_buffer = ob_start(); # my output buffer, with flagging

# burning down (somewhere after)
if ($my_has_buffer)
{
  $c = ob_get_level() - $my_default_level;
  if ($c <= 0)
  {
    # someone else already cleared my buffer.
  }
  else
  {
    while($c--)
    {
      ob_end_clean();
    }
  }
}

您可以将此类封装在一个类中,该类在实例化时自动处理此问题,并且可以将标志/变量保留在其实例中。然而,具体的实现在很大程度上取决于您使用的系统以及针对哪个请求。例如,我为wordpress做了一个插件,负责处理激活时输出的主题:Theme Napkin。这并不是完全可比的,因为它只处理非常特定的请求,而在该请求中只处理非常特定的操作。不过,它确实可以处理输出缓冲,并成功地防止过早发送头。

关于xml - 使用PHP构建虚拟xml文件时,消除错误并避免报头已发送错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6694415/

相关文章:

php - 使用代码通过 IMAP 将标签附加到 Gmail 中的邮件

c# - 模型到 XML 文件映射 - 如何在源 XML 文件中查找反序列化对象的位置。

xml - 我可以将 NUL (\x00) 放入 xml 中吗

java - Android - 在 Android 中设置自定义工具栏时出现 NullPointerException

php - 三个表和 belongsTo() 函数的 Laravel Eloquent 关系映射问题

PHP/CSS 不能让元素放在 table 上?

javascript - 未捕获的类型错误 : Cannot read property 'html' of undefined - AJAX post answer only in spesific div

java - 签名未根据凭证 key 进行验证

java - 如何更改项目单击时的列表项目外观。

php - gearman 问题和 php cli