ID:               46479
 Comment by:       macke at meteli dot net
 Reported By:      rosenfield dot albert at gmail dot com
 Status:           Feedback
 Bug Type:         Apache2 related
 Operating System: win32
 PHP Version:      5.2CVS-2008-11-01
 New Comment:

Newest 5.2 snapshot has the same problem. Lines 334-335 in
/sapi/php_apache.c are the same as before:

php_end_ob_buffers(1 TSRMLS_CC);
php_header(TSRMLS_C);

Is the solution as simple as removing those two lines or are there
other implications? If so, could we at least add a new parameter to
virtual() that disables those two? It would be backward compatible also.


Previous Comments:
------------------------------------------------------------------------

[2009-03-21 23:18:37] j...@php.net

Please try using this CVS snapshot:

  http://snaps.php.net/php5.2-latest.tar.gz
 
For Windows:

  http://windows.php.net/snapshots/



------------------------------------------------------------------------

[2008-11-05 11:37:25] rosenfield dot albert at gmail dot com

Did not realize the Apache version was relevant, sorry.

I've experienced the problem on more than one server, some of them that
I administrate (some not).  Given that virtual() is one of the more
'professional tools' in the PHP 'toolbox', it is probably not used much
overall by developers, so I doubt that the code behind had changed and
just marked it as being in the "newest version".  Sorry about that!

A server with the newest Apache/PHP combo installed, which also has a
filter installed where this is a problem is www.heidisql.com - a GET on
port 80 will retrieve the version numbers (via the Server: header):

  Apache/2.2.9 (win32)
  PHP/5.2.7RC3-dev (win32)

------------------------------------------------------------------------

[2008-11-04 22:30:09] j...@php.net

Since you failed to give the actual Apache version (and I doubt 
you're using 5.3 either) I'm assuming you have apache 2 something. 
Please update that information. Also, give the exact PHP version.

------------------------------------------------------------------------

[2008-11-04 13:39:29] rosenfield dot albert at gmail dot com

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 this bug report at http://bugs.php.net/?id=46479&edit=1

Reply via email to