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 you know is real but can’t prove. The site was just… failing silently. We were sure it was the database, but we couldn’t see what was actually happening. We needed a way to listen in on the database connection itself. That is where the WordPress proxy pattern comes in.
My first thought was the query filter. It is a standard WordPress hook that lets you see or change a query before it runs. Simple, right? I set up a logger to capture every query, and sure enough, we saw them all. The catch is that the filter runs before the database does anything. It tells you what WordPress meant to do, but nothing about whether the query actually succeeded or failed. Dead end.
Editing the core wpdb class directly is never an option. Do that and you’ve built an unmaintainable mess that breaks on the next core update. The safe approach is a design pattern. The proxy pattern lets you create a stand-in, a proxy, for another class. Your proxy extends the original WordPress class, inherits its methods, and lets you intercept calls to it. You can run your own logic before or after the original method, without touching core code. It is a clean 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. Put a file named db.php in your wp-content directory and WordPress loads it instead of its own wpdb class. That makes it the right place to define our proxy. This builds on an idea I first saw explained on carlalexander.ca.
Our proxy class extends the original wpdb class and overrides the query() method. Inside the new method, we first call parent::query() so the query runs as usual. Once it is done, we check the $this->last_error property. If it is not empty, 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 created a new, reliable hook the whole team can use. You can hook into ‘wpdb_query_error’ to send the error to Slack, write it to a separate file, or pass it to an external monitoring service. And that was it. A few hours after we deployed 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.
This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, get in touch with my team. We have probably seen your problem before.