PHP reference: wordplus/realtime-php
The package needs PHP 8.1 or later, with sodium, and curl or PHP's streams. It takes the same options as the Node and Python packages, and answers the browser the same way. In Laravel, the Realtime facade and app( Realtime::class ) are one instance, made from config/wordplus-realtime.php (Laravel).
new Realtime( $options )
Realtime::fromEnvironment( $options ) is the same, with site, secret and local_key taken from WORDPLUS_SITE, WORDPLUS_SECRET and WORDPLUS_LOCAL_KEY.
| Option | |
|---|---|
site, secret | your app's key and secret |
local_key | 64 hex characters of your own (encryption) |
profile | fn ( $user ) => [ 'name' => …, 'avatar' => …, 'url' => … ] or null: the user's directory entry, and by default their presence info and call info |
has_keys | fn ( $user ) => false cuts a user off every key |
generations | a GenerationStore, for rotateChannel() and rotateUser() |
seal | false sends everything plain |
base | where your routes are, /api/realtime by default, for config() |
headers | headers the browser sends to your routes, such as a CSRF token, for config() |
worker, rooms | where your app serves the two files, for config() |
token_ttl, call_token_ttl | seconds: 6 hours by default; a channels token lasts at most a day, a calls token at most 6 hours |
server, api | WordPlus Cloud by default |
http | fn ( $method, $url, $headers, $body ) => [ 'status' => …, 'body' => … ], your own client for publishing |
A user is the id your app gives, an integer or a string, or null for nobody. In this version, calls and the directory take integer ids only.
Owners
| Method | |
|---|---|
channel( $prefix, fn ( $user, $channel ) => $answer ) | private and presence channels, by the start of their names; answers false, true, or [ 'id' => …, 'info' => … ] (channels, presence) |
calls( $prefix, fn ( $user, $call, $with ) => $answer ) | calls and rooms; answers false, true, or [ 'info', 'publish', 'subscribe', 'admin', 'hidden' ] (calls) |
kind( $kind, fn ( $rest, $user, $label ) => bool, $newest = null ) | keys of your own kind (encryption) |
fallback( fn ( $user, $channel ) => $answer ) | the channels no prefix owns; in Laravel, routes/channels.php |
The longest prefix that fits decides. Each method returns the instance, so calls chain.
Routes
| Method | |
|---|---|
respond( $action, $user ) | answers the current request in plain PHP: auth, calls or keys, with its status, headers and JSON |
answerRequest( $method, $contentType, $action, $body, $user ) | → [ 'status', 'body' ], for a framework of your own |
handle( $action, $body, $user ) | → [ 'status', 'body' ], for a body already decoded |
The routes take JSON POSTs only, and answer anything else with 415. A page on another site can't send JSON without asking first, and it is never allowed to. A refusal is part of the answer (denied), not an HTTP error.
| Method | What it answers |
|---|---|
authorize( $channels, $user ) | [ 'token', 'exp', 'keys', 'denied' ] |
authorizeCall( $request, $user ) | [ 'token', 'exp', 'key' ] or [ 'denied' => … ] |
keysAnswer( $labels, $user ) | [ 'keys', 'identity'? ] |
identity( $user ) | [ 'token', 'exp' ] or null |
token( $channels, $user = null, $info = null, $ttl = null ) | a channels token, signed as the auth route signs |
config( $user = null, $headers = [] ) | the browser's settings for connect(), with a signed-in user's keys and identity |
mayJoin( $channel, $user ), mayHave( $label, $user ) | what the routes decide, for your own checks |
Publishing
| Method | |
|---|---|
publish( $channels, $event, $data = null, [ 'except' => $tab, 'seal' => false ] ) | one channel, or up to 100; data up to 10 KB of JSON (publishing) |
publishBatch( [ [ 'channels', 'event', 'data', 'except', 'seal' ], … ] ) | 10 events a request |
channelInfo( $channel ) | [ 'subscriptions', 'members'? ] |
rotateChannel( $channel ), rotateUser( $user ) | moves a key on; needs generations |
They throw RealtimeError when WordPlus Cloud refuses: $e->errorCode holds the code, and $e->status the HTTP status.
Keys
$realtime->keys() is the app's Keys:
| Method | |
|---|---|
seal( $text, $label ), sealJson( $data, $label ) | a sealed value, {label}:1.{…} |
open( $value ) | the text, or null |
entry( $label ) | a key as the browser takes it |
dayLabel(), channelLabel( $channel ), userLabel( $user ) | the labels the SDK uses |
Generations
interface GenerationStore
{
public function get( string $name ): ?int;
public function set( string $name, int $generation ): void;
}
Keep them where all your servers see them, and for good: a database row, or a cache that doesn't evict. In Laravel, WordPlus\Realtime\Laravel\CacheGenerations keeps them in a cache store.
Helpers
Realtime::channelKind( $channel ) gives public, private or presence. Realtime::validChannel( $channel ) says whether WordPlus Cloud takes the name.