Core concepts

Conveyor SubProtocol

“Patterns are the language of our craft. They provide a shared vocabulary for us to communicate, innovate, and efficiently solve common challenges in software development.”

Erich Gamma


This implementation is about the basic usage of Socket Conveyor, without the Conveyor Server.

The basic usage

Conveyor processes every incoming message at the “message” event of the WebSocket server. For that, do as follows:

1<?php
2
3use OpenSwoole\WebSocket\Server;
4use OpenSwoole\WebSocket\Frame;
5use Conveyor\ConveyorServer;
6
7$websocket = new Server('0.0.0.0', 8080);
8$websocket->on('message', fn (Server $server, Frame $frame) => 
9    Conveyor::init()
10        ->server($server)
11        ->fd($frame->fd)
12        ->run($frame->data));
13$websocket->on('request', fn(Request $rq, Response $rp) => $rp->end($html));
14$websocket->start();

This solution will handle every incoming message and process it, providing all Socket Conveyor's features. Thats it!

The Workflow

When using Socket Conveyor within an existing OpenSwoole server, you need to pay attention to its workflow. Socket Conveyor has a Workflow to make sure a sequence of pieces are in place at any given moment. This is a part that os always evolving, so having some visualizable structure makes the work easier.

Here is a diagram of the workflow in place:

Conveyor SubProtocol Workflow

This diagram represents the Workflow implemented in Socket Conveyor. This workflow happens in chain in the code, we will check that later. For now, let's see the list of functions that the developer has access to in the actual usage of this class to accomplish the workflow:

Full example:

1<?php
2
3Conveyor\Conveyor::init()
4    ->server($server)
5    ->fd($fd)
6    // here we replace any default persistence in Conveyor (must implement ConveyorPersistenceInterfacesGenericPersistenceInterface)
7    ->persistence()
8    // here we add extra actions (must implement ConveyorActionsInterfacesActionInterface)
9    ->addActions([new SampleAction()])
10    // here we add middlewares to actions (must be callables)
11    ->addMiddlewareToAction(SampleAction::NAME, new SampleMiddleware())
12    ->run(json_encode([
13        'action' => SampleAction::NAME,
14        'token'  => 'invalid-token',
15        'second-verification'  => 'valid',
16    ]));

Explanation:

  1. Function: server

    • Dependencies: Starts from the started state.
    • Next State: Transitions to the server_set state.
  2. Function: fd

    • Dependencies: Starts from the server_set state.
    • Next State: Transitions to the fd_set state.
  3. Function: persistence

    • Dependencies: Can start from any of the following states:
      • fd_set
      • actions_added
      • middleware_added
    • Next State: Transitions to the persistence_set state.
  4. Function: addActions

    • Dependencies: Can start from any of the following states:
      • fd_set
      • persistence_set
      • middleware_added
      • actions_added (self-transition)
    • Next State: Transitions to the actions_added state.
  5. Function: addMiddlewareToAction

    • Dependencies: Can start from any of the following states:
      • fd_set
      • persistence_set
      • actions_added
      • middleware_added (self-transition)
    • Next State: Transitions to the middleware_added state.
  6. Function: run

    • Dependencies: Can start from any of the following states:
      • persistence_set
      • actions_added
      • middleware_added
    • Next State: Transitions to the action_prepared state.

At this stage, there is a sequence of transitions that happens without functions:

  1. Transition: prepare_pipeline

    • Dependencies: Starts from the action_prepared state.
    • Next State: Transitions to the pipeline_prepared state.
  2. Transition: process_message

    • Dependencies: Starts from the pipeline_prepared state.
    • Next State: Transitions to the message_processed state.
  3. Transition: finalize

    • Dependencies: Starts from the message_processed state.
    • Next State: Transitions to the finalized state.

Each function represents a transition in the workflow, and then, the final few transitions happen after the "run" function call. This documentation can serve as a guide for understanding the flow and dependencies of the system.

Available Actions

This package comes with some out-of-the-box Actions, but you can (and probably will need) build your own for your own needs by extending the existent ones or creating new. To learn more how to create your own, check here: Creating your own Actions.

Associate User to Fd

Conveyor\Actions\AssocUserToFdAction

Action responsible for associating users to connections.

Structure:

1{
2    "action": "assoc-user-to-fd-action",
3    "userId": 1
4}

Base (default)

Conveyor\Actions\BaseAction

This is the base action. Works like a ping pong, returning the message to the client who sent it.

Structure:

1{
2    "action": "base-action",
3    "data": "message"
4}

Notice that this action also works if you don’t send a JSON, forcing the action to be the base one.

Broadcast

Conveyor\Actions\BroadcastAction

This is for messages to be broadcasted on the context of the connection that dispatches it.

Structure:

1{
2    "action": "broadcast-action",
3    "data": "message"
4}

Channel Connect

Conveyor\Actions\ChannelConnectAction

Action used to connect to a channel.

Structure:

1{
2    "action": "channel-connect",
3    "channel": "channel-name"
4}

Channel Disconnect

Conveyor\Actions\ChannelDisconnectAction

Action used to disconnect from a channel.

Structure:

1{
2    "action": "channel-disconnect"
3}

Fanout

Conveyor\Actions\FanoutAction

Action used to broadcast without context borders (to every client in the server).

Structure:

1{
2    "action": "fanout-action",
3    "data": "message"
4}

Extra notes

Conveyor is very flexible, and allows you to create your own actions, extending its protocol. If that's what you want, you can find how to here: Create you own actions

Previous
Conveyor