Got a call from a client with a big WooCommerce site. They were seeing phantom errors—orders not completing, AJAX calls failing, but nothing in the logs. Nothing. The server logs were clean, and `WP_DEBUG` was a firehose of noise on a live site. It was one of those problems that you know is real, but you can’t prove it. The site was just… failing silently. We knew it had to be the database, but we couldn’t see *what* was happening. We needed a way to listen in on the database connection itself. That’s where the WordPress proxy pattern comes in.
My first thought was to use the `query` filter. It’s a standard WordPress hook that lets you see or modify a query before it runs. Simple, right? I set up a logger to capture every query. And yeah, we saw the queries. But here’s the kicker: the filter runs *before* the database does its thing. It tells you what WordPress *intended* to do, but it gives you zero information about whether the query actually succeeded or failed. It was a dead end for finding errors.
Directly modifying the core `wpdb` class is not an option. Ever. You do that, and you’ve created an unmaintainable mess that will break on the next core update. The real, professional solution is to use a design pattern. The proxy pattern lets you create a substitute, or “proxy,” for another class. Your proxy extends the original WordPress class, inherits all its methods, and lets you intercept calls to it. You can add your own logic before or after the original method runs, all without touching core code. It’s a clean, safe way to get inside a black box.
Building a Proxy for the WordPress Database
WordPress has a built-in, if not widely known, way to do this for the database object: a `db.php` drop-in. If you place a file named `db.php` in your `wp-content` directory, WordPress will load it instead of its own `wpdb` class. This is the perfect place to define our proxy. The idea here builds on a concept I first saw explained over at carlalexander.ca. It’s powerful stuff.
Our proxy class will extend the original `wpdb` class and override the `query()` method. Inside our new method, we first call the original `parent::query()` to let it run as usual. Then, after it’s done, we check the `$this->last_error` property. If it’s not empty, we know the query failed, and we can finally log it.
<?php
// In wp-content/db.php
if (!defined('ABSPATH')) {
die();
}
/**
* A wpdb proxy to log database errors.
*/
class Wael_wpdb_proxy extends wpdb {
/**
* Perform a database query and log any errors.
*
* @param string $query Database query
* @return int|false
*/
public function query($query) {
// Run the original query first.
$result = parent::query($query);
// Now, check for an error after the fact.
if ('' !== $this->last_error) {
// We finally caught it. Send the error and query to a custom action.
do_action('wpdb_query_error', $this->last_error, $this->last_query);
}
return $result;
}
}
// Tell WordPress to use our new class.
$wpdb = new Wael_wpdb_proxy(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);So, What’s the Point?
By firing a `do_action`, we’ve created a new, reliable hook that anyone on the team can use. You can hook into ‘wpdb_query_error’ to send the error to Slack, log it to a separate file, or even send it to an external monitoring service. And that was it. A few hours after deploying this, we caught the culprit—a race condition in a poorly written coupon plugin that was causing intermittent deadlocks. Problem solved.
The proxy pattern is more than just a debugging tool. You can use it for:
- Adding a caching layer to expensive queries.
- Blocking or redirecting specific types of queries for security.
- Measuring query performance more accurately than other tools allow.
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