Creating a Page Wrapper From a Wordpress Plugin
How to insert code right after body opening tag, and creating a wrapper around the code in a page using PHP output buffering and the Wordpress shutdown hook.
By. Jacob
Edited: 2021-11-22 22:03
In Wordpress development, you may sometimes have a need to create a page wrapper that wraps around all content in the HTML, including the header and footer elements; but, even Wordpress with all its hooks and filters does not make this easy to do.
One benefit of doing this is that you can easily blur the wrapper while having elements outside of the wrapper that will not be blurred. This is the perfect design for popups and modal windows.
There is at least couple of ways to do it, without having to edit the template files or creating a child theme, one of the easiest is by using the shutdown hook with PHP's output buffer functions; But, if you want reliability, this is probably one of the few instances where it is better to create a child theme.
See also: How output buffering works in PHP
There is just one problem for plugin developers — we can not safely expect that the buffer has not already been flushed — yet, this solution is probably the best available so far, and it works perfectly for personal projects.
add_action('shutdown', function () {
$ob_content = '';
while(ob_get_level() > 0) {
$ob_content .= ob_get_clean();
}
$ob_content = preg_replace("|(<body[^>]*>)|", "$1" . '<div id="my_wrapper">', $ob_content);
$ob_content = str_replace("</body>", '</div><!-- close wrapper --></body>', $ob_content);
echo $ob_content;
exit();
}, 0);
The shutdown hook
If you want to include a wrapper around all of the content in a page, you will need to add a opening <div> tag right after the <body> opening tag, as well as a closing </div> tag right before the </body> closes. This is why the problem is so complicated to solve in Wordpress.
Some hooks, like wp_body_open, are theme-dependent, meaning that they will not work for all themes. It is up to the theme developer to support different Wordpress features. If supported, you may be able to use a combination of wp_footer, wp_head, and wp_body_open.
If a given hook is not supported, you may need to modify the theme code yourself before you can use the hook. I had to manually add <?php echo wp_body_open(); ?> after the <body> opening tag in a child theme's code before I was able to use the wp_body_open hook.
This is where the shutdown hook allows you to manipulate the compiled HTML code before outputting it to the browser. Inside the hook handler you can then do a simple replace string in string operation before sending the HTML.
You can use the hook like this:
add_action('shutdown', function () {
$ob_content = '';
// Since we can sometimes have multiple output buffers,
// we need to collect the content of each one of theme
// By default, output buffering is enabled, so PHP will
// usually always start at OB level 1
while(ob_get_level() > 0) {
$ob_content .= ob_get_clean();
}
echo $ob_content;
exit();
}, 0);
The 0 gives the hook a priority, the low value forces it to execute as early as possible.
Inserting code in the HTML
The hardest thing to do is how to insert your code in the code, but luckily you already know how to do that from my tutorial about string replacement:
See: How to replace parts of a string with str_replace and preg_replace
I already wrote a simple regular expression to help you insert a wrapper div at the right places in your code. Please note, while string functions such as str_replace is technically faster, it will be insignificant in most applications.
1. Obtain the content from all output buffer levels and clean them:
$ob_content = '';
while(ob_get_level() > 0) {
$ob_content .= ob_get_clean();
}
2. Insert the wrapper by replacement:
$ob_content = preg_replace("|(<body[^>]*>)|", "$1" . '<div id="my_wrapper">', $ob_content);
$ob_content = str_replace("</body>", '</div><!-- close wrapper --></body>', $ob_content);
I only used preg_replace to "remember" potential HTML attributes on the body opening tag — this is obviously not needed for the closing tag, and so I use str_replace instead.
3. echo out the edited HTML:
add_action('shutdown', function () {
$ob_content = '';
while (ob_get_level() > 0) {
$ob_content .= ob_get_clean();
}
$ob_content = preg_replace("|(<body[^>]*>)|", "$1" . '<div id="my_wrapper">', $ob_content);
$ob_content = str_replace("</body>", '</div><!-- close wrapper --></body>', $ob_content);
}, 0);
How output buffering works
See also: How output buffering works in PHP
In most circumstances, there is probably not going to be any issues with manipulating contents of the output buffer, because it just "sits there" waiting to be flushed anyway. However, there is no guarantee that other plugins or code is not flushing the buffer earlier, and that will cause things to silently break.
Another potential issue is that the chunk_size of the active buffer might have been set too low, and so, the current content of the buffer might not contain what we are looking for, as it may already have been flushed.
A developer might sometimes explicitly flush the content of the output buffer; the advantage of doing this is, in theory, that you minimize PHP's memory consumption and speed up loading of your web pages by allowing the browser to read parts of the code faster. The disadvantage is that it might be unpredictable, as it will only work with very large HTML pages, and it can also prevent you from compressing the page with one of following brotli, gzip, or deflate — although it is possible with careful configuring a server.
Flushing the output buffer is mainly useful when sending large files to users (clients), such as when streaming video files in PHP; it is not very useful for simple HTML pages, because they are generated very fast. In fact, enabling some kind of caching of HTML might be better for such purposes.
Tell us what you think: