I got a ticket for a high-traffic WooCommerce site where the client was stuck. They had one plugin for database scaling—a HyperDB-style read/write replica setup—and another for deep performance analysis, like Query Monitor. The problem? They couldn’t activate both at the same time. The site was either fast or it was debuggable, but never both. A total mess.
This is a classic WordPress issue, where two plugins try to modify the same core object, in this case, the global $wpdb. Both plugins wanted to extend the wpdb class to add their functionality. When two plugins try to be the “one true” extension of a class, only one can win. The other one just breaks.
My first thought was this might be a load order problem. You know, just a simple hack in a mu-plugin to make sure one loads after the other, letting them chain together. And yeah, that worked… for about five minutes. It turns out extending a class is a brute-force approach. The second plugin had no way of knowing what the first one did, so it just stomped all over it. The real fix had to be at the architectural level, using something more flexible like the WordPress decorator pattern.
Understanding the Decorator Pattern for WordPress
Instead of extending a class (inheritance), the decorator pattern is about wrapping it (composition). You create a new class that holds the original object and implements the same interface. This lets you add functionality before or after calling the original object’s methods. And here’s the kicker: because your wrapper also implements the same interface, it can be wrapped by another decorator, and another. You can stack them like LEGOs.
The big hurdle with $wpdb is that it doesn’t have an interface. No contract to follow. But we can get around that with a bit of PHP magic. This approach is a solid way to handle a classic issue, and it builds on a great concept I saw over at carlalexander.ca.
Here’s a basic decorator that logs database errors without replacing $wpdb entirely.
/**
* Decorator for WPDB that checks for errors.
*/
class WPDBErrorCheckingDecorator
{
/**
* The decorated instance of WPDB.
*
* @var wpdb
*/
private $wpdb;
/**
* Constructor.
*
* @param wpdb $wpdb
*/
public function __construct($wpdb)
{
$this->wpdb = $wpdb;
}
/**
* Intercepts all calls to the WPDB object.
*/
public function __call($method, array $arguments)
{
// Only run our logic for methods that execute queries
$tracked_methods = ['delete', 'get_col', 'get_results', 'get_row', 'get_var', 'query', 'update'];
if ( ! in_array($method, $tracked_methods) ) {
return call_user_func_array([$this->wpdb, $method], $arguments);
}
// Run the original method
$return = call_user_func_array([$this->wpdb, $method], $arguments);
// Now, check for an error
if ( ! empty($this->wpdb->last_error) ) {
do_action('wpdb_error', $this->wpdb->last_error, $this->wpdb->last_query);
}
return $return;
}
/**
* Pass property access through to the original object.
*/
public function __get($variable)
{
return $this->wpdb->$variable;
}
/**
* Pass property setting through to the original object.
*/
public function __set($variable, $value)
{
$this->wpdb->$variable = $value;
}
}
// In your plugin's main file:
global $wpdb;
$wpdb = new WPDBErrorCheckingDecorator($wpdb);So, What’s the Takeaway?
The magic methods (__call, __get, __set) let our decorator act as a transparent pass-through for any method or property we don’t explicitly want to modify. We intercept the methods that actually run queries, run them, and then add our error-checking logic afterward. No conflicts. You could have another plugin wrap our `WPDBErrorCheckingDecorator` instance to add caching, and it would just work.
- Proxy Pattern (Extending): Easy to implement, but rigid. Creates conflicts because only one class can extend another.
- Decorator Pattern (Wrapping): More flexible and allows for stacking multiple functionalities. It’s the grown-up way to solve this problem.
For huge, fundamental changes like HyperDB, extending the class might still be the pragmatic choice. But for smaller, self-contained features like logging or caching, the decorator pattern is almost always the right call. It respects the ecosystem. It plays nice with other plugins. Period.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
Leave a Reply