[wp-hackers] Replace default doctype on one page only through plugin

Gaarai gaarai at gaarai.com
Mon Jan 19 13:01:44 GMT 2009


I've never seen that entry in the documentation. I just didn't 
understand how they could nest as there is nothing to separate one 
grouping of ob_start/ob_end_flush and another grouping. So, if ob_start 
calls do nest, how do the ob_get_contents calls know which level of 
contents to get, the first or the second?

Thinking about it more (since I was really tired when I made that 
response last night), I suppose it's possible that it forces nesting by 
using a stack. Thus, if I call ob_start, ob_start, $a = ob_get_contents, 
ob_end_flush, $b = ob_get_contents, ob_end_flush, $a will only contain 
the buffer from the most-recent ob_start call while $b contains the 
output buffer that was essentially on the stack and returned to the top 
when ob_end_flush was first called.

I still think that this is a bad idea though. Since the code I provided 
has such a huge gap between the ob_start and ob_end_flush calls, it's 
also possible for someone else to do this. Imagine that this theoretical 
code also latches into the wp_head action, is called before my 
send_output function, and calls ob_start. Now also imagine that this 
code does not call ob_end_flush until the shutdown action. Now we have 
mismatched nesting. There really isn't a way to prevent such issues as 
you can never know what code may interact with your code at some point 
down the line.

My stance remains that using ob_start in this manner is dangerous and 
adds a definitive point-of-failure to the code. So, my conclusion last 
night was correct, but the reason for it being correct was off a bit.

Now if ob_start somehow adopted a naming convention like an object model 
rather than being based off of a stack model, this could work. For 
example, imagine the following:

    <?php
        if ( $contact_page_displayed ) {
            add_action( 'get_header', 'start_output_collection', 1000 );
            add_action( 'wp_head', 'send_output', 1000 );
        }
       
        function start_output_collection() {
            global $my_header_output_buffer;
           
            $my_header_output_buffer = ob_start();
        }
       
        function send_output() {
            global $my_header_output_buffer;
           
            if ( false !== $my_header_output_buffer ) {
                $header = ob_get_contents( $my_header_output_buffer );
               
                if ( false === $header )
                    die( 'send_output() failed to retrieve header output
    buffer. File: ' . __FILE__ . ' on line: ' . __LINE__ );
               
                if ( false === ob_end_clean( $my_header_output_buffer ) )
                    error_log( 'send_output() received a failure when
    trying to remove an output buffer. File: ' . __FILE__ . ' on line: '
    . __LINE__ );
               
                $header = preg_replace( '|<!DOCTYPE[^>]*>|i',
    '<!DOCTYPE..>', $header );
               
                echo $header;
        }
    ?>

If such a mechanism as I've coded here could exist, then I would say 
that using ob_start for such applications would be viable. However, this 
is not the case, thus ob_start is still dangerous to use except in very 
tight sections of code where random code couldn't wander in and ruin the 
party.

For anyone reading this, the code above is theoretical code and won't 
function. I know, I tested it. Besides, it's based off of a premise of 
how code could function if specific PHP functions worked in a different 
manner. In other words, don't use it.

Chris Jean
http://gaarai.com/
http://wp-roadmap.com/



Mike Schinkel wrote:
> Gaarai <gaarai at gaarai.com> wrote:
>   
>> The problem is that the ob_start function does not nest. There is no 
>> way to guarantee that nothing else will call ob_start between the
>> start_output_collection() and send_output() functions. On a standard 
>> install with the default theme, there are a total of 45 apply_filters 
>> calls as well as requiring the theme's header.php code. At any point 
>> in executing an of these sections of code, some code may exist that 
>> makes use of ob_start.
>>     
>
> I'm confused.  According to PHP.NET[1]:
>
>    Output buffers are stackable, that is, you may call ob_start() 
>    while another ob_start() is active. Just make sure that you call 
>    ob_end_flush() the appropriate number of times. If multiple output 
>    callback functions are active, output is being filtered sequentially 
>    through each of them in nesting order. 
>
> And this[2] explicitly says you can nest calls. Obviously, I'm missing something as your statement and PHP.NET seem to contradict. I'm sure it's my misunderstanding; I've been coding for 20 years but only PHP for two and only recently on a hard-core basis so I haven't learned all it's ins-and-outs of PHP. So I'm hoping you can help me better understand this apparent dichotomy. 
>
> TIA.
>
> -Mike Schinkel
> http://mikeschinkel.com/custom-wordpress-plugins/
>
> [1] http://us3.php.net/ob_start
> [2] http://www.codewalkers.com/c/a/Miscellaneous/PHP-Output-Buffering/1/
>
>
> _______________________________________________
> wp-hackers mailing list
> wp-hackers at lists.automattic.com
> http://lists.automattic.com/mailman/listinfo/wp-hackers
>
>   


More information about the wp-hackers mailing list