Master the output functions

Teaching of a Samurai Engineer 11: Output control functions – continued

This time, I’m going to dig into individual functions and explain them a little.
In particular, I’m going to go in depth on the ob_start () function, which is the basis of many output control functions.

This time, I’m going to dig into individual functions and explain them a little.
In particular, I’m going to go in depth on the ob_start () function, which is the basis of many output control functions.

The ob_start () function can be used without parameters, but you can give it a few as well.

The first parameter is ‘callable $output_callback = NULL’, which at the point of output, registers all functions in the output contents that can be modified.
Let’s write some code to experiment with it.

First, confirm how it works by giving it an unprocessed callable.

<?php
//
ob_start (function ($s) {
return $s;
});
echo “test\n”;
//
ob_end_flush ();

<result>
test
</result>

With the above parameters, if you don’t return some letter in the function, nothing will be outputted, so be careful.
You might get a better feel for it after commenting out the ‘return $s’ in the code and trying it again.
The returned row of characters is the output content.

Now let’s start adding some code.

<?php
//
ob_start (function ($s) {
$s = ’Output Control is: ’. $s;
return $s;
});
echo “test\n”;
//
ob_end_flush ();

<result>
$s = ’Output Control is: test
</result>

As you can see here, if the first parameter for the ob_start () function is a callable function, the output will be that returned value. The called function’s parameter will contain the buffered contents, so you can use this in processing.

Next, the second parameter is ‘int $chunk_size=0 ’, which is the length of the buffer. Whenever the buffer length goes above the specified size, the buffer is flushed.
If you set this to 0 (the default), this doesn’t stop the buffer from existing; rather, it sets the buffer to only be flushed when the output buffer is closed.
Let’s experiment with some code that explains this quickly.

First, a pattern with chunk_size as 0. The default parameters are highlighted so the changes are easy to see.
<?php
//
ob_start (NULL, 0);

echo “test\n”;
header (’Location: http://example.com’);
//
ob_end_flush ();

<result>
test
</result>

Once you shorten the chunk_size to be easily understood, it looks like this.
<?php
//
ob_start (NULL, 3);
echo “test\n”;
header (’Location: http://example.com’);
//
ob_end_flush ();

<result>
test
Warning: Cannot modify header information — headers already sent by …
</result>

The reason the above happened is simple. When the chunk_size is 0:

  • The ob_start function begins the output buffer: The buffer length is 0 (infinite)
  • ‘echo “test\n; “’ buffers the output
  • The header function outputs an HTTP RESPONSE HEAD
  • the ob_end_flush function flushes the buffer

    That’s how it goes for 0, but at chunk_size 3…

  • The ob_start function begins the output buffer: The buffer length is 3
  • ‘echo “test\n; “’ buffers the output
  • As the buffer length is 3, and the character length is 5, the flush (HTTP RESPONSE BODY output) begins
  • The header function outputs the HTTP RESPONSE HEAD, but as one is already being outputted, you receive a ‘headers already sent’ message

Now, let’s move on.
This flow of outputting every time there’s a chunk_size overflow relates to the first parameter, output_callback, as well.
Let’s write some code to experiment with it.

<?php
//
ob_start (function ($s) {
$s = ’Output Control is: ’. $s;
return $s;
}, 3);
echo “test\n”;
echo “hoge\n”;
//
ob_end_flush ();

<result>
Output Control is: test
Output Control is: hoge
Output Control is:
</result>

As you can see here, it will be performed every time chunk_size is exceeded.
This does depend, though, on why you’re calling ob_start. If one of your reasons is to guard against ‘headers already sent’, maybe you should be careful in specifying chunk_size.

Finally, the third parameter is flags. If you assign this, you can limit configuration of the output buffer.
For details, it may be best to look at>the manual.
To break it down to its basics:
PHP_OUTPUT_HANDLER_CLEANABLE is clean (erase the buffer)
PHP_OUTPUT_HANDLER_FLUSHABLE is flush (output the buffer)
PHP_OUTPUT_HANDLER_REMOVABLE erases the output buffer after the script ends

These are the permissions indicated.

Additionally, as listed in the page linked above, and “>another page,

<parameter>

PHP_OUTPUT_HANDLER_STDFLAGS Default setting for output buffer flags.
PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_FLUSHABLE |
PHP_OUTPUT_HANDLER_REMOVABLE likewise
</parameter>
These flags also exist (but as they are default values, I doubt they will come up very often).

Let’s confirm the above by performing an ob_end_flush () having erased PHP_OUTPUT_HANDLER_CLEANABLE permissions.

<?php
ini_set (’display_errors’, ’1 ’);
error_reporting (-1);
//
ob_start (NULL, 0, PHP_OUTPUT_HANDLER_FLUSHABLE | PHP_OUTPUT_HANDLER_REMOVABLE);
echo “test\n”;
ob_clean ();
//
ob_end_flush ();

<result>
test
Notice: ob_clean (): failed to delete buffer of default output handler (0) in /home/furu/t.php on line 11

</result>

You can tell here that ob_clean () does not function, and also that the Notice is being outputted.
ob_start () is often ignored as it’s typically only handling default parameters, but it actually has many different uses.
If you can remember those uses and capabilities as needed, there should be times when it’s useful to you.

ob_start () can also be nested.
Technically it’s nested in the below code as well, but if you run it, it probably won’t particularly feel that way.

<?php
//
ob_start ();
ob_start ();
//
echo “test\n”;
//
ob_end_flush ();
ob_end_flush ();

Let’s add functions and confirm that it’s nested.

<?php
//

ob_start (function ($s) {

return “Outer loop: “’. $s;

});

ob_start (function ($s) {

return “Inner loop: “’. $s;

});

//
echo “test\n”;

//

ob_end_flush ();

ob_end_flush ();

<result>
Outer loop: Inner loop: test

</result>

This is how it should come out.

Additionally, when there are many levels of nesting, to tell how nested the buffering is, you have a function called ob_get_level ().

Let’s add that to the earlier code.

<?php
//
echo ’1 st level’, ob_get_level (), “\n”;
ob_start (function ($s) {
return “Outer loop: “’. $s;
});
echo ’2 nd level’, ob_get_level (), “\n”;
ob_start (function ($s) {
return “Inner loop: “’. $s;
});
echo ’3 rd level’, ob_get_level (), “\n”;
//
echo “test\n”;
//
ob_end_flush ();
echo ’4 th level’, ob_get_level (), “\n”;
ob_end_flush ();
echo ’5 th level’, ob_get_level (), “\n”;

<result>
1 st level0
Outer loop: 2 nd level1
Inner loop: 3 rd level2
test
4 th level1
5 th level0
</result>

It might be hard to keep up with, but you can use this to tell how nested something is.

Similarly, ob_get_length () is a function that will return the length of the output buffer.

<?php
//
echo ’1 st level’, ob_get_level (), “\n”;
$len = ob_get_length ();
var_dump ($len);
ob_start (function ($s) {
return “Outer loop:”. $s;
});
echo ’2 nd level’, ob_get_level (), “\n”;
$len = ob_get_length ();
var_dump ($len);
ob_start (function ($s) {
return “Inner loop:”. $s;
});
echo ’3 rd level’, ob_get_level (), “\n”;
//
echo “test\n”;
$len = ob_get_length ();
var_dump ($len);
//
ob_end_flush ();
echo ’4 th level’, ob_get_level (), “\n”;
ob_end_flush ();
echo ’5 th level’, ob_get_level (), “\n”;

<result>

1 st level0
bool (false)
Outer loop:2 nd level1
int (11)
Inner loop:3 rd level2
test
int (16)
4 th level1
5 th level0
</result>

When the output buffer has yet to begin, keep in mind that boolean will return a false result.

Other than that…

  1. Keep in mind if you want to output the buffer (*_flush), wipe it (*_clean), or get it (ob_get_contents)
  2. Also if you want to continue it (ob_get_* or ob_*), or end the buffer (ob_end_*)

This should cover most predictions of actions from function names.

Use your output buffer as needed, and tackle difficult problems smartly.

Next time, we’ll look at error handling and logging, and related functions, in more detail.

Michiaki Furusho

PREVIOUS ARTICLE NEXT ARTICLE