Search by

micilini / php-websockets

micilini

Native PHP WebSocket and realtime chat library built from scratch. Works standalone or with Laravel.

Package info

github.com/micilini/php-websockets

pkg:composer/micilini/php-websockets

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.2 2026-05-10 00:32 UTC

This package is auto-updated.

Last update: 2026-10-03 02:00:51 UTC


README

Native PHP WebSocket and realtime chat foundation built from scratch with PHP sockets. Works standalone with plain PHP or inside Laravel applications.

PHP 8.2+ Laravel 10/11/12/13 MIT License Tests Packagist

Pure PHP WebSockets. Realtime chat. Private rooms. Bots. Laravel-ready.

Application Images

Image 1 Image 2 Image 3 Image 4

Overview

PHPSockets started in 2016 as an educational experiment demonstrating how to build a WebSocket server directly with PHP sockets, without Node.js, without socket.io, and without a third-party realtime runtime.

The project has now been rebuilt as a modern Composer package for PHP 8.2+, with a clean architecture, WebSocket protocol primitives, a realtime server runtime, a chat kit, private conversations, private group rooms, small attachments, emoji-safe payloads, bot hooks, optional storage adapters, and Laravel integration.

The goal is not only to provide a chat example.

The goal is to provide a native PHP realtime foundation that developers can use to build:

  • realtime chat widgets;
  • support chat systems;
  • private rooms;
  • collaborative dashboards;
  • notification servers;
  • internal realtime tools;
  • chatbot-ready messaging flows;
  • Laravel-powered realtime applications.

Features

  • Native WebSocket core — handshake, frames, opcodes, close codes, ping/pong and payload validation.
  • Pure PHP socket runtime — no socket.io, Ratchet, ReactPHP, Swoole, Workerman or Node.js required.
  • Realtime server layer — connection registry, lifecycle events, message dispatching and safe closing.
  • Chat Kit — sessions, unique display names, presence, global messages, direct messages and rooms.
  • EasyChat example — simple global chat for beginners.
  • MediumChat example — callback/event-driven chat for customization.
  • PrivateChat example — global room, direct 1:1 conversations, private group rooms and unread badges.
  • Private group rooms — create a room with selected online users only.
  • Typing indicators — user feedback for active conversations.
  • Message receipts — simple sent/received/read states in examples.
  • Emoji support — safe UTF-8 text payloads with a built-in emoji picker in examples.
  • Small attachments — images, PDFs and text files up to the configured limit.
  • Downloadable file messages — delivered attachments include a download action.
  • Fragmented text frame support — large JSON text messages can be reassembled safely.
  • Storage adapters — in-memory, file JSONL messages and PDO-based SQL storage.
  • Database migrations — SQLite, MySQL and PostgreSQL schemas through the migration runner.
  • Bot hooks — register lightweight bots that can respond in global, direct and private group contexts.
  • Laravel integration — Service Provider, Facade, publishable config and Artisan commands.
  • Quality tooling — PHPUnit, PHPStan, PHP CS Fixer and GitHub Actions.
  • Legacy preserved — the original 2016 EasyChat and MediumChat implementations are kept for historical reference.

Installation

Install through Composer:

composer require micilini/php-websockets

For local development:

git clone https://github.com/micilini/php-websockets.git
cd php-websockets
composer install

Requirements

Required:

  • PHP 8.2 or higher;
  • ext-sockets;
  • ext-json;
  • Composer.

Optional:

  • ext-pdo for SQL storage adapters;
  • ext-pdo_sqlite for SQLite storage and tests;
  • Laravel 10, 11, 12 or 13 for Laravel integration.

Quick Start

Standalone PHP server

Create a simple WebSocket chat server:

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Micilini\PhpSockets\Chat\ChatServer;
use Micilini\PhpSockets\Config\ChatConfig;
use Micilini\PhpSockets\Config\ServerConfig;

$server = ChatServer::create(
    ServerConfig::new(
        host: '127.0.0.1',
        port: 8080,
        maxPayloadBytes: 4 * 1024 * 1024,
        enableDebugLogs: true,
    ),
    ChatConfig::new(
        maxAttachmentBytes: 2 * 1024 * 1024,
    ),
);

$server->run();

Run it:

php server.php

The WebSocket server will listen on:

ws://127.0.0.1:8080

Running the Examples

The example servers can be executed both from a cloned repository and from a project where the package was installed through Composer. The examples use examples/bootstrap.php to locate the correct Composer autoload file automatically.

Each example has two parts:

  1. a WebSocket server process;
  2. a browser UI served by PHP's built-in HTTP server.

EasyChat

Start the WebSocket server:

php examples/easy-chat/server.php

Open another terminal and serve the UI:

php -S 127.0.0.1:8000 -t examples/easy-chat/public

If you installed the package inside another project with Composer, run:

php vendor/micilini/php-websockets/examples/easy-chat/server.php
php -S 127.0.0.1:8000 -t vendor/micilini/php-websockets/examples/easy-chat/public

Open:

http://127.0.0.1:8000

EasyChat demonstrates:

  • global chat;
  • unique display names;
  • presence;
  • typing indicators;
  • emoji picker;
  • small attachments;
  • safe message rendering.

MediumChat

Start the WebSocket server:

php examples/medium-chat/server.php

Open another terminal and serve the UI:

php -S 127.0.0.1:8001 -t examples/medium-chat/public

If you installed the package inside another project with Composer, run:

php vendor/micilini/php-websockets/examples/medium-chat/server.php
php -S 127.0.0.1:8001 -t vendor/micilini/php-websockets/examples/medium-chat/public

Open:

http://127.0.0.1:8001

MediumChat demonstrates everything from EasyChat plus:

  • server-side callbacks;
  • low-level socket events;
  • chat lifecycle logs;
  • event panel in the browser;
  • extensibility points for custom behavior.

PrivateChat

Start the WebSocket server:

php examples/private-chat/server.php

Open another terminal and serve the UI:

php -S 127.0.0.1:8002 -t examples/private-chat/public

If you installed the package inside another project with Composer, run:

php vendor/micilini/php-websockets/examples/private-chat/server.php
php -S 127.0.0.1:8002 -t vendor/micilini/php-websockets/examples/private-chat/public

Open:

http://127.0.0.1:8002

PrivateChat demonstrates:

  • global room;
  • direct 1:1 conversations;
  • private group rooms;
  • selected participants;
  • unread badges;
  • attachments;
  • emoji picker;
  • message receipts;
  • bot commands.

Try the bot commands:

/help
/echo Hello PHPSockets

Example Matrix

Example Global Chat Direct 1:1 Private Groups Attachments Bots Best For
EasyChat ✅ ❌ ❌ ✅ ❌ First contact and learning
MediumChat ✅ ❌ ❌ ✅ ❌ Events and callbacks
PrivateChat ✅ ✅ ✅ ✅ ✅ Advanced realtime chat

Laravel Integration

PHPSockets can be used inside Laravel applications through Composer package discovery.

Install the package:

composer require micilini/php-websockets

Publish the configuration:

php artisan vendor:publish --tag=phpsockets-config

Check the package status:

php artisan phpsockets:status

Start the WebSocket chat server from Laravel:

php artisan phpsockets:serve

Run SQL migrations when using a persistent storage driver:

php artisan phpsockets:migrate --driver=sqlite

The package registers:

  • Micilini\PhpSockets\Laravel\PhpSocketsServiceProvider
  • Micilini\PhpSockets\Laravel\PhpSocketsFacade
  • phpsockets:serve
  • phpsockets:migrate
  • phpsockets:status

Example usage:

use Micilini\PhpSockets\Laravel\PhpSocketsFacade as PhpSockets;

PhpSockets::bots();

Laravel is optional. The native PHP core continues to work standalone.

Running PHPSockets in a Laravel Project

A Laravel app usually has two running processes:

Laravel HTTP server
  http://127.0.0.1:8000

PHPSockets WebSocket server
  ws://127.0.0.1:8080

Run the WebSocket server:

php artisan phpsockets:serve

In another terminal:

php artisan serve

Then your Laravel pages can connect to:

ws://127.0.0.1:8080

Hosting the Examples Through Laravel Routes

For a Laravel demo app, you can keep PHPSockets as the engine and let Laravel serve the example screens.

Recommended local package structure while developing PHPSockets inside a Laravel app:

laravel-app/
  packages/
    micilini/
      php-websockets/
  public/
    phpsockets/
      easy/
      medium/
      private/
  routes/
    web.php

After installing from Packagist, the package will live under:

laravel-app/
  vendor/
    micilini/
      php-websockets/

The idea is:

http://127.0.0.1:8000/
  Dashboard with Easy, Medium and Private options

http://127.0.0.1:8000/easy
  EasyChat hosted by Laravel

http://127.0.0.1:8000/medium
  MediumChat hosted by Laravel

http://127.0.0.1:8000/private
  PrivateChat hosted by Laravel

The WebSocket server still runs in a separate process, which is the correct model for realtime applications.

Configuration

After publishing the Laravel config, config/phpsockets.php contains:

return [
    'server' => [
        'host' => env('PHPSOCKETS_HOST', '127.0.0.1'),
        'port' => (int) env('PHPSOCKETS_PORT', 8080),
        'max_payload_bytes' => (int) env('PHPSOCKETS_MAX_PAYLOAD_BYTES', 4 * 1024 * 1024),
        'tick_microseconds' => (int) env('PHPSOCKETS_TICK_MICROSECONDS', 10000),
        'connection_limit' => (int) env('PHPSOCKETS_CONNECTION_LIMIT', 100),
        'debug' => (bool) env('PHPSOCKETS_DEBUG', false),
    ],

    'chat' => [
        'max_display_name_length' => (int) env('PHPSOCKETS_MAX_DISPLAY_NAME_LENGTH', 40),
        'max_room_name_length' => (int) env('PHPSOCKETS_MAX_ROOM_NAME_LENGTH', 80),
        'max_private_group_members' => (int) env('PHPSOCKETS_MAX_PRIVATE_GROUP_MEMBERS', 20),
        'history_limit' => (int) env('PHPSOCKETS_HISTORY_LIMIT', 50),
        'max_attachment_bytes' => (int) env('PHPSOCKETS_MAX_ATTACHMENT_BYTES', 2 * 1024 * 1024),
    ],

    'storage' => [
        'driver' => env('PHPSOCKETS_STORAGE', 'memory'),
        'database' => env('PHPSOCKETS_DATABASE'),
        'dsn' => env('PHPSOCKETS_DSN'),
        'username' => env('PHPSOCKETS_DB_USERNAME'),
        'password' => env('PHPSOCKETS_DB_PASSWORD'),
    ],
];

Example .env:

PHPSOCKETS_HOST=127.0.0.1
PHPSOCKETS_PORT=8080
PHPSOCKETS_MAX_PAYLOAD_BYTES=4194304
PHPSOCKETS_MAX_ATTACHMENT_BYTES=2097152
PHPSOCKETS_ATTACHMENT_DIR=.phpsockets/attachments
PHPSOCKETS_STORAGE=memory
PHPSOCKETS_DEBUG=true

API Overview

Main server

use Micilini\PhpSockets\Chat\ChatServer;
use Micilini\PhpSockets\Config\ChatConfig;
use Micilini\PhpSockets\Config\ServerConfig;

$server = ChatServer::create(
    ServerConfig::new(host: '127.0.0.1', port: 8080),
    ChatConfig::new(),
);

$server->run();

Server events

$server->on('user.joined', function (array $event): void {
    // User joined the chat.
});

$server->on('user.left', function (array $event): void {
    // User left the chat.
});

$server->on('message.received', function (array $event): void {
    // A chat message was received.
});

$server->on('room.created', function (array $event): void {
    // A private room was created.
});

$server->on('bot.responded', function (array $event): void {
    // A bot generated a response.
});

Available high-level events

  • user.joined
  • user.left
  • message.received
  • message.sent
  • room.created
  • bot.responded

Available low-level socket events

  • open
  • close
  • error

Storage

PHPSockets uses in-memory storage by default. This is perfect for examples, demos and development.

Available storage options:

memory
file JSONL messages
pdo sqlite
pdo mysql
pdo pgsql

SQLite migration example

use Micilini\PhpSockets\Database\MigrationRunner;
use Micilini\PhpSockets\Storage\Pdo\PdoConnectionFactory;

$pdo = PdoConnectionFactory::sqlite(__DIR__ . '/storage/phpsockets.sqlite');

(new MigrationRunner($pdo))->run('sqlite');

Laravel migration command

php artisan phpsockets:migrate --driver=sqlite --database=database/phpsockets.sqlite

Attachments

The examples support small file messages.

Attachment runtime directory

By default, PHPSockets stores temporary example attachments in a project-local directory:

.phpsockets/attachments

You can override this location with:

PHPSOCKETS_ATTACHMENT_DIR=/absolute/path/to/attachments

This is especially useful for production deployments, Laravel apps, containers and Windows environments.

Supported MIME types:

image/png
image/jpeg
image/gif
application/pdf
text/plain

Default max file size:

2 MB

Attachment behavior:

  • selecting a file does not send it immediately;
  • the selected file appears as a pending attachment;
  • the user can add a text caption;
  • the file is sent only when the user clicks Send;
  • delivered files include a download button;
  • unsafe MIME types are rejected;
  • files above the configured limit are rejected.

The initial transport uses JSON text-frame envelopes with base64 payloads over WebSocket. Larger uploads should use a future HTTP upload flow with WebSocket metadata messages.

Bot Hooks

PHPSockets includes a lightweight bot layer.

Bots can listen to text messages and return a response in the same conversation context.

Supported contexts:

  • Global Room;
  • Direct conversations;
  • Private group rooms.

Example:

use Micilini\PhpSockets\Contracts\BotInterface;
use Micilini\PhpSockets\Chat\Bot\BotContext;
use Micilini\PhpSockets\Chat\Bot\BotResponse;

final class EchoBot implements BotInterface
{
    public function name(): string
    {
        return 'EchoBot';
    }

    public function handle(BotContext $context): ?BotResponse
    {
        $text = trim($context->text());

        if (!str_starts_with($text, '/echo ')) {
            return null;
        }

        return BotResponse::text(substr($text, 6));
    }
}

Register the bot:

$server->bots()->register(new EchoBot());

Bots are intentionally simple in v1. They do not require any external AI API.

Protocol Notes

PHPSockets uses JSON text frames for chat messages.

Example client message:

{
  "type": "message.global",
  "payload": {
    "text": "Hello world 🚀"
  }
}

Example server message:

{
  "type": "message.received",
  "payload": {
    "message": {
      "id": "msg_01",
      "roomId": "global",
      "fromUserId": "usr_01",
      "kind": "text",
      "body": "Hello world 🚀"
    }
  }
}

Binary WebSocket frames are not accepted by the chat core in this version. File messages are transported as validated JSON text-frame envelopes.

Security Model

PHPSockets includes a practical safety baseline for examples and local-first applications:

  • messages are rendered safely in the examples;
  • user-provided text should not be rendered with unsafe innerHTML;
  • display names are normalized and must be unique while online;
  • payload size is limited;
  • attachment size is limited;
  • attachment MIME type is validated;
  • users cannot send messages to rooms they do not belong to;
  • private group messages are delivered only to room members;
  • bot messages do not trigger infinite bot loops;
  • malformed frames can be rejected with WebSocket close codes.

This package is a realtime foundation. Production applications should still add authentication, authorization, HTTPS/WSS, monitoring, rate limiting, persistent storage and infrastructure hardening.

Project Structure

src/
  WebSocket.php
  Config/
    ServerConfig.php
    ChatConfig.php
  Protocol/
    Handshake.php
    Frame.php
    FrameCodec.php
    Opcode.php
    CloseCode.php
  Server/
    WebSocketServer.php
    ServerRuntime.php
    SocketServer.php
    Loop.php
  Connection/
    Connection.php
    ConnectionRegistry.php
  Events/
    CallbackEventDispatcher.php
    EventDispatcher.php
  Contracts/
    BotInterface.php
    SessionStoreInterface.php
    MessageStoreInterface.php
    RoomStoreInterface.php
    AttachmentStoreInterface.php
  Chat/
    ChatServer.php
    ChatKernel.php
    ChatMessage.php
    Room.php
    RoomManager.php
    PresenceManager.php
    Bot/
      BotManager.php
      BotContext.php
      BotResponse.php
  Storage/
    InMemory/
    File/
    Pdo/
  Database/
    MigrationRunner.php
    Schema/
  Laravel/
    PhpSocketsServiceProvider.php
    PhpSocketsFacade.php
    PhpSocketsManager.php
    Commands/
      ServeCommand.php
      MigrateCommand.php
      StatusCommand.php

examples/
  easy-chat/
  medium-chat/
  private-chat/

legacy/
  EasyChat/
  MediumChat/

Testing and Quality

Install development dependencies:

composer install

Validate the package:

composer validate --strict

Run tests:

composer test

Run static analysis:

composer analyse

Check code style:

composer cs:check

Fix code style:

composer cs:fix

Run the full quality pipeline:

composer quality

Legacy Code

The original 2016 implementation is preserved under:

legacy/EasyChat
legacy/MediumChat
legacy/README-2016.md
legacy/NOTES.md

The legacy code is kept for historical and educational purposes only. The modern Composer package lives in src/ and does not depend on the old structure.

Roadmap

Current v1 focus:

  • native WebSocket core;
  • realtime server runtime;
  • chat kit;
  • EasyChat, MediumChat and PrivateChat;
  • private groups;
  • storage adapters;
  • small attachments;
  • bot hooks;
  • Laravel integration;
  • documentation and Packagist release.

Future ideas:

  • standalone CLI binary;
  • WSS/TLS helper documentation;
  • Redis adapter;
  • multi-process scaling;
  • chunked binary file transfer;
  • HTTP upload + WebSocket metadata;
  • JWT/auth adapters;
  • Laravel dashboard;
  • official Docker image;
  • benchmarks.

Packagist Release Checklist

Before tagging a stable release:

composer validate --strict
composer quality

Then:

git tag v1.0.0
git push origin v1.0.0

Submit the repository to Packagist:

https://packagist.org/packages/submit

After Packagist detects the package, users can install it with:

composer require micilini/php-websockets

Repository:

https://github.com/micilini/php-websockets

License

PHPSockets is open-sourced software licensed under the MIT license.

Credits

Created and maintained by Micilini.

Originally created in 2016 and rebuilt as a modern native PHP realtime foundation in 2026.