From: rosenfield dot albert at gmail dot com Operating system: Any PHP version: 5.3.0alpha2 PHP Bug Type: Scripting Engine problem Bug description: virtual() prints output to browser
Description: ------------ When execution is redirected with virtual(), before the Apache sub-request is carried out, PHP prints headers and document contents. The document contents are never a practical problem, because you never have any - you're in the act of redirecting the request. The headers however ARE a problem. PHP generates some headers itself, and these are then sent to the browser. Whoops, there we go - now the new/real destination of the request is unable to, for example, set it's own Content-Type header. Or anything else, because PHP has already printed something completely unrelated to the contents the actual document is going to contain. It could seem that the idea was to implement something akin to <!--#include--> with a "virtual" keyword on it (often used in IIS for ASP), but currently php's virtual() does not work like that. Nobody uses it for this purpose, rather developers use include() and require() (often with __FILE__ or similar to hit a relative directory) because these do not flush headers. Where virtual() is really useful is in it's ability to perform Apache sub-requests. With this, and a simple Apache RewriteRule, any request can be re-routed to a PHP script (effectively a filter), and then routed back to the original destination after processing with virtual(). (This is an extremely convenient way to implement modular functionality in web applications, for example a security filter written in PHP.) As is currently, a significant number of hacks are required to work around the fact that virtual() prints headers before redirection. I personally have about a hundred lines of code that does nothing but try and predict which Content-Type the destination generates and try to coerce PHP into printing that instead, when it would have been much more practical if PHP just had discarded it's buffers in the first place. Hereby a request to remove this automation. With regards to backwards compatibility, I haven't been able to find anyone via Google that used virtual() in a different fashion than above. Just in case someone really does, though, there should be a way for them to continue to have headers flushed. With the low-to-non-existent amount of scripts depending on this flushing, I suggest that it is enough to modify the documentation. For example, it could read "if you want PHP's auto-generated headers to be printed before the sub-request is carried out, execute flush()". Reproduce code: --------------- Doing virtual(whatever) reproduces the problem. Here's a piece of code from a filter I'm using, which is perhaps more explanatory. if ($filter_mode) { // Pass request on via Apache to intended destination script or static file. // Nesting via virtual() is broken, PHP flush()es and sends a bunch of HTTP headers. // Crappy. if ($info->content_type == "application/x-httpd-php") { // Try to work around broken virtual() by using include() if target happens to // be PHP script. chdir(dirname($info->filename)); require($info->filename); die(); } else if ($info->content_type == "text/x-perl") { // Dynamic content other than PHP must be fed through virtual(), so we have // no way of working around the broken behaviour of header flushing. Sadly, // we'll both have to guess a content type and cross our fingers that the // target script does not need to modify headers... header("Content-Type: text/html"); // For some reason, contrary to documentation, virtual() requires an URI // including query parameters, and not a filename, here. virtual($fulluri); die(); } else if ($info->content_type == "httpd/unix-directory") { // See comments above and below. if ($block_dir_listing) { fail_denied( "Listing directory contents is not allowed on this server.\n" . "Please browse the web pages to find what you need.\n" ); } else { // Switch from httpd/unix-directory (see code below if/else branch) to // text/html content type. Apache generates the directory html // automatically. header("Content-Type: text/html"); virtual($uri); die(); } } // Again, since PHP insists on flushing headers when virtual() is invoked, // we have to apply the correct Content-Type. For static files this can // be deduced by looking at the filename, which is done here via lookup_uri. // For other types of files, you will need to manually change the script in // question and/or modify httpd.conf and modify the if/else block above. header("Content-Type: ".$info->content_type); virtual($info->uri); } Expected result: ---------------- virtual() does not cause anything to print to the browser. Actual result: -------------- virtual() causes auto-generated headers to dump to the browser. -- Edit bug report at http://bugs.php.net/?id=46479&edit=1 -- Try a CVS snapshot (PHP 5.2): http://bugs.php.net/fix.php?id=46479&r=trysnapshot52 Try a CVS snapshot (PHP 5.3): http://bugs.php.net/fix.php?id=46479&r=trysnapshot53 Try a CVS snapshot (PHP 6.0): http://bugs.php.net/fix.php?id=46479&r=trysnapshot60 Fixed in CVS: http://bugs.php.net/fix.php?id=46479&r=fixedcvs Fixed in CVS and need be documented: http://bugs.php.net/fix.php?id=46479&r=needdocs Fixed in release: http://bugs.php.net/fix.php?id=46479&r=alreadyfixed Need backtrace: http://bugs.php.net/fix.php?id=46479&r=needtrace Need Reproduce Script: http://bugs.php.net/fix.php?id=46479&r=needscript Try newer version: http://bugs.php.net/fix.php?id=46479&r=oldversion Not developer issue: http://bugs.php.net/fix.php?id=46479&r=support Expected behavior: http://bugs.php.net/fix.php?id=46479&r=notwrong Not enough info: http://bugs.php.net/fix.php?id=46479&r=notenoughinfo Submitted twice: http://bugs.php.net/fix.php?id=46479&r=submittedtwice register_globals: http://bugs.php.net/fix.php?id=46479&r=globals PHP 4 support discontinued: http://bugs.php.net/fix.php?id=46479&r=php4 Daylight Savings: http://bugs.php.net/fix.php?id=46479&r=dst IIS Stability: http://bugs.php.net/fix.php?id=46479&r=isapi Install GNU Sed: http://bugs.php.net/fix.php?id=46479&r=gnused Floating point limitations: http://bugs.php.net/fix.php?id=46479&r=float No Zend Extensions: http://bugs.php.net/fix.php?id=46479&r=nozend MySQL Configuration Error: http://bugs.php.net/fix.php?id=46479&r=mysqlcfg