• 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 WP HTTP API

WordPress Development Recipe: Real-time custom event triggers using WebSockets and WP HTTP API

Leveraging WebSockets for Real-time WordPress Event Triggers

This recipe details a robust method for implementing real-time, custom event triggers within WordPress, bypassing traditional polling mechanisms. We’ll architect a solution that utilizes WebSockets for instantaneous client-server communication, triggered by custom events fired within the WordPress ecosystem. This approach is particularly valuable for dynamic dashboards, collaborative editing features, or any scenario demanding immediate feedback on backend changes.

Core Components: WebSocket Server and WordPress Integration

The foundation of this system is a dedicated WebSocket server. For this example, we’ll use Ratchet, a popular PHP WebSocket library. The WordPress side will be responsible for detecting custom events and dispatching them to the WebSocket server via the WP HTTP API, acting as a webhook. This decouples the real-time layer from the core WordPress execution flow, ensuring performance and scalability.

Setting up the Ratchet WebSocket Server

First, ensure you have Composer installed. Navigate to a dedicated directory for your WebSocket server and initialize a new PHP project:

  • composer init

Next, install the Ratchet library:

  • composer require cboden/ratchet

Create a server file, e.g., server.php:

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

require dirname(__DIR__) . '/vendor/autoload.php';

class WordPressEventComponent implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);
        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        // In this setup, the server primarily broadcasts messages from WordPress.
        // If you need client-to-server messages, implement logic here.
        echo "Message received from {$from->resourceId}: {$msg}\n";
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we'll no longer send it messages
        $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }

    public function broadcast($msg) {
        foreach ($this->clients as $client) {
            // Only send to clients that are not the sender (if applicable)
            // For this recipe, we assume all messages are broadcast to all connected clients.
            $client->send($msg);
        }
    }
}

// Create a component and wrap it in a WebSocket server
$component = new WordPressEventComponent();
$wsServer = new WsServer($component);

// Wrap the WebSocket server in an HTTP server
$httpServer = new HttpServer($wsServer);

// Set up the server to listen on port 8080
$server = IoServer::factory($httpServer, 8080);

echo "WebSocket server started on port 8080...\n";

$server->run();

To run the server, execute:

php server.php

WordPress Plugin for Event Dispatching

Now, let’s create a WordPress plugin that hooks into custom actions and sends data to our WebSocket server. Create a new plugin directory, e.g., wp-content/plugins/realtime-events/, and add a main PHP file, realtime-events.php.

Plugin Structure and Initialization

The plugin will define a class to manage the WebSocket communication. We’ll use the WP HTTP API to send POST requests to a simple endpoint on our WebSocket server (which we’ll set up next). For simplicity, we’ll assume the WebSocket server is accessible at http://localhost:8080/dispatch. In a production environment, you’d likely use a dedicated API gateway or a more sophisticated webhook handler.

<?php
/**
 * Plugin Name: Realtime Events
 * Description: Dispatches custom WordPress events to a WebSocket server.
 * Version: 1.0
 * Author: Your Name
 */

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

class Realtime_Events_Dispatcher {
    private $websocket_url = 'http://localhost:8080/dispatch'; // URL to your WebSocket server's dispatch endpoint

    public function __construct() {
        // Hook into a custom action. Replace 'my_custom_event' with your actual event hook.
        add_action( 'my_custom_event', array( $this, 'handle_custom_event' ), 10, 1 );

        // Example: Hooking into post save for demonstration
        add_action( 'save_post', array( $this, 'handle_post_save_event' ), 10, 3 );

        // Add a simple endpoint to the WebSocket server for receiving events
        add_action( 'rest_api_init', array( $this, 'register_websocket_endpoint' ) );
    }

    /**
     * Handles a generic custom event and dispatches it to the WebSocket server.
     *
     * @param mixed $event_data Data associated with the event.
     */
    public function handle_custom_event( $event_data ) {
        $payload = array(
            'event' => 'my_custom_event',
            'data'  => $event_data,
            'timestamp' => current_time( 'mysql' ),
        );
        $this->send_to_websocket( $payload );
    }

    /**
     * Handles post save events and dispatches relevant data.
     *
     * @param int     $post_id Post ID.
     * @param WP_Post $post    Post object.
     * @param bool    $update  Whether this is an existing post being updated.
     */
    public function handle_post_save_event( $post_id, $post, $update ) {
        // Avoid triggering on autosaves and revisions
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return;
        }
        if ( wp_is_post_revision( $post_id ) ) {
            return;
        }

        $payload = array(
            'event'     => 'post_saved',
            'data'      => array(
                'post_id'   => $post_id,
                'title'     => $post->post_title,
                'status'    => $post->post_status,
                'type'      => $post->post_type,
                'is_update' => $update,
            ),
            'timestamp' => current_time( 'mysql' ),
        );
        $this->send_to_websocket( $payload );
    }

    /**
     * Sends data to the WebSocket server via HTTP POST.
     *
     * @param array $payload The data to send.
     */
    private function send_to_websocket( $payload ) {
        $args = array(
            'body'    => json_encode( $payload ),
            'headers' => array(
                'Content-Type' => 'application/json',
            ),
            'timeout' => 15, // Adjust timeout as needed
            'method'  => 'POST',
        );

        $response = wp_remote_post( $this->websocket_url, $args );

        if ( is_wp_error( $response ) ) {
            error_log( 'Realtime Events: Failed to send to WebSocket server - ' . $response->get_error_message() );
        } else {
            // Optional: Log successful dispatch
            // error_log( 'Realtime Events: Successfully dispatched event. Response: ' . wp_remote_retrieve_body( $response ) );
        }
    }

    /**
     * Registers a REST API endpoint on the WebSocket server to receive events.
     * This is a simplified approach. In production, you might have a dedicated
     * webhook receiver service.
     */
    public function register_websocket_endpoint() {
        register_rest_route( 'realtime-events/v1', '/dispatch', array(
            'methods'  => 'POST',
            'callback' => array( $this, 'handle_incoming_dispatch' ),
            'permission_callback' => '__return_true', // In production, implement proper authentication/authorization
        ) );
    }

    /**
     * Callback for the REST API endpoint to receive events from WordPress.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response
     */
    public function handle_incoming_dispatch( WP_REST_Request $request ) {
        $data = $request->get_json_params();

        if ( empty( $data ) || ! isset( $data['event'] ) ) {
            return new WP_REST_Response( 'Invalid payload', 400 );
        }

        // Here, we need to access the Ratchet server instance to broadcast.
        // This requires a more advanced setup where the REST API endpoint
        // can communicate with the running Ratchet server.
        // For this example, we'll simulate the broadcast.
        // In a real scenario, the REST API would likely push to a message queue
        // that the Ratchet server consumes, or use IPC.

        // For demonstration, let's assume we have a global $websocket_server instance
        // that holds our WordPressEventComponent. This is NOT production-ready.
        global $websocket_server_instance;
        if ( isset( $websocket_server_instance ) && $websocket_server_instance instanceof WordPressEventComponent ) {
            $websocket_server_instance->broadcast( json_encode( $data ) );
            return new WP_REST_Response( 'Event dispatched', 200 );
        } else {
            error_log( 'Realtime Events: WebSocket server instance not available for broadcast.' );
            return new WP_REST_Response( 'Server error', 500 );
        }
    }
}

new Realtime_Events_Dispatcher();

// Global variable to hold the WebSocket server instance (for demonstration purposes)
// In a real application, you'd manage this more robustly.
$websocket_server_instance = null;

Activate the “Realtime Events” plugin in your WordPress admin. Now, whenever do_action('my_custom_event', $some_data); or a post is saved, the plugin will attempt to send a JSON payload to your WebSocket server.

Bridging the Gap: WebSocket Server Endpoint

The Ratchet server needs to be able to receive these HTTP POST requests from WordPress. We can achieve this by integrating a simple HTTP server or a framework that can handle both WebSocket and HTTP requests. For this example, we’ll modify server.php to include a basic HTTP route for receiving events. This is a simplified approach; a production system might use a dedicated API gateway or a separate microservice.

We’ll use the React\Http\Server component for handling HTTP requests. First, install the necessary package:

  • composer require react/http

Modify server.php:

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

use Psr\Http\Message\ServerRequestInterface;
use React\Http\Response;
use React\Http\Server as HttpServerReact;
use React\EventLoop\Factory as LoopFactory;
use React\Socket\Server as SocketServer;

require dirname(__DIR__) . '/vendor/autoload.php';

class WordPressEventComponent implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        echo "New WebSocket connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        // Not used for incoming WordPress events in this setup
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        echo "WebSocket connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "WebSocket error: {$e->getMessage()}\n";
        $conn->close();
    }

    /**
     * Broadcasts a message to all connected WebSocket clients.
     * @param string $msg The message to broadcast.
     */
    public function broadcast($msg) {
        echo "Broadcasting: {$msg}\n";
        foreach ($this->clients as $client) {
            $client->send($msg);
        }
    }
}

// Instantiate the WebSocket component
$wsComponent = new WordPressEventComponent();

// Create the WebSocket server
$webSocketServer = new WsServer($wsComponent);

// Create the HTTP server for handling REST API calls
$httpServerReact = new HttpServerReact(function (ServerRequestInterface $request) use ($wsComponent) {
    // Handle REST API endpoint for receiving events from WordPress
    if ($request->getMethod() === 'POST' && $request->getUri()->getPath() === '/dispatch') {
        $body = $request->getBody()->getContents();
        $data = json_decode($body, true);

        if (json_last_error() === JSON_ERROR_NONE && isset($data['event'])) {
            // Broadcast the received event to all connected WebSocket clients
            $wsComponent->broadcast($body);
            return new Response(200, ['Content-Type' => 'application/json'], json_encode(['status' => 'success', 'message' => 'Event broadcasted']));
        } else {
            return new Response(400, ['Content-Type' => 'application/json'], json_encode(['status' => 'error', 'message' => 'Invalid JSON payload or missing event key']));
        }
    }

    // Handle WebSocket upgrade requests
    // The WsServer will handle the upgrade logic internally.
    // We just need to ensure it's part of the request handling pipeline.
    // This part is implicitly handled by how HttpServerReact and WsServer are composed.
    // For explicit handling, you might need more complex routing.

    // Default response for other routes
    return new Response(404, ['Content-Type' => 'text/plain'], 'Not Found');
});

// Combine WebSocket and HTTP servers
// This requires a bit more advanced setup with ReactPHP to handle both protocols on the same port.
// A common pattern is to use a proxy or a more sophisticated routing layer.
// For simplicity here, we'll run them on different ports or use a proxy.
// Let's assume a proxy (like Nginx) directs WebSocket traffic to port 8080 and HTTP to another port.

// --- Alternative: Running on the same port requires a more integrated approach ---
// For this example, we'll run the HTTP server on port 8000 and WebSocket on 8080.
// In production, Nginx or HAProxy would typically proxy these.

// --- Running HTTP Server on port 8000 ---
$loop = LoopFactory::create();
$socket = new SocketServer('0.0.0.0:8000', $loop);
$httpServerReact->listen($socket);
echo "HTTP server started on port 8000...\n";

// --- Running WebSocket Server on port 8080 ---
// The Ratchet IoServer is designed to handle WebSocket connections.
// We'll run it separately for clarity in this example.
$server = IoServer::factory(
    new HttpServer(
        // This HttpServer is from Ratchet, not ReactPHP
        new WsServer($wsComponent)
    ),
    8080
);

// Start both servers (this is a simplified execution model)
// In a real deployment, you'd use a process manager like Supervisor.
$loop->addTimer(0.1, function () use ($server) {
    $server->run();
});

$loop->run();

// Note: The global $websocket_server_instance assignment in the WordPress plugin
// needs to be handled by a mechanism that makes the running WsServer instance
// accessible. This is a significant architectural challenge for this direct integration.
// A more robust solution would involve a message queue or a dedicated API service.
// For the purpose of this recipe, we'll assume a mechanism exists to pass the $wsComponent
// instance to the WordPress REST API handler.
// For demonstration, let's simulate it:
global $websocket_server_instance;
$websocket_server_instance = $wsComponent; // This assignment is problematic in a real running server.

To run this combined server, you’ll need to manage two processes or use a process manager. For development, you can run:

  • php server.php (This will start both the HTTP and WebSocket servers, assuming the loop handles them correctly or you use a process manager).

Important Note on Server Integration: The provided server.php attempts to run both HTTP and WebSocket servers. In a real-world scenario, you would typically:

  • Use a reverse proxy (like Nginx or HAProxy) to direct WebSocket traffic (e.g., `/ws`) to your Ratchet server and HTTP API traffic (e.g., `/api`) to a separate PHP application or the same application running on a different port.
  • The global variable assignment $websocket_server_instance = $wsComponent; is a simplification for demonstration. In a production environment, the WordPress REST API callback needs a reliable way to access the running WebSocket server instance. This could involve a shared memory segment, a message queue (like Redis Pub/Sub or RabbitMQ), or a dedicated inter-process communication mechanism.

Client-Side Implementation (JavaScript)

On the client-side (e.g., within a theme’s JavaScript file or a frontend plugin), you’ll establish a WebSocket connection to your server and listen for incoming messages.

document.addEventListener('DOMContentLoaded', function() {
    // Replace with your WebSocket server URL
    var wsUrl = 'ws://localhost:8080';
    var websocket;

    try {
        websocket = new WebSocket(wsUrl);

        // Connection opened
        websocket.addEventListener('open', function (event) {
            console.log('WebSocket connection established.');
            // Optionally send a message to the server upon connection
            // websocket.send('Hello Server!');
        });

        // Listen for messages
        websocket.addEventListener('message', function (event) {
            console.log('Message from server: ', event.data);
            try {
                var data = JSON.parse(event.data);
                // Handle different event types
                if (data.event === 'post_saved') {
                    console.log('Post saved:', data.data);
                    // Update UI, show notification, etc.
                    alert('A post was just saved: ' + data.data.title);
                } else if (data.event === 'my_custom_event') {
                    console.log('Custom event received:', data.data);
                    // Handle your custom event
                }
            } catch (e) {
                console.error('Failed to parse message data:', e);
            }
        });

        // Listen for errors
        websocket.addEventListener('error', function (event) {
            console.error('WebSocket error observed:', event);
        });

        // Connection closed
        websocket.addEventListener('close', function (event) {
            if (event.wasClean) {
                console.log(`WebSocket connection closed cleanly, code=${event.code} reason=${event.reason}`);
            } else {
                console.error('WebSocket connection died');
            }
            // Attempt to reconnect after a delay
            setTimeout(function() {
                console.log('Attempting to reconnect...');
                // You might want to implement a more sophisticated reconnect strategy
                // with exponential backoff.
                // For now, just reload or re-initiate connection.
                // window.location.reload(); // Or re-initialize the websocket object
            }, 5000); // Try to reconnect every 5 seconds
        });

    } catch (error) {
        console.error('Failed to initialize WebSocket:', error);
        // Handle initial connection failure
    }

    // Example of how to trigger a custom event from the frontend (optional)
    // This would typically involve an AJAX call to a WordPress REST API endpoint
    // that then triggers the 'my_custom_event' action.
    /*
    document.getElementById('trigger-event-button').addEventListener('click', function() {
        fetch('/wp-json/myplugin/v1/trigger-custom-event', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ message: 'Triggered from frontend!' })
        })
        .then(response => response.json())
        .then(data => console.log('Frontend trigger response:', data))
        .catch(error => console.error('Frontend trigger error:', error));
    });
    */
});

To integrate this JavaScript, you can enqueue it as a script in your WordPress theme’s functions.php or within a dedicated frontend plugin:

function realtime_events_enqueue_scripts() {
    // Ensure this path is correct relative to your theme or plugin
    wp_enqueue_script( 'realtime-events-client', get_template_directory_uri() . '/js/realtime-client.js', array(), '1.0', true );
    // Or if it's in a plugin: plugins_url( '/js/realtime-client.js', __FILE__ )
}
add_action( 'wp_enqueue_scripts', 'realtime_events_enqueue_scripts' );

Production Considerations and Enhancements

For production deployments, several aspects require careful consideration:

  • Scalability: The single Ratchet server might become a bottleneck. Consider using a message broker (e.g., Redis Pub/Sub, RabbitMQ) where WordPress publishes events, and multiple WebSocket server instances subscribe to the broker and broadcast to their connected clients.
  • Authentication & Authorization: Secure your WebSocket endpoint and the WordPress dispatch endpoint. For WebSockets, you might pass tokens during the initial handshake. For the HTTP dispatch, use API keys or OAuth.
  • Error Handling & Reconnection: Implement robust error handling and automatic reconnection strategies on the client-side, including exponential backoff.
  • Process Management: Use a process manager like Supervisor to ensure your WebSocket server process is always running and automatically restarts if it crashes.
  • Security: Sanitize and validate all data passed between WordPress and the WebSocket server. Be mindful of potential injection attacks.
  • Deployment: Configure a reverse proxy (Nginx, HAProxy) to handle SSL termination, load balancing, and routing of WebSocket (WSS) and HTTP traffic.
  • State Management: If clients need to maintain state or subscribe to specific events, implement a mechanism for this (e.g., sending subscription messages from client to server).

This recipe provides a foundational architecture for real-time event handling in WordPress. By combining the power of WebSockets with the flexibility of the WP HTTP API and custom actions, you can build highly dynamic and responsive web 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

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Categories

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

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

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