Skip to main content
Markdown

Channels

A channel carries events to every browser subscribed to it. Your server publishes to it (publishing), and browsers may send each other events on it (client events).

Names and kinds​

Channel names follow Pusher's rules, which many developers already know: up to 200 characters of A-Z a-z 0-9 _ - = @ , . ;.

NameKindWho may subscribe
orderspublicanyone with the site's key: put nothing private here
private-orders-42privatewhoever your callback lets in
presence-room-7presencewhoever your callback lets in, listed to everyone on it (presence)
  • Each site's channels are its own. Two sites' orders never meet.
  • They are separate from the Pusher-compatible API's. A Pusher client can't join them, and the same name on both is two different channels.
  • private-encrypted-… names are not taken.

Owning channels​

A plugin owns the channels whose names start with its prefix, and decides who may join them:

add_action( 'plugins_loaded', function () {
wordplus_realtime()->channel( 'private-orders-', function ( $user_id, $channel ) {
$order = (int) substr( $channel, strlen( 'private-orders-' ) );
return $user_id > 0 && my_shop_customer_of( $order ) === $user_id;
} );
} );
  • $user_id is the signed-in user, or 0 for a visitor.
  • The answer is false, true, or an array. For a presence channel the array may carry info, which everyone on the channel sees, and id for a member who isn't a WordPress user (presence).
  • The longest prefix wins: register private-orders-vip- beside private-orders- and the first decides for its channels.
  • A private or presence channel nobody owns is refused to everyone.

Subscribing in the browser​

Your script depends on the SDK's handle, and the page gets its settings in window.wordplusRealtimeConfig:

wp_enqueue_script( 'my-shop', plugins_url( 'shop.js', __FILE__ ), array( wordplus_realtime()->script() ), '1.0.0', true );
if ( window.wordplusRealtimeConfig ) {
const rt = wordplusRealtimeClient.channels( window.wordplusRealtimeConfig );

const order = rt.subscribe( 'private-orders-42' );
order.on( 'updated', ( data ) => showStatus( data.status ) );
order.on( 'realtime:subscribed', () => console.log( 'listening' ) );
order.on( 'realtime:error', ( e ) => console.warn( e.code ) ); // forbidden, expired, bad_token, auth_failed …

// Later:
order.unsubscribe(); // or rt.unsubscribe( 'private-orders-42' )
}
  • subscribe() returns the same subscription for the same name, and works before the connection is up.
  • An event's listener gets ( data, meta ). meta.user_id is set for a client event, naming the user who sent it.
  • once( event, fn ) listens once. off( event, fn ) stops listening.
  • rt.on( 'error', fn ) hears the connection refused, with { code }: not_included, not_licensed, wrong_origin … (errors).
  • rt.on( 'connected' | 'disconnected' | 'moved', fn ) follow the connection.
  • rt.close() leaves every channel. The browser's other tabs keep the connection.

The page's own events​

The SDK's own events are named realtime:…. Your server can't publish such a name (bad_request), and the page drops one sent by a browser, so neither can be faked.

EventGetsWhen
realtime:subscribedthe members, for presencethe subscription is in place, again after each reconnect
realtime:error{ code }the subscription was refused
realtime:member_added{ id, info }someone joined a presence channel
realtime:member_removed{ id }someone left it
realtime:unopened{ event, label }an event came sealed with a key the site won't give this user (encryption)

Sealed​

What your server publishes to a private or presence channel, what browsers send each other on it, and its members' info travel sealed with the channel's key, which comes with the channel's token. The page opens them before your listener sees them, in the order they came. You write nothing for it; see encryption.

Tokens​

A private or presence channel needs a token, which the site signs and nobody else can make:

  • The page asks the site's auth route, POST /wp-json/wordplus/v1/realtime/auth with { channels: [...] }. The channels subscribed in the same moment go in one request.
  • Each channel's owner answers. The route returns one token for the channels allowed, and the reason for each refused (denied).
  • The token is kept and renewed ten minutes before it runs out, so a move to another server or a reconnect never asks the site again.
  • Tokens live 6 hours by default (the filter wordplus_realtime_token_ttl, at most 24 hours).
  • A function in place of the route: pass authorize: ( channels ) => Promise<{ token, exp, denied }> to channels() to sign elsewhere.

Moves and reconnects​

The connection moves to another realtime server without a drop when one restarts. The page subscribes every channel again on the new connection, with the tokens it holds, before switching over. After a reconnect it does the same. Presence counts each user's connections, so a move never shows anyone leaving.

One connection for the whole browser​

Every tab and every plugin of a browser rides one connection to the site's channels. A tab hears only the events of its own subscriptions. An event a tab caused itself, a client event or a publish with except naming that tab, doesn't come back to it.

Limits​

See limits: 1,000 channels a connection, events of up to 10 KB, and 1,000 members a presence channel.