Over the years, I think the concept of a “drop-in” plugin has become overloaded. What once referred to a very specific type of functionality has evolved to mean a couple of things.
- One of the definitions retains the original definition which is you can place specific files into the
plugins
directory to replace core WordPress functionality. These are not standard WordPress plugins, though. For example, you’ll likely often see files likeadvanced-cache.php
orobject-cache.php
in theplugins
directory. These are the original type of ‘drop-ins’ when it comes to working with WordPress. - Another definition are plugins that aren’t specifically
mu-plugins
and they aren’t standalone plugins either. Instead, these are pieces of functionality that can be dropped into any other plugin and add functionality. Say you have two different plugins that are used by a lot of people and you want to give them the ability to add a feature without activating a new plugin. The way in which you can do this is have them drop a file into their existing plugin.
Here’s the challenge with the second definition: When you drop functionality of into the context of another plugin, that plugin may not be the only one already running the same code.
In other words, say you have a file called acme-functionality.php
that can be added to any plugin. If you drop acme-functionality.php
into multiple, activated plugins then you may end up with all kinds of results none of which are ideal. And why isn’t it ideal? Because you want the code to run only once.
What’s a way to check to see if a file is already running in the context of another plugin before running it’s code?
Run Custom Functionality Once in Multiple Plugins
Outlining Functionality
To be consistent, we’ll assume that the code we’re writing to drop-in to any other plugin will be referred to by acme-functionality.php
.
Verifying this plugin necessitates the following functionality (or something close enough to it):
Make sure the plugin isn’t already active. This assumes we’ll have a function that returns a boolean value. And to determine this value, we need to do the following:
- Define a directory through which want to scan (in the case of WordPress, this would be the
plugins
directory and its subdirectories). - Define the name of the file we only want to run once. This is the name of the file we’ve been referencing as
acme-functionality.php
. - Count how many times the plugin is found throughout the
plugins
directory. - Determine if it’s greater than or equal to one.
Then we can make a call to the function and if it returns false, we simply don’t run the code in the plugin.
Writing the Function
First, let’s take a look at the function that can help us run custom functionaltiy once in multiple plugins:
/** * Determines if this file is used in any of the other plugins running * on the instance of WordPress. * * @return bool True if the file is used in any of the other plugins. */ function isPluginActive(): bool { // Define the directory to scan $dir = trailingslashit( dirname(__DIR__) ); /* Define the name of the file we wish to check. * NOTE: This should always be the name of this file. */ $fileToCheck = 'acme-functionality.php'; // Initialize a counter to track of the number of files found $count = 0; // Recursively scan the directory and subdirectories for PHP files $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir) ); foreach ($iterator as $file) { // If we find this file elsewhere, then increase the counter. if ( $file->isFile() && strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'php' ) { if ($file->getFilename() === $fileToCheck) { $count++; } } } return $count >= 1; }
Note that I’m using several standard library functions such as RecursiveIteratorIterator
and RecursiveDirectoryIterator
.
Per the PHP manual, these are described as follows:
Can be used to iterate through recursive iterators.
RecursiveIteratorIterator
The RecursiveDirectoryIterator provides an interface for iterating recursively over filesystem directories.
RecursiveDirectoryIterator
The RecursiveIteratorIterator
accepts a RecursiveDirectoryIterator
which accepts a directory through which to iterate. In short, this makes it easy to iterator through all of the directories and subdirectories in the plugins
directory.
Then, if the specified file name is found at least once, then we know the plugin is active.
Conclusion
When we know the code is used elsewhere in the file system, we can easily stop it from executing it again by simply calling the following conditional:
if (!isPluginActive()) { return; } // The rest of your code here.
It’s functions like this that help use write code that isn’t run any more than its needs to be run. If you wanted to take this a step a further, then it’s possible to even cache the result for a deterministic amount of time before running it again.
That’s beyond the scope of this article but it’s something to keep in mind should you be doing something similar in your own work.