PHP Output Buffer in detail

Source: Internet
Author: User
Tags michael wallner sapi
As you know, there is a layer in PHP called the output buffer. This article is to explain what it really is? How does PHP internally implement it? And how do I use it in a PHP program? This layer is not complex, but it is often misunderstood, and many PHP developers do not have it in their hands. Today, let's get this together and figure it out.

What we're going to talk about is based on PHP 5.4 (and above), and the OB layer in PHP has changed a lot since version 5.4, which is exactly rewritten, and some of them may not be compatible with PHP 5.3.

What is an output buffer?

The output stream of PHP contains a lot of bytes, usually the text that the programmer wants to output from PHP, mostly the Echo statement or the printf () function output. For the output buffers in PHP, you need to know the three-point content.

The 1th is that any function that outputs something will use the output buffer, which is, of course, a program written in PHP. If you are writing a PHP extension, the function (c function) You use may write the output directly to the SAPI buffer layer without having to go through the OB layer. You can learn the API documentation for these C functions in the source file Main/php_output.h, which provides us with a lot of other information, such as the default buffer size.

2nd you need to know is that the output buffer layer is not the only layer used to buffer the output, it is actually just one of many layers. The last thing you have to remember is that the output buffer layer behaves in relation to the SAPI (Web or CLI) you use, and different SAPI may behave differently. Let's take a look at the relationships of these layers through a picture:

The above image shows the logical relationship of three buffer layers in PHP. The above two layers are the "output buffers" that we usually recognize, and the last one is the output buffers in the SAPI. These are the layers in PHP, and when the output bytes leave PHP at a lower level in the computer architecture, the buffer will continue to appear (terminal buffer), fast-cgi buffer, Web server buffer, OS buffer, TCP/IP stack buffer ... )。 Keep in mind a general principle that, in addition to the case in PHP discussed in this article, many parts of a piece of software will retain information before passing it to the next section until it is finally passed on to the user.

The SAPI of the CLI is a bit special, here's the point. The CLI enforces the output_buffer option in the INI configuration to 0, which means that the default PHP output buffer is disabled. So in the CLI, what you want to output by default is passed directly to the SAPI layer, unless you manually call the Ob_ () class function. And in the CLI, the value ofImplicit_flush is also set to 1. We often do not understand the role of Implicit_flush , the source code has everything: when Implicit_flush is set to open (value 1), once any output is written to the SAPI buffer layer, It refreshes immediately (flush, which means that the data is written to a lower level and the buffer is emptied). In other words: whenever you write any data into the CLI SAPI, the CLI sapi immediately throws the data to its next level, typically the standard output pipe, write (), and Fflush () which are responsible for doing this. Simple, right!

Default PHP output buffers

If you use a SAPI different from the CLI, like PHP-FPM, you will use the following three buffer-related INI configuration options:

    • Output_buffering

    • Implicit_flush

    • Output_handler

Before figuring out what these options mean, it's important to note that you can't use Ini_set () to change the values of these options at run time. The values of these options will be parsed before the PHP program is started, before any scripts are run, so it might be possible to use Ini_set () to change their values at run time, but the changed values do not take effect and it is too late because the output buffer layer is up and activated. You can only change their values by editing the php.ini file or by using the-D option when executing a PHP program.

By default, the PHP release will set the output_buffering to 4,096 bytes in php.ini. If you do not use any of the php.ini files (or if you do not use the-D option when starting PHP), the default value will be 0, which means that the output buffer is disabled. If you set its value to "on", the default output buffer size will be 16kb. As you might have guessed, using buffers for output content in a Web application environment is good for performance. The default 4k setting is an appropriate value, which means that you can write 4,096 ASCII characters before communicating with the SAPI layer below. And in a Web application environment, a byte-to-byte transmission of messages through a socket is not good for performance. The better way is to transfer everything at once to the server, or at least one piece at a time. The less the number of data exchanges between layers and layers, the better the performance. You should always keep the output buffers in a usable state, and PHP will be responsible for transmitting the content to the end user after the request is over, and you don't have to do anything.

Implicit_flush already mentioned in the previous discussion of the CLI. For other sapi,Implicit_flush is set to OFF by default, which is the correct setting because refreshing the SAPI is probably not what you want as long as new data is written. For the FASTCGI protocol, the flush operation (flushing) sends a FASTCGI array packet (packet) after each write, and it is better to write the fastcgi buffer before sending the packet. If you want to manually refresh the SAPI buffer, use the flush () function of PHP. If you want to write it once, you can set the implicit_flush option in the INI configuration, or call the Ob_implicit_flush () function once.

Output_handler is a callback function that modifies the contents of a buffer before it refreshes. PHP extensions provide a number of callback functions (users can also write their own callback functions, as described below).

    • Ob_gzhandler: Using ext/zlib compression output

    • Mb_output_handler: Converting character encoding using ext/mbstring

    • Ob_iconv_handler: Converting character encoding using Ext/iconv

    • Ob_tidyhandler: Using Ext/tidy to organize output HTML text

    • Ob_[inflate/deflate]_handler: Using ext/http compression output

    • Ob_etaghandler: Automatically generate an etag for HTTP using ext/http

The contents of the buffer are passed to the callback function of your choice (one only) to perform the work of the content conversion, so if you want to get PHP to the Web server and the user's content, you can use the output buffer callback. At the moment, it is necessary to mention that the "output" here refers to the message header (headers) and the body of the message (body). The message header of HTTP is also part of the OB layer.

Message header and message body

When you use an output buffer (either user or PHP), you may want to send the HTTP message header and content in the way you want. You know that any protocol must send the message header before the message body is sent (this is also what is called "header"), but if you use the output buffer layer, then PHP will take over these without you worrying about it. In fact, any PHP function (header (), Setcookie (), session_start ()) that is related to the output of the message header uses the internal sapi_header_op () function, which only writes the contents to the message header buffer. Then when you output content is, for example, using printf (), the content is written to the output buffer (assuming only one). When the content in this output buffer needs to be sent, PHP sends the message header first and then sends the message body. PHP has done everything for you. If you feel uncomfortable and want to do it yourself, you will only have to disable the output buffer, otherwise there is no other way.

User output buffer (buffers)

For the user output buffer, let's start with an example to see how it works and what you can do with it. Again, if you want to use the default PHP output buffer layer, you cannot use the CLI because it has disabled this layer. The following example uses the default PHP output buffer, using PHP's internal Web server SAPI:

/* Launched via php-doutput_buffering=32-dimplicit_flush=1-s127.0.0.1:8080-t/var/www */echo str_repeat (' A ', 31); Sleep (3); Echo ' B '; sleep (3); Echo ' C ';

In this example, when you start PHP, the default output buffer size is set to 32 bytes, and when the program runs, it writes 31 bytes to it before it goes to sleep. At this point the screen is empty and nothing is output, as expected. After 2 seconds of sleep, and then write a byte, this byte fills the buffer, it will immediately flush itself, the data inside the SAPI layer to pass the buffer, because we set Implicit_flush to 1, so the SAPI layer of the buffer will immediately flush to the next layer. The string ' aaaaaaaaaa{31 a}b ' will appear on the screen, and then the script goes to sleep again. After 2 seconds, a byte is output, and there are 31 empty bytes in the buffer, but the PHP script is executed, so the buffer containing the 1 bytes is immediately refreshed, which will output the string ' C ' on the screen.

From this example we can see how the default PHP output buffers work. We didn't call any buffer-related functions, but that doesn't mean it doesn't exist, and you have to realize that it exists in the running environment of the current program (which works in non-CLI mode).

OK, now we're going to talk about the user output buffer, which is created by calling Ob_start (), and we can create a lot of these buffers (up to memory exhaustion), which make up a stack structure, each new buffer is stacked onto the previous buffer, and whenever it fills up or overflows, Performs a flush operation and then passes the data to the next buffer.

Ob_start (function ($CTC) {static $a = 0; return $a + +. '- ' . $CTC. "\ n";}, Ob_start (function ($CTC) {return Ucfirst ($CTC);}, 3); echo "Fo"; sleep (2); Echo ' o '; sleep (2); echo "Barbazz"; Sleep (2); echo "Hello";/* 0-foobarbazz\n 1-hello\n */
I'll explain the example here instead of the original. We assume that the first Ob_start created the user buffer as buffer 1, and the second ob_start is created as Buffer 2. In accordance with the last-in-first-out principle of the stack, any output will first be stored in buffer 2.

The size of buffer 2 is 3 bytes, so the first Echo statement output of the string ' fo ' (2 bytes) will first be stored in buffer 2, another character, when the second Echo statement output of ' o ' after the buffer 2 is full, so it will refresh (flush), before the refresh will be called before the Ob_ The callback function of Start (), which converts the first letter of the string in the buffer to uppercase, so the output is ' Foo '. It is then saved in Buffer 1, and the buffer 1 has a size of 10.

The third echo statement will output ' Barbazz ', it will be placed in buffer 2, the string has 7 bytes, buffer 2 has overflowed, so it will immediately flush, call the callback function to get the result of ' Barbazz ', and then passed to buffer 1. This time buffer 1 saved the ' Foobarbazz ', 10 characters, buffer 1 will be refreshed, the same will call Ob_start () callback function, buffer 1 callback function before the string to add a line number, and at the end of the add a carriage return, so the first line of output is ' O ' Foobarbazz '.

The last Echo statement outputs the string ' Hello ', which is greater than 3 characters, so the buffer 2 refresh is triggered, because the script is now executed, so the buffer 1 is flushed immediately, and the resulting second row output is ' 1-hello '.

Internal implementation of the output buffer

Since version 5.4, the entire buffer layer has been rewritten (completed by Michael Wallner). The previous code is rubbish, many things can not be done, and there are many bugs. This article will provide you with more relevant information. So PHP 5.4 will be re-designed for this part, now the design is better, the code is also more neat, add some new features, and 5.3 version of the incompatibility problem is very few. Praise One!

One of the best features is that the extension can declare its own output buffer callback conflicts with other extensions that are provided by the callback. Prior to this, this was not possible before, if you were to develop an extension that uses an output buffer, you must first figure out the possible impact of all other extensions that provide buffer callbacks.

Here is a simple example that shows how to register a callback function to convert a character in a buffer to uppercase, and the code for this example may not be very good, but it is sufficient for our purposes:

#ifdef have_config_h#include "config.h" #endif include "Php.h" #include "php_ini.h" #include "main/php_output.h" # Include "php_myext.h" static int myext_output_handler (void **nothing, Php_output_context *output_context) {char *dup = N    ULL;    DUP = Estrndup (Output_context->in.data, output_context->in.used);    Php_strtoupper (DUP, output_context->in.used);    Output_context->out.data = DUP;    output_context->out.used = output_context->in.used;    Output_context->out.free = 1; return SUCCESS;}    Php_rinit_function (myext) {Php_output_handler *handler; Handler = php_output_handler_create_internal ("Myext handler", sizeof ("Myext handler")-1, Myext_output_handler,/* PHP_    Output_handler_default_size * *, php_output_handler_stdflags);    Php_output_handler_start (handler); return SUCCESS;}    Zend_module_entry myext_module_entry = {standard_module_header, "myext", NULL,/* Function entries */null, NULL,/* Module shutdown */php_rinit (Myext),/* ReQuest init */NULL,/* Request shutdown */null,/* Module information */"0.1",/* Replace with version number F or your extension */standard_module_properties}; #ifdef Compile_dl_myextzend_get_module (Myext) #endif

Trap

Most of the pitfalls have been revealed. There are some logic problems, some of them are hidden. Logically, it is most obvious that you should not call any buffer-related functions within the output buffer callback function, nor output anything in the callback function.

It is relatively less obvious that some PHP internal functions also use output buffers, which are superimposed on other buffers, which fill their buffers and then refresh, or return content. Print_r (), Highlight_file (), and Highlight_file::handle () are all such functions. You should not use these functions in the callback function of the output buffer. This behavior can lead to undefined errors, or at least not the results you expect.

Summarize

The output layer, like a net, loops all the "missing" outputs from PHP and saves them in a fixed-size buffer. When the buffer is filled, the contents are flushed (written) to the next layer (if any), or written to the following logical layer: SAPI buffer. Developers can control the number of buffers, the size, and the actions that can be performed at each buffer layer (purge, refresh, and delete). This is a very flexible approach that allows libraries and framework designers to take complete control of their own output and put them into a global buffer. For the output, we need to know the contents of any output stream and any HTTP message headers, and PHP will send them in the correct order.

The output buffers also have a default buffer, which can be controlled by setting 3 INI configuration options, which are designed to prevent a large number of small write operations, resulting in too frequent access to the SAPI layer, which consumes a lot of power and is not conducive to performance. PHP extensions can also define callback functions, and then execute this callback on each buffer, which is already a lot of applications, such as performing data compression, HTTP header management, and a lot of other things.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.