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.

358 views
d

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:

  1. How to fix incorrect cookie path when developing systems to engage with Wordpress as a back-end.
  2. How to properly remove the title from a Wordpress post, and how to do it for specific post types.
  3. There is no function to select a post by slug name (post name), but we can create our own function to do it; find out how in this tutorial.
  4. How to properly customize Wordpress based website, where to add your own code, and how to override existing functionality.

More in: Wordpress Development