buffer is one piece of memory section, it is usually 4Kb in Linux. It is mainly used between different devices with different speed or different priorities. With buffer, the waiting time between different processes will be reduced. Here is one simple example, when you type something in a text editor, every time when you type a character, the operating system will not write it to the disk directly, instead it will write it to buffer first When the buffer is full, the data in the buffer will be written to disk. But when you call flush(), the data in the buffer will be written to the disk immediately.
php.output_buffering
By default php buffer is enabled, and the default size of the buffer is 4096, i.e 4Kb. You can find the output_buffering in php.ini file. When echo,print user data, the output data will be written to output buffer. When the output buffer is full, it will send the data to browser through TCP. You can also use ob_start() manually activate php.output_buffering. Even the data size is over 4Kb, the data will not be sent to browser using TCP since ob_start() has extended the PHP buffer. Only when the script completes execution or ob_end_flush() is called, the data will be sent to the browser.
1. When output_buffering=4096, and the data size is small
<?php for ($i = 0; $i < 10; $i++) { echo $i . '<br/>'; sleep($i + 1); // } ?>
The data will be sent to browser and displayed only when the response ends instead of sending data every few seconds. Before waiting for the server to complete its processing, the browser will remain blank. This is because the data size is too small, so the output buffer is not filled up. The order of data write is : echo->php buffer->tcp buffer->browser.
2. When output_buffering=0, and data size is small
<?php //ini_set('output_buffering', 0) not working //Should edit /etc/php.ini,set output_buffering=0 to disable output buffering //ini_set('output_buffering', 0); //disable output buffering for ($i = 0; $i < 10; $i++) { echo $i . '<br/>'; flush(); //Ask OS to output data to browser sleep($i + 1); // } ?>
After disabling output buffer, the data will be sent to browser consistently, it no needs to wait for the completion of this script. This is because the data will not stay at the output buffer, the order of data write is : echo->tcp buffer->browser
3. When output_buffering=4096, the data size is relatively large. Without calling ob_start()
<?php for ($i = 0; $i < 10; $i++) { echo file_get_contents('./f4096') . $i . '<br/>'; sleep($i +1); } ?>
The response is not over, data will be sent to the browser consistently and the browser display will not remain blank. Even though php.output_buffering is enabled, data will still be sent consistently instead of all at once. This is because output buffer is too small, and when it's filled up, the data will be sent to the browser right away.
4. When output_buffering=4096, the data size is relatively large. Calling ob_start()
<?php ob_start(); //Enable php buffer for ($i = 0; $i < 10; $i++) { echo file_get_contents('./f4096') . $i . '<br/>'; sleep($i + 1); } ob_end_flush(); ?>
Data will be sent to the browser only after the script completes execution. Before output, the browser will remain blank and wait for server response.
tcpdump
Here we can monitor tcp packets using tcpdump. We can observe the difference between using ob_start() and not using ob_start().
1. Without using ob_start()
12:30:21.499528 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . ack 485 win 6432 12:30:21.500127 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 1:2921(2920) ack 485 win 6432 12:30:21.501000 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 2921:7301(4380) ack 485 win 6432 12:30:21.501868 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 7301:8412(1111) ack 485 win 643 12:30:24.502340 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 8412:14252(5840) ack 485 win 6432 12:30:24.503214 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 14252:15712(1460) ack 485 win 6432 12:30:24.503217 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 15712:16624(912) ack 485 win 6432 12:30:31.505934 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 16624:23924(7300) ack 485 win 6432 12:30:31.506839 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 23924:24836(912) ack 485 win 6432 12:30:42.508871 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 24836:32136(7300) ack 485 win 6432 12:30:42.509744 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 32136:33048(912) ack 485 win 6432 12:30:57.512137 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 33048:40348(7300) ack 485 win 6432 12:30:57.513016 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 40348:41260(912) ack 485 win 6432 12:31:06.513912 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 41260:41265(5) ack 485 win 6432 12:31:06.514012 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: F 41265:41265(0) ack 485 win 6432 12:31:06.514361 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . ack 486 win 6432
2. Using ob_start()
12:36:06.542244 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . ack 485 win 6432 12:36:51.559128 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 1:2921(2920) ack 485 win 6432 12:36:51.559996 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 2921:7301(4380) ack 485 win 6432 12:36:51.560866 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 7301:11681(4380) ack 485 win 6432 12:36:51.561612 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 11681:16061(4380) ack 485 win 6432 12:36:51.561852 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 16061:20441(4380) ack 485 win 6432 12:36:51.562479 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 20441:24821(4380) ack 485 win 6432 12:36:51.562743 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 24821:29201(4380) ack 485 win 6432 12:36:51.562996 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 29201:33581(4380) ack 485 win 6432 12:36:51.563344 IP 192.168.0.8.webcache > 192.168.0.28.noagent: P 33581:35041(1460) ack 485 win 6432 12:36:51.563514 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 35041:36501(1460) ack 485 win 6432 12:36:51.563518 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 36501:37961(1460) ack 485 win 6432 12:36:51.563523 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 37961:39421(1460) ack 485 win 6432 12:36:51.563526 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 39421:40881(1460) ack 485 win 6432 12:36:51.563529 IP 192.168.0.8.webcache > 192.168.0.28.noagent: FP 40881:41233(352) ack 485 win 6432 12:36:51.570364 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . ack 486 win 6432
With above comparism, the time interval is different between these two. Without using ob_start(), the waiting interval is relatively large, the data in tcp buffer is sent out after waiting for 4 seconds. Data doesn't remain in the php buffer for a long time. This is because php buffer is filled up and the data has to be sent out. With ob_start(), data is sent to the browser all at once.
We often can see use of ob_start() in some template engines. In some famous open source projects such as wordpress,drupal and smarty, we can see the use of it. For example, in drupal application:
//@file:user-profile.tpl.php
- username: name; ?>
- picture:picture; ?>
//@file:template-render.php //@file:profile.php $user); print theme_render_template('user-profile.tpl.php', $variables); ?>
Source : http://www.perfgeeks.com/?p=344