PHP has a number of REALLY good libraries to handle the WebSocket Protocol out there - some of which I solely based this project upon. This package/library/project offers a number of features present in those, using ReactPHP as its asyncronous handler. I developed this project as a way to understand in depth how the protocol worked in real life and how I could create something more lower-level with it without the need to use something more lower-level or too much abstracted, such as Socket.io and others. Build your application using small, reusable and atomic components if you want or simply drop a single handler for incoming data, this library will handle compression, deflation and connection for you.
- IMPORTANT
- How to
- Requirements
- Configuration
- Features Checklist + Challenges
- Understanding The Protocol
- Based on
This library is server-side only. I did not have interest in implementing client code, but feel free to do it if you are into that.
Quick start:
require_once 'vendor/autoload.php';
$server = new \Kit\Websocket\WsServer(host: '0.0.0.0', port: 1337);
$server->setConnectionHandler(
connectionHandlerInterface: new class extends \Kit\Websocket\Message\Protocols\AbstractTextMessageHandler {
private array $connections;
public function __construct()
{
$this->connections = [];
}
// Optional, as with on onDisconnect, onError
public function onOpen(\Kit\Websocket\Connection\Connection $connection): void
{
$this->connections[] = $connection;
}
public function handleTextData(string $data, \Kit\Websocket\Connection\Connection $connection): void
{
$connection->getLogger()->debug("IP" . ":" . $connection->getIp() . PHP_EOL);
$connection->getLogger()->debug("Data: " . $data . PHP_EOL);
$broadcast = array_filter($this->connections, fn($conn) => $conn !== $connection);
foreach ($broadcast as $conn) {
$conn->writeText($data);
}
}
}
);
$server->start();
Whether using this library as a standalone solution or on top of other ReactPHP libraries (e.g, HTTP Server), the important piece is the declaration of a handler to execute real-time busineess logic.
- PHP 8.3^
You can create a configuration object using the constructor of from the static method createFromArray
.
$configArray = [
'timeout' => 5, // In seconds
'maxPayloadSize' => 524288, //(0.5MiB)
'maxMessagesBuffering' => 100, // 5mb in messages
'writeMasked' => false,
'prod' => true,
'ssl' => false,
'certFile' => '',
'passphrase' => '',
'sslContextOptions' => [],
];
$config = \Kit\Websocket\Config\Config::createFromArray($configArray);
// OR
$config = new \Kit\Websocket\Config\Config(
// DEFAULTS: int $timeout = self::MESSAGE_TIMEOUT_IN_SECONDS,
// DEFAULTS: int $maxPayloadSize = self::MAX_PAYLOAD_SIZE,
// DEFAULTS: int $maxMessagesBuffering = self::MAX_MESSAGES_BUFFERING,
// DEFAULTS: bool $writeMasked = false,
// DEFAULTS: bool $prod = true,
// DEFAULTS: ?\Kit\Websocket\Config\SslConfig $sslConfig = null
);
$server = new \Kit\Websocket\WsServer(host: '0.0.0.0', port: 1337, $config);
The Connection
object has the following methods you can use:
getIp(): string
getLogger(): LoggerInterface
getSocketWriter(): MessageWriter
write(string|Frame $frame, FrameTypeEnum $frameTypeEnum): void
writeText(string|Frame $frame): void
writeBinary(string|Frame $frame): void
In new apps you often use https. So you should use wss with WebSockets to secure data exchange. Woketo
supports wss out of the box, you just need to add the related options (ssl
and certFile
).
You should instanciate woketo like this:
$server = new \Kit\Websocket\WsServer(9001, '127.0.0.1',
new Config(
...[
'ssl' => true,
'certFile' => 'path/to/certificate/cert.pem',
'sslContextOptions' => [
'verify_peer' => false,
'allow_self_signed' => true
]
]
)
);
Why is there only one cert file required while I have 2 files (cert and private key) ?
PHP uses a PEM formatted certificate that contains the certificate and the private key.
Here is a way to generate your PEM formatted certificate for a local usage:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout acme.key -out acme.crt
cat acme.key > acme.pem
cat acme.crt >> acme.pem
- Web Socket Server
- Receiving data
- Establishes handshake connections according to the Web Socket protocol
- Receives masked data payloads
- Receives TEXT and BINARY frames
- Decodes 7-bits long data payloads
- Decodes 16-bits long data payloads
- Decodes 64-bits long data payloads
- Uses permessage-deflate with zlib to handle inflate out-of-the-box.
- WSS Support (WebSocket over SSL).
- Replying (see example/vue-client/SocketChat)
- Builds data frames according to the Web Socket protocol
- Sends 7-bits long unmasked data payloads
- Sends 16-bits long unmasked data payloads
- Sends 64-bits long unmasked data payloads
- Responds deflate messages with zlib out-of-the-box.
- Receiving data
This project relies on heavy documentation, tests and other repositories. You can read more on:
- MDN
- Erick Wendel's Blog
- I could spend a lot of time explaining every single detail about this implementation, but luckly someone else has made it before I would :D
- Masking, Fragments and Stuff
- MDN