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

Reply via email to