The Mother of All WordPress Hacks

Or, (Don’t) Break in Case of Missing Hook

When there is no hook where you need one, you have limited options. None of them is great. Here’s a new option to (possibly) trump them all…

The need for hooks

Hooks are what make WordPress WordPress. They allow customizing without forking, or splitting, from the wonderful code which is already out there for you to use. They allow customization and external updates to co-exists peacefully (most of that time). This ability is what makes WordPress so powerful. Hooks are so great, and sometimes there just isn’t enough of them.

Sometimes WordPress’s core, a theme, or a plugin just doesn’t have the right hook in the right place for you to implement your brilliant idea. In such cases you have a few traditional options:

  1. Contacting the developers – and waiting…
  2. Overriding core functions or entire files – and risking site breakage on future updates.
  3. Using frontend JavaScript, which has its drawbacks.
  4. Giving up, moving to India, and joining a Buddhist monastery.

That’s pretty much it, isn’t it?

Well, not necessarily…

Filter it all with ob_start()

In some cases, there is another, better, option. In the absence of the right hook, you can’t interfere with the formation of the page for achieving your goal. But, you can manipulate the page after it has already been constructed, one moment before it is sent to the visitor’s browser. That is to say, you can filter the entire HTML of the page, and change it.

PHP’s ob_start() function “will turn output buffering on. While output buffering is active no output is sent from the script […], instead, the output is stored in an internal buffer”. This function takes a callback function as a parameter. The callback function “will be called when the output buffer is […] sent […] to the browser at the end of the request”. Basically, the ob_start() function can hold the final constructed HTML for a moment and give you one last chance to manipulate it before it is sent to the browser. A simple str_replace() in the callback function will be enough in many cases. For more complex cases, regex and preg_replace() might do the trick.

Here’s an example I used for lazy loading videos echoed by YITH Woocommerce Featured Audio & Video Content:

<?php // Start the buffer at the head of the page add_action('wp_head', 'buffer_start'); function buffer_start() { // Make sure it isn't an AJAX request, then call the callback function if (!wp_doing_ajax()) { ob_start('callback'); } } // Manipulate the buffered HTML function callback($buffer) { $buffer = str_replace(' src="https://www.youtube.com/embed/', ' src="" data-src="https://www.youtube.com/embed/', $buffer); return $buffer; } // Send the output buffer and turn off output buffering add_action('wp_footer', 'buffer_end'); function buffer_end() { if (!wp_doing_ajax()) { ob_end_flush(); } }
Code language: PHP (php)

Benefits

  1. It is less risky and fragile than overriding core functions and files or using frontend JavaScript. ob_start() won’t break your site. Worst case scenario – if things change and str_replace() doesn’t find the needle anymore – your hack will disappear. Nothing else dramatic will happen, and things will fail gracefully. You can even set a simple email alert for when it happens using wp_mail().
  2. It is more versatile than using frontend JavaScript. Timing issues and other limitations such as AMP can make it difficult to retroactively manipulate HTML exactly as and when you want.
  3. It is cacheable. Unlike when using JavaScript, the resulted manipulation using ob_start() is cachable with dynamic cache.

Example use cases

Can be found here.

Drawbaks

Well… It won’t solve all of your issues. Sometimes joining a monastery is just inevitable.

Conclusion

When a hook is missing where you need it, filtering the entire page HTML before it is sent to the browser, using ob_start(), might enable you an elegant solution.

External links

Leave a comment

Your email address will not be published. Required fields are marked *