This is my first question, so please excuse me for any mistake I make.
The issue:
Let's say I'm using a PHP CMS like WordPress, Drupal, etc.
I want to write a plugin / module to output virtual xml files, e.g. http://example.com/page.xml
and to do that I use a function called send_headers
(or similar) in which I send something like Content-Type: text/xml; charset=UTF-8
to visitors' browsers.
If I have PHP errors (or other things) such as undefined index somewhere in the codes, and they are outputted before I send the content-type header above, the visitors will be greeted by the cryptic Content Encoding Error
error, or in some cases the header already sent
error.
Of course I can clear all the bugs in my codes as well as other unexpectedly outputted things, but those CMSs have a lot of hooks, and a lot of plugins can be developed by other developers, over which I do not have any control. PHP errors and unexpected outputs can be everywhere.
My approaches:
- Disable error reporting using
error_reporting
function and / orini_set('display_errors', 0)
. This approach works but with some limitations:- If error_reporting has already set by internal functions of those CMSs and other plugins with bugs are loaded before my plugin, the PHP errors are still shown.
ini_set
also has the same limitation, and what's worse is that it might not be enabled.- If the headers have already been sent by unexpected outputs, those two functions can't help.
Clean all outputs and headers using
ob_end_clean()
before sending any new headers insend_headers()
, e.g.:$ob_level = @ob_get_level(); if ($ob_level) while ($ob_level > 0) { $ob_level -= 1; @ob_end_clean(); }
This approach does have the same drawbacks, but if I add something like
ob_start()
to a boot file for those CMS (which can be a config file),ob_end_clean
can pretty much clean up every thing that tries to mess up the virtual xml file's headers. The real issue with this method is that it might cause other unexpected behaviours.For example, on a server that I'm testing my plugin on (Apache/2.2.16 Ubuntu, PHP 5.3.5), ob_end_clean seems to affect even headers sent after it is called:
$ob_level = @ob_get_level(); if ($ob_level) while ($ob_level > 0) { $ob_level -= 1; @ob_end_clean(); } // When I view the response headers, no Content-Type is set, // other headers set by header() function are discarded as well. // Headers set by the server are not affected however. header('Content-Type: text/xml; charset=UTF-8');
The question:
Is there a better way to build virtual xml files using PHP without worrying about PHP errors and sent headers mangling those files' headers?
Thanks for any help.
EDIT 1 - in reply to Hakre's answer:
So best would be to only install the output buffer if the request is actually for your XML. You've been a bit unspecified about which plugin this is going to be and which platform (wordpress, drupal, ...). Implementing a output buffer often needs to take care to not destruct the order of output buffers a platform and it's add-ons are already using. Naturally any plugin that wants to make use of output buffers tries to reach the top level. So will you. So it's not always easy to find the right working here.
Okay let's consider this for a WordPress plugin. I plan to put an ob_start in the file that WordPress uses to load my plugin, and since most plugins use the init action hook to initialize, I think that's the place where we can place a top-level output buffering for our plugin. What do you think?
You're already checking the output buffer level in your code. I have no clue why you put @ in front of the functions, if you're expecting those functions not to work you must check that on it's own, and not continue just as-is. So I would first of all drop the error suppression operator.
Actually those ob functions might emit a warning or similar things, and even if they don't work there's a huge chance that there's nothing to clear so my plugin can still work normally. But if they emit any output, I will just effectively mess up my own headers.
For example, I've done one plugin for wordpress that's taking care for themes that do output upon activation: Theme Napkin.
Much like WordPress's warning for unexpected outputs from plugins upon activation, right? But how about plugins that emit errors in the front end?
Both the 'friendly message' and 'output buffering' approaches should work well, but the output buffering method, which I have mentioned in my question, does cause unexpected behaviours (it seems to discard all headers set by PHP on some specific server configurations). So maybe it's best to just output a friendly message to the users, which makes it harder for them to blame something that is not actually the culprit :-).