How to organize custom code for plugins & themes

Modern WordPress sites tend to have dozens of plugins installed, and most of them even have a theme activated (some are using headless WP, hence “most of them”). And that’s ok until you want to start modifying their behavior.

Imagine having WooCommerce, WooCommerce Subscriptions, WooCommerce Memberships, BuddyPress, WPForms, WP Mail SMTP, All in One SEO Pack, MonsterInsights, and lots of other plugins active. Oh, and a Divi theme. Let’s call all of them “packages” because writing “plugin and/or theme” all the time is too much work for a developer.

All of those packages heavily use WordPress hooks (actions and filters) to extend WP default behavior, and they also have their own hooks for both internal and external use. We now have hundreds and thousands of various places, that can be extended (via add_action()), modified (add_filter()) and even switched off (remove_action() and remove_filter()). And with great power comes a great desire to make changes in ways various parts are working.

Do you want to hide that thing on your WooCommerce product page? Use a filter. Add countries dropdown to BuddyPress profiles? Use an action. Disable (really cool) async email notifications delivery in WPForms? Use a filter and so on. You now end up with various custom hooks for dozens of installed plugins.

And I really hope you don’t end up dumping all of them in your Divi theme functions.php file. You should at least have a Divi-child theme activated, which is a child for a Divi. But even using a child theme functions.php is (IMO) a bad decision, as you are mixing view with logic, which is pretty much always a bad idea.

Another option would be to install a plugin that will allow managing custom code, like “Snippets”. You can read more about that here on WPBeginner. But your are adding additional overhead to your already heavy WordPress install. And I personally don’t really trust the security of such plugins (that can run any code that you, or someone else, copy-pasted and saved in it), unless I review the code by myself. And that’s a lot of code to review for free.

So how do I manage all those custom actions and filters?

WordPress has a cool feature called mu-plugins. You can read more about that in this support article. In the WordPress hooks hierarchy, all the code from that directory is loaded very early, which is a good thing if you understand the basics of coding and the concept of loading order in general.

Let’s assume you have a file /wp-content/mu-plugins/test.php:

<?php
// This will fail: WPForms plugin hasn't been loaded yet.
wpforms()->do_something();
// This will work fine: WPForms already loaded.
add_action( 'init', function() {
    wpforms()->do_something();
} );
add_action( 'wpforms_process', function() {
    wpforms()->do_something();
} );

So basically, you can’t use package functions/classes directly, you must wrap them inside certain WP/package hooks that are happening AFTER package initialization.

With this understanding, the file structure I use to store all package-specific hooks makes sense:

/wp-content/mu-plugins/hacks-buddypress.php
/wp-content/mu-plugins/hacks-divi.php
/wp-content/mu-plugins/hacks-woocommerce.php
/wp-content/mu-plugins/hacks-wpforms.php
/wp-content/mu-plugins/hacks-wp-mail-smtp.php

I prefer to use some prefix (like debug- or hacks-) and a package slug to group all of my custom code for a specific package.

WordPress recognizes them correctly, and if you provide a plugin header in those files – it will even display you some additional information.

This interface is native to WordPress, readable, easy to remove those files when you uninstall/delete the package (using (s)ftp), and easy to find and add something new. This helps keep everything organized.


Comments

Leave a Reply

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