Search code examples
phpoutput-buffering

output_add_rewrite_var problems


Why doesn't this work?

output_add_rewrite_var('var', 'value');

// some links
echo '<a href="file.php">link</a>
<a href="http://example.com">link2</a>';

// a form
echo '<form action="script.php" method="post">
<input type="text" name="var2" />
</form>';

$tmp = ob_get_contents();

#print_r(ob_list_handlers());

ob_end_clean();

echo $tmp;

Taken from http://www.php.net/manual/en/function.ob-get-contents.php

I found a fix

ob_start();
ob_start();
output_add_rewrite_var('var', 'value');

// some links
echo '<a href="file.php">link</a>
<a href="http://example.com">link2</a>';

// a form
echo '<form action="script.php" method="post">
<input type="text" name="var2" />
</form>';

ob_end_flush();

$tmp = ob_get_contents();

#print_r(ob_list_handlers());

ob_end_clean();

echo $tmp;

The fix is not working

The fix is not working if I try to run it two times after each other, it works fine the first time, the last time it doesn't add the '?var=value' at all. Anyone have a clue?


Solution

  • Why it won't work

    As evidenced by ob_list_handlers() in the example in the docs for output_add_rewrite_var(), "URL-Rewriter" is an output handler. Output handlers are not run on data on the way into the buffer; output handlers are run on data on the way out of the buffer. To illustrate this, what happens if you're using the ob_gzhandler and you grab the contents of the buffer? You don't get gz-encoded data; you get the original, uncompressed data.

    From the docs for ob_start()'s output_callback parameter:

    The function will be called when the output buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) or when the output buffer is flushed to the browser at the end of the request.

    This is why your first example doesn't work. You're grabbing the contents of the buffer directly with ob_get_contents() rather than flushing its contents, so the rewrite handler hasn't been executed.

    Why the fix works

    The fix you posted is right on track. Since you have to flush the buffer for the output handler to run, you need to have another output buffer to catch the flushed data. Then that outer buffer will contain the handler-processed data, so grab its contents instead.


    Problems with multiple, consecutive buffers

    In regard to trying to "run it two times after each other," I see exactly what you mean. If I have a second inner buffer after the first is closed, the URL-Rewriter only works on the first. If I copy/paste and switch the order of the inner buffers, the one that works switches. It's always the one that occurs first. After playing with it for a few hours now, I'm tempted to say it's a PHP bug, unless there's an undocumented reason why it would behave that way specifically for the URL-Rewriter output handler.

    To test, I wrote a simple output handler.

    function ob_uppercase($buffer)
    {
        return strtoupper($buffer);
    }
    

    I got what I expected in terms of output handler usage.

    // outer buffer
    ob_start();
    
    print_r(ob_list_handlers());
    
    // inner buffer 1
    ob_start('ob_uppercase');
    print_r(ob_list_handlers());
    ob_end_flush();
    
    print_r(ob_list_handlers());
    
    // inner buffer 2
    ob_start('ob_uppercase');
    print_r(ob_list_handlers());
    ob_end_flush();
    
    print_r(ob_list_handlers());
    
    /*
    Yields:
    
    Array
    (
        [0] => default output handler
    )
    ARRAY
    (
        [0] => DEFAULT OUTPUT HANDLER
        [1] => OB_UPPERCASE
    )
    Array
    (
        [0] => default output handler
    )
    ARRAY
    (
        [0] => DEFAULT OUTPUT HANDLER
        [1] => OB_UPPERCASE
    )
    Array
    (
        [0] => default output handler
    )
    
    */
    

    Now if I did the same thing with the URL-Rewriter, it's not getting used in the second "inner buffer."

    // outer buffer
    ob_start();
    
    print_r(ob_list_handlers());
    
    // inner buffer 1
    ob_start();
    output_add_rewrite_var('var', 'value');
    print_r(ob_list_handlers());
    ob_end_flush();
    
    print_r(ob_list_handlers());
    
    // inner buffer 2
    ob_start();
    output_add_rewrite_var('var', 'value');
    print_r(ob_list_handlers());
    ob_end_flush();
    
    print_r(ob_list_handlers());
    
    /*
    Yields:
    
    Array
    (
        [0] => default output handler
    )
    Array
    (
        [0] => default output handler
        [1] => URL-Rewriter
    )
    Array
    (
        [0] => default output handler
    )
    Array
    (
        [0] => default output handler
        [1] => default output handler
    )
    Array
    (
        [0] => default output handler
    )
    */
    

    It doesn't matter if I use the same var name in both inner buffers or not. All uses of output_add_rewrite_var() in the second inner buffer is completely ignored, no matter what the vars are. (I also tested with more than two, and in all cases only the first one works.)

    I say it's a bug. If you agree, please submit a bug report to http://bugs.php.net .