• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » WordPress Development Recipe: Real-time custom event triggers using WebSockets and WordPress Database Class ($wpdb)

WordPress Development Recipe: Real-time custom event triggers using WebSockets and WordPress Database Class ($wpdb)

Leveraging WebSockets for Real-time WordPress Event Triggers

Traditional WordPress development often relies on polling or AJAX for near real-time updates. However, for true real-time interactivity – think live chat, collaborative editing, or instant notifications – a persistent, bidirectional communication channel is essential. WebSockets provide this capability. This recipe outlines how to integrate WebSockets with WordPress, specifically triggering events based on database changes detected via the WordPress Database Class ($wpdb).

Prerequisites and Setup

Before diving into the code, ensure you have the following in place:

  • A WordPress installation with administrative access.
  • Basic understanding of PHP, JavaScript, and the WordPress Plugin API.
  • A WebSocket server implementation. For this example, we’ll assume a simple Node.js server using the ws library. You can adapt this to other server technologies (e.g., Ratchet for PHP, or managed services like Pusher/Ably).
  • Composer installed for PHP dependency management.

We’ll also need a mechanism to detect database changes. While WordPress doesn’t natively offer a robust database trigger system accessible directly from PHP, we can hook into actions that modify the database and then use $wpdb to query for specific changes or states.

Node.js WebSocket Server Implementation

This is a minimal Node.js server that listens for connections and broadcasts messages. It will act as the intermediary between your WordPress site and the client-side JavaScript.

Server Setup (Node.js)

First, set up a new Node.js project:

mkdir wp-websocket-server
cd wp-websocket-server
npm init -y
npm install ws

Create a file named server.js:

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

console.log('WebSocket server started on port 8080');

wss.on('connection', (ws) => {
    console.log('Client connected');

    ws.on('message', (message) => {
        console.log(`Received message => ${message}`);
        // In a real app, you might broadcast this or process it
    });

    ws.on('close', () => {
        console.log('Client disconnected');
    });

    ws.send('Welcome to the WordPress WebSocket server!');
});

// Function to broadcast messages to all connected clients
function broadcast(data) {
    wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
            client.send(JSON.stringify(data));
        }
    });
}

// Export broadcast function for use in other modules
module.exports = { broadcast };

To run the server:

node server.js

Keep this server running in the background. For production, consider using a process manager like PM2.

WordPress Plugin Development: Event Triggering

We’ll create a simple WordPress plugin that hooks into common actions (like post updates) and, if a specific condition is met, sends a message to our WebSocket server.

Plugin Structure

Create a new directory for your plugin, e.g., wp-content/plugins/wp-realtime-events. Inside, create a main plugin file, e.g., wp-realtime-events.php.

<?php
/**
 * Plugin Name: WP Realtime Events
 * Description: Triggers custom events via WebSockets on database changes.
 * Version: 1.0
 * Author: Your Name
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

// Define WebSocket server details
define( 'WP_WEBSOCKET_SERVER_HOST', 'localhost' ); // Or your server IP/domain
define( 'WP_WEBSOCKET_SERVER_PORT', 8080 );

/**
 * Function to send data to the WebSocket server.
 *
 * @param array $data The data to send.
 * @return bool True on success, false on failure.
 */
function wrt_send_to_websocket_server( $data ) {
    $host = WP_WEBSOCKET_SERVER_HOST;
    $port = WP_WEBSOCKET_SERVER_PORT;

    // Use fsockopen for a simple, non-blocking connection.
    // For more robust error handling or persistent connections, consider a dedicated library.
    $fp = @fsockopen( $host, $port, $errno, $errstr, 1 ); // 1-second timeout

    if ( ! $fp ) {
        error_log( "WP Realtime Events: Could not connect to WebSocket server ({$host}:{$port}) - {$errstr} ({$errno})" );
        return false;
    }

    // Construct a WebSocket message (simplified for this example)
    // A real implementation might need to adhere to WebSocket framing protocols.
    // For simplicity, we're sending a JSON string. The Node.js server is expecting this.
    $message = json_encode( $data );

    // Basic HTTP Upgrade request to establish WebSocket connection
    // This is a simplified approach. A full WebSocket handshake is more complex.
    // For this recipe, we assume the Node.js server is configured to accept simple JSON messages
    // over a basic TCP connection or handles the handshake implicitly.
    // A more robust solution would involve a proper WebSocket client library in PHP or
    // a dedicated PHP WebSocket server (e.g., Ratchet).

    // For a direct TCP connection to a simple WS server expecting JSON:
    $request = "GET / HTTP/1.1\r\n";
    $request .= "Host: {$host}:{$port}\r\n";
    $request .= "Upgrade: WebSocket\r\n";
    $request .= "Connection: Upgrade\r\n";
    $request .= "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"; // Example key
    $request .= "Sec-WebSocket-Version: 13\r\n";
    $request .= "\r\n";

    fwrite( $fp, $request );
    $response = fread( $fp, 1024 );

    // Check if the handshake was successful (simplified check)
    if ( strpos( $response, '101 Switching Protocols' ) === false ) {
        error_log( "WP Realtime Events: WebSocket handshake failed. Response: " . $response );
        fclose( $fp );
        return false;
    }

    // Now send the actual data as a WebSocket frame (simplified)
    // A proper frame would involve masking and length encoding.
    // For this basic example, we'll send the JSON string directly.
    // The Node.js server needs to be able to parse this.
    // A more correct approach would be to use a PHP WebSocket client library.

    // Simplified sending: Assume the server expects raw data after handshake.
    // This is NOT standard WebSocket framing. For production, use a library.
    $payload = $message;
    $payload_len = strlen( $payload );
    $header = pack('C', 0x81); // FIN + Text frame
    if ($payload_len < 126) {
        $header .= pack('C', $payload_len);
    } elseif ($payload_len < 65536) {
        $header .= pack('C', 126);
        $header .= pack('n', $payload_len);
    } else {
        $header .= pack('C', 127);
        $header .= pack('N', ($payload_len >> 32) & 0xFFFFFFFF);
        $header .= pack('N', $payload_len & 0xFFFFFFFF);
    }

    fwrite( $fp, $header . $payload );

    fclose( $fp );
    return true;
}

/**
 * Hook into post update action.
 *
 * @param int     $post_id The ID of the post being updated.
 * @param WP_Post $post    The post object.
 * @param bool    $update  Whether this is an existing post being updated.
 */
function wrt_trigger_post_update_event( $post_id, $post, $update ) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( wp_is_post_revision( $post_id ) ) {
        return;
    }

    // Example: Trigger event only for published posts of type 'post'
    if ( $post->post_type === 'post' && $post->post_status === 'publish' ) {
        global $wpdb;

        // Example: Check for a specific meta key change or a simple content update
        $previous_content = get_post_meta( $post_id, '_wrt_previous_content', true );
        $current_content = $post->post_content;

        // Simple check: if content has changed, or if it's a new publish
        if ( $current_content !== $previous_content || ! $previous_content ) {
            $event_data = array(
                'event' => 'post_updated',
                'post_id' => $post_id,
                'title' => $post->post_title,
                'timestamp' => current_time( 'mysql' ),
                'user_id' => get_current_user_id(),
            );

            wrt_send_to_websocket_server( $event_data );

            // Update the stored previous content for the next check
            update_post_meta( $post_id, '_wrt_previous_content', $current_content );
        }
    }
}
add_action( 'save_post', 'wrt_trigger_post_update_event', 10, 3 );

/**
 * Clean up meta data on post deletion.
 */
function wrt_cleanup_post_meta( $post_id ) {
    delete_post_meta( $post_id, '_wrt_previous_content' );
}
add_action( 'delete_post', 'wrt_cleanup_post_meta' );

/**
 * Enqueue JavaScript for client-side WebSocket connection.
 */
function wrt_enqueue_scripts() {
    // Only enqueue on the front-end
    if ( ! is_admin() ) {
        wp_enqueue_script( 'wrt-websocket-client', plugin_dir_url( __FILE__ ) . 'js/websocket-client.js', array(), '1.0', true );
        // Pass WebSocket server details to the script
        wp_localize_script( 'wrt-websocket-client', 'wrt_config', array(
            'websocket_url' => 'ws://' . WP_WEBSOCKET_SERVER_HOST . ':' . WP_WEBSOCKET_SERVER_PORT,
        ) );
    }
}
add_action( 'wp_enqueue_scripts', 'wrt_enqueue_scripts' );

Important Notes on wrt_send_to_websocket_server:

  • The provided wrt_send_to_websocket_server function uses fsockopen for a basic TCP connection and attempts a simplified WebSocket handshake. This is **not a production-ready WebSocket client implementation** in PHP. Standard WebSocket requires specific framing and masking.
  • For robust production environments, consider using a dedicated PHP WebSocket client library (e.g., cb-websocket, Ratchet\Client if you’re running a PHP WebSocket server) or an external service.
  • The Node.js server is assumed to be simple enough to handle this basic communication or a proper handshake.
  • Error handling is minimal. Production code would require more sophisticated error checking and retry mechanisms.

Client-Side JavaScript

Create a js directory inside your plugin folder and add a websocket-client.js file.

// js/websocket-client.js

document.addEventListener('DOMContentLoaded', (event) => {
    if (typeof wrt_config === 'undefined' || !wrt_config.websocket_url) {
        console.error('WP Realtime Events: WebSocket configuration not found.');
        return;
    }

    const wsUrl = wrt_config.websocket_url;
    let websocket;

    function connectWebSocket() {
        console.log(`Attempting to connect to WebSocket: ${wsUrl}`);
        websocket = new WebSocket(wsUrl);

        websocket.onopen = () => {
            console.log('WebSocket connection opened.');
            // Send a message to confirm connection or subscribe to events
            websocket.send(JSON.stringify({
                action: 'subscribe',
                channel: 'posts'
            }));
        };

        websocket.onmessage = (event) => {
            console.log('Message from server: ', event.data);
            try {
                const data = JSON.parse(event.data);
                // Handle incoming events here
                if (data.event === 'post_updated') {
                    console.log(`Post updated: ID ${data.post_id}, Title: ${data.title}`);
                    // Example: Update a UI element without a full page reload
                    const notificationElement = document.getElementById('wrt-notification');
                    if (notificationElement) {
                        notificationElement.textContent = `Post "${data.title}" was just updated!`;
                        notificationElement.style.display = 'block';
                        setTimeout(() => { notificationElement.style.display = 'none'; }, 5000);
                    }
                }
            } catch (e) {
                console.error('Failed to parse WebSocket message:', e);
            }
        };

        websocket.onerror = (error) => {
            console.error('WebSocket Error: ', error);
        };

        websocket.onclose = (event) => {
            console.log('WebSocket connection closed.', event.code, event.reason);
            // Attempt to reconnect after a delay
            setTimeout(connectWebSocket, 5000); // Reconnect every 5 seconds
        };
    }

    connectWebSocket();

    // Optional: Add a simple notification area to the front-end
    const notificationDiv = document.createElement('div');
    notificationDiv.id = 'wrt-notification';
    notificationDiv.style.position = 'fixed';
    notificationDiv.style.bottom = '20px';
    notificationDiv.style.right = '20px';
    notificationDiv.style.backgroundColor = '#4CAF50';
    notificationDiv.style.color = 'white';
    notificationDiv.style.padding = '15px';
    notificationDiv.style.borderRadius = '5px';
    notificationDiv.style.zIndex = '10000';
    notificationDiv.style.display = 'none';
    document.body.appendChild(notificationDiv);
});

Database Interaction with $wpdb

The core of detecting changes relies on WordPress hooks and the $wpdb global object. While $wpdb itself doesn’t provide triggers, it’s essential for querying data and understanding the state of your WordPress database.

Querying for Specific Changes

In the wrt_trigger_post_update_event function, we used get_post_meta and update_post_meta. This is a common way to track changes without directly querying the database tables for every save. However, for more complex scenarios, you might use $wpdb directly:

/**
 * Example of using $wpdb to check for specific data changes.
 * This is illustrative and would typically be part of a more complex hook.
 */
function wrt_check_specific_data_change() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'options'; // Example: wp_options table

    // Let's say we want to track changes to a specific option value
    $option_name = 'my_custom_setting';
    $current_value = get_option( $option_name );

    // To detect changes, we'd need to store the previous value somewhere.
    // This could be in another option, a custom table, or transient.
    $previous_value = get_option( '_wrt_previous_option_value_' . $option_name );

    if ( $current_value !== $previous_value && $current_value !== false ) {
        $event_data = array(
            'event' => 'option_updated',
            'option_name' => $option_name,
            'new_value' => $current_value, // Be cautious about sending sensitive data
            'timestamp' => current_time( 'mysql' ),
        );
        wrt_send_to_websocket_server( $event_data );

        // Update the stored previous value
        update_option( '_wrt_previous_option_value_' . $option_name, $current_value );
    } elseif ( $current_value === false && $previous_value !== false ) {
        // Option was deleted
        $event_data = array(
            'event' => 'option_deleted',
            'option_name' => $option_name,
            'timestamp' => current_time( 'mysql' ),
        );
        wrt_send_to_websocket_server( $event_data );
        delete_option( '_wrt_previous_option_value_' . $option_name );
    }
}
// You would hook this function to an appropriate action, e.g., 'update_option' or 'delete_option'
// add_action( 'update_option', 'wrt_check_specific_data_change', 10, 3 ); // Note: update_option hook parameters differ
// add_action( 'delete_option', 'wrt_check_specific_data_change', 10, 1 );

When using $wpdb for complex queries or to monitor specific table changes, remember to:

  • Always use $wpdb methods like prepare() for security, especially when constructing queries with dynamic values.
  • Be mindful of performance. Frequent, heavy queries can impact your WordPress site’s speed.
  • Leverage WordPress hooks strategically to minimize the overhead of checking for changes.

Production Considerations and Enhancements

This recipe provides a foundational understanding. For a production-ready system, consider the following:

Scalability and Reliability

  • WebSocket Server: Use a robust WebSocket server solution (e.g., Socket.IO, or a managed service) that handles reconnections, scaling, and load balancing.
  • PHP WebSocket Client: Implement a reliable PHP WebSocket client. Libraries like GuzzleHttp\Psr7 combined with a WebSocket client implementation or a dedicated library are recommended over raw fsockopen.
  • Message Queues: For high-traffic sites, decouple the event detection from sending messages. Use a message queue (like RabbitMQ or Redis Streams) to buffer events and have a separate worker process send them to the WebSocket server.
  • Error Handling & Retries: Implement comprehensive error handling and automatic retry mechanisms for WebSocket connections and message sending.
  • Security: Secure your WebSocket endpoint. Implement authentication and authorization to ensure only legitimate clients can connect and receive data. Consider WSS (WebSocket Secure) for encrypted communication.

Advanced Eventing

  • Event Channels/Topics: Allow clients to subscribe to specific event channels (e.g., ‘new_comments’, ‘product_updates’) rather than receiving all events.
  • Data Filtering: Implement server-side filtering to send only relevant data to clients.
  • User-Specific Events: Trigger events only for specific logged-in users or user roles.
  • Database Triggers (External): For very high-performance needs, consider using native database triggers (if your database supports them and you have the infrastructure) that write to a log table or queue, which your application then monitors.

Monitoring and Debugging

  • Implement detailed logging on both the WordPress side and the WebSocket server.
  • Use browser developer tools to inspect WebSocket traffic and client-side JavaScript errors.
  • Monitor server resource usage (CPU, memory, network).

By combining WordPress hooks, the power of $wpdb for data introspection, and the real-time capabilities of WebSockets, you can build highly dynamic and interactive experiences directly within your WordPress applications.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (662)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (5)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • Server (118)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (726)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (726)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala