{"id":19112901,"url":"https://github.com/chipslays/porter","last_synced_at":"2025-04-30T22:14:43.660Z","repository":{"id":62501842,"uuid":"472767806","full_name":"chipslays/porter","owner":"chipslays","description":"🤵‍ An easy way to build real-time applications with WebSockets. Supports channels, validation and other things. SocketIO alternative for PHP.","archived":false,"fork":false,"pushed_at":"2025-01-13T19:41:11.000Z","size":723,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-30T22:14:37.112Z","etag":null,"topics":["php","php-websocket","socket","websocket-client","websocket-library","websocket-server"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chipslays.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-03-22T12:57:00.000Z","updated_at":"2025-01-13T19:41:09.000Z","dependencies_parsed_at":"2025-04-19T00:33:36.007Z","dependency_job_id":"387dc6b5-fed2-43bb-b77b-c5bf481e28fa","html_url":"https://github.com/chipslays/porter","commit_stats":{"total_commits":372,"total_committers":3,"mean_commits":124.0,"dds":"0.037634408602150504","last_synced_commit":"b949673f269f887440eaca6b48bfc13f9e444399"},"previous_names":[],"tags_count":110,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chipslays%2Fporter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chipslays%2Fporter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chipslays%2Fporter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chipslays%2Fporter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chipslays","download_url":"https://codeload.github.com/chipslays/porter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251789618,"owners_count":21644086,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","php-websocket","socket","websocket-client","websocket-library","websocket-server"],"created_at":"2024-11-09T04:34:28.664Z","updated_at":"2025-04-30T22:14:43.640Z","avatar_url":"https://github.com/chipslays.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Porter 🤵‍\n![](https://img.shields.io/github/license/chipslays/porter?color=blue)\n![](https://img.shields.io/packagist/dt/chipslays/porter)\n\nA simple PHP 8 websocket server and client wrapper over Workerman with events, channels and other stuff, can say this is a Socket IO alternative for PHP.\n\n![](/.github/images/porter.png)\n\n\u003e **Note**\n\u003e\n\u003e Latest version 1.2 is production ready, maintenance only small features, fix bugs  and has **no breaking changes** updates.\n\n# 🧰 Installation\n\n1. Install Porter via Composer:\n\n```bash\ncomposer require chipslays/porter\n```\n\n2. Put javascript code in views:\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/chipslays/porter@latest/dist/porter.min.js\"\u003e\u003c/script\u003e\n```\n\n3. All done.\n\n\u003e Laravel integration can be found [here](/examples/laravel/README.md).\n\n# 👨‍💻 Usage\n\n### Server (PHP)\n\nSimplest ping-pong server.\n\n```php\nuse Porter\\Events\\Event;\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nserver()-\u003ecreate('0.0.0.0:3737');\n\nserver()-\u003eon('ping', function (Event $event) {\n    $event-\u003ereply('pong');\n});\n\nserver()-\u003estart();\n```\n\nRun server.\n\n```bash\nphp server.php start\n```\n\nOr run server in background as daemon process.\n\n```bash\nphp server.php start -d\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eList of all available commands\u003c/summary\u003e\n\n`php server.php start`\n\n`php server.php start -d`\n\n`php server.php status`\n\n`php server.php status -d`\n\n`php server.php connections`\n\n`php server.php stop`\n\n`php server.php stop -g`\n\n`php server.php restart`\n\n`php server.php reload`\n\n`php server.php reload -g`\n\u003c/details\u003e\n\n### Client (Javascript)\n\nSend `ping` event on established connection.\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/chipslays/porter@latest/dist/porter.min.js\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\n    const client = new Porter(`ws://${location.hostname}:3737`);\n\n    client.connected = () =\u003e {\n        client.send('ping');\n    }\n\n    client.on('pong', payload =\u003e {\n        console.log(payload);\n    });\n\n    client.listen();\n\u003c/script\u003e\n```\n\n# 💡 Examples\n\nExamples can be found [here](/examples).\n\n# 📚 Documentation\n\n\u003e **NOTE:** The documentation may not contain the latest updates or may be out of date in places. See examples, code and comments on methods. The code is well documented.\n\n## Basics\n\n### Local development\n\n```php\nuse Workerman\\Worker;\n\n$worker = new Worker('websocket://0.0.0.0:3737');\n```\n\n### On server with SSL\n\n```php\nuse Workerman\\Worker;\n\n$context = [\n    // More see http://php.net/manual/en/context.ssl.php\n    'ssl' =\u003e [\n        'local_cert' =\u003e '/path/to/cert.pem',\n        'local_pk' =\u003e '/path/to/privkey.pem',\n        'verify_peer' =\u003e false,\n        // 'allow_self_signed' =\u003e true,\n    ],\n];\n$worker = new Worker('websocket://0.0.0.0:3737', $context);\n$worker-\u003etransport = 'ssl';\n```\n\n## 🔹 `Server`\n\nCan be used anywhere as function `server()` or `Server::getInstance()`.\n\n```php\nuse Porter\\Server;\n\n$server = Server::getInstance();\n$server-\u003eon(...);\n```\n\n```php\nserver()-\u003eon(...);\n```\n\n#### `boot(Worker $worker): self`\n\nBooting websocket server. It method init all needle classes inside.\n\n\u003e Use this method instead of constructor.\n\n```php\n$server = Server::getInstance();\n$server-\u003eboot($worker);\n\n// by helper function\nserver()-\u003eboot($worker);\n```\n\n#### `setWorker(Worker $worker): void`\n\nSet worker instance.\n\n```php\nuse Workerman\\Worker;\n\n$worker = new Worker('websocket://0.0.0.0:3737');\nserver()-\u003eboot($worker); // booting server\n\n$worker = new Worker('websocket://0.0.0.0:3737');\n$worker-\u003e... // configure new worker\n\n// change worker in already booted server\nserver()-\u003esetWorker($worker);\n```\n\n#### `setWorker(Worker $worker): void`\n\nSet worker instance.\n\n```php\nuse Workerman\\Worker;\n\n$worker = new Worker('websocket://0.0.0.0:3737');\nserver()-\u003esetWorker($worker);\n```\n\n#### `getWorker(): Worker`\n\nGet worker instance.\n\n```php\nserver()-\u003egetWorker();\n```\n\n#### `addEvent(AbstractEvent|string $event): self`\n\nAdd event class handler.\n\n```php\nuse Porter\\Server;\nuse Porter\\Payload;\nuse Porter\\Connection;\nuse Porter\\Events\\AbstractEvent;\n\nclass Ping extends AbstractEvent\n{\n    public static string $id = 'ping';\n\n    public function handle(Connection $connection, Payload $payload, Server $server): void\n    {\n        $this-\u003ereply('pong');\n    }\n}\n\nserver()-\u003eaddEvent(Ping::class);\n```\n\n#### `autoloadEvents(string $path, string|array $masks = ['*.php', '**/*.php']): void`\n\nAutoload all events inside passed path.\n\n\u003e Note: Use it instead manual add events by `addEvent` method.\n\n```php\nserver()-\u003eautoloadEvents(__DIR__ . '/Events');\n```\n\n#### `on(string $type, callable $handler): void`\n\n\n\n\u003e **Note** \n\u003e\n\u003e `Event $event` class extends and have all methods \u0026 properties of `AbstractEvent`.\n\n```php\n$server-\u003eon('ping', function (Event $event) {\n    $event-\u003ereply('pong');\n});\n```\n\n#### `start(): void`\n\nStart server.\n\n```php\nserver()-\u003estart();\n```\n\n#### `onConnected(callable $handler): void`\n\nEmitted when a socket connection is successfully established.\n\n\u003e In this method available vars: `$_GET`, `$_COOKIE`, `$_SERVER`.\n\n```php\nuse Porter\\Terminal;\nuse Porter\\Connection;\n\nserver()-\u003eonConnected(function (Connection $connection, string $header) {\n    Terminal::print('{text:darkGreen}Connected: ' . $connection-\u003egetRemoteAddress());\n\n    // Here also available vars: $_GET, $_COOKIE, $_SERVER.\n    Terminal::print(\"Query from client: {text:darkYellow}foo={$_GET['foo']}\");\n});\n```\n\n#### `onDisconnected(callable $handler): void`\n\nEmitted when the other end of the socket sends a FIN packet.\n\n\u003e **NOTICE:** On disconnect client connection will leave of all the channels where he was.\n\n```php\nuse Porter\\Terminal;\nuse Porter\\Connection;\n\nserver()-\u003eonDisconnected(function (Connection $connection) {\n    Terminal::print('{text:darkGreen}Connected: ' . $connection-\u003egetRemoteAddress());\n});\n```\n\n#### `onError(callable $handler): void`\n\nEmitted when an error occurs with connection.\n\n```php\nuse Porter\\Terminal;\nuse Porter\\Connection;\n\nserver()-\u003eonError(function (Connection $connection, $code, $message) {\n    Terminal::print(\"{bg:red}{text:white}Error {$code} {$message}\");\n});\n```\n\n#### `onStart(callable $handler): void`\n\nEmitted when worker processes start.\n\n```php\nuse Porter\\Terminal;\nuse Workerman\\Worker;\n\nserver()-\u003eonStart(function (Worker $worker) {\n    //\n});\n```\n\n#### `onStop(callable $handler): void`\n\nEmitted when worker processes stoped.\n\n```php\nuse Porter\\Terminal;\nuse Workerman\\Worker;\n\nserver()-\u003eonStop(function (Worker $worker) {\n    //\n});\n```\n\n#### `onReload(callable $handler): void`\n\nEmitted when worker processes get reload signal.\n\n```php\nuse Porter\\Terminal;\nuse Workerman\\Worker;\n\nserver()-\u003eonReload(function (Worker $worker) {\n    //\n});\n```\n\n#### `onRaw(callable $handler): void`\n\nHandle non event messages (raw data).\n\n```php\nserver()-\u003eonRaw(function (string $payload, Connection $connection) {\n    if ($payload == 'ping') {\n        $connection-\u003esend('pong');\n    }\n});\n```\n\n#### `to(TcpConnection|Connection|array $connection, string $event, array $data = []): self`\n\nSend event to connection.\n\n```php\nserver()-\u003eto($connection, 'ping');\n```\n\n#### `broadcast(string $event, array $data = [], array $excepts = []): void`\n\nSend event to all connections.\n\nYes, to **all connections** on server.\n\n```php\nserver()-\u003ebroadcast('chat message', [\n    'nickname' =\u003e 'John Doe',\n    'message' =\u003e 'Hello World!',\n]);\n```\n\n#### `storage(): Storage`\n\nGetter for Storage class.\n\n```php\nserver()-\u003estorage();\n\nserver()-\u003estorage()-\u003eput('foo', 'bar');\n\n$storage = server()-\u003estorage();\n$storage-\u003eget('foo');\n\n// can also be a get as propperty\nserver()-\u003estorage()-\u003eput('foo', 'bar');\n$storage = server()-\u003estorage;\n```\n\n#### `channels(): Channels`\n\nGetter for Channels class.\n\n```php\nserver()-\u003echannels();\n\nserver()-\u003echannels()-\u003ecreate('secret channel');\n\n$channels = server()-\u003echannels();\n$channels-\u003eget('secret channel');\n```\n\n#### `connection(int $connectionId): ?Connection`\n\nGet connection instance by id.\n\n```php\n$connection = server()-\u003econnection(1);\nserver()-\u003eto($connection, 'welcome message', [\n    'text' =\u003e 'Hello world!'\n]);\n\n// also can get like\n$connection = server()-\u003egetWorker()-\u003econnections[1337] ?? null;\n```\n\n#### `connections(): Collection[]`\n\nGet collection of all connections on server.\n\n```php\n$connections = server()-\u003econnections();\nserver()-\u003ebroadcast('update users count', ['count' =\u003e $connections-\u003ecount()]);\n\n// also can get like\n$connections = server()-\u003egetWorker()-\u003econnections;\n```\n\n#### `validator(): Validator`\n\nCreate validator instance.\n\nSee [documenation \u0026 examples](https://respect-validation.readthedocs.io/en/latest) how to use.\n\n```php\n$v = server()-\u003evalidator();\n\nif ($v-\u003eemail()-\u003evalidate('john.doe@example.com')) {\n    //\n}\n\n// available as helper\nif (validator()-\u003econtains('example.com')-\u003evalidate('john.doe@example.com')) {\n    //\n}\n```\n\n## 🔹 `Channels`\n\nThis is a convenient division of connected connections into channels.\n\nOne connection can consist of an unlimited number of channels.\n\nChannels also support broadcasting and their own storage.\n\nChannel can be access like:\n\n```php\n// by method\nserver()-\u003echannels();\n\n// by property\nserver()-\u003echannels;\n```\n\n#### `create(string $id, array $data = []): Channel`\n\nCreate new channel.\n\n```php\n$channel = server()-\u003echannels()-\u003ecreate('secret channel', [\n    'foo' =\u003e 'bar',\n]);\n\n$channel-\u003ejoin($connection)-\u003ebroadcast('welcome message', [\n    'foo' =\u003e $channel-\u003edata-\u003eget('foo'),\n]);\n```\n\n#### `get(string $id): ?Channel`\n\nGet a channel.\n\n\u003e Returns `NULL` if channel not exists.\n\n```php\n$channel = server()-\u003echannels()-\u003eget('secret channel');\n```\n\n#### `all(): Channel[]`\n\nGet array of channels (`Channel` instances).\n\n```php\nforeach (server()-\u003echannels()-\u003eall() as $id =\u003e $channel) {\n    echo $channel-\u003econnections()-\u003ecount() . ' connection(s) in channel: ' . $id . PHP_EOL;\n}\n```\n\n#### `count(): int`\n\nGet count of channels.\n\n```php\n$count = server()-\u003echannels()-\u003ecount();\n\necho \"Total channels: {$count}\";\n```\n\n#### `delete(string $id): void`\n\nDelete channel.\n\n```php\nserver()-\u003echannels()-\u003edelete('secret channel');\n```\n\n#### `exists(string $id): bool`\n\nChecks if given channel id exists already.\n\n```php\n$channelId = 'secret channel';\nif (!server()-\u003echannels()-\u003eexists($channelId)) {\n    server()-\u003echannels()-\u003ecreate($channelId);\n}\n```\n\n#### `join(string $id, Connection|Connection[]|int[] $connections): Channel`\n\nJoin or create and join to channel.\n\n```php\nserver()-\u003echannels()-\u003ejoin($connection);\nserver()-\u003echannels()-\u003ejoin([$connection1, $connection2, $connection3, ...]);\n```\n\n## 🔹 `Channel`\n\n#### `join(TcpConnection|Connection|array $connections): self`\n\nJoin given connections to channel.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n$channel-\u003ejoin($connection);\n$channel-\u003ejoin([$connection1, $connection2, $connection3, ...]);\n```\n\n#### `leave(TcpConnection|Connection $connection): self`\n\nRemove given connection from channel.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n$channel-\u003eleave($connection);\n```\n\n#### `exists(TcpConnection|Connection|int $connection): bool`\n\nChecks if given connection exists in channel.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n$channel-\u003eexists($connection);\n```\n\n#### `connections(): Connections`\n\nA array of connections in this channel. Key is a `id` of connection, and value is a instance of connection `Connection`.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n\nforeach($channel-\u003econnections()-\u003eall()) as $connection) {\n    $connection-\u003elastMessageAt = time();\n}\n```\n\n```php\n$channel = server()-\u003echannel('secret channel');\n\n$connection = $channel-\u003econnections()-\u003eget([1337]); // get connection with 1337 id\n```\n\n#### `broadcast(string $event, array $data = [], array $excepts = []): void`\n\nSend an event to all connection on this channel.\n\n\u003e `TcpConnection[]|Connection[]|int[] $excepts` Connection instance or connection id.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n$channel-\u003ebroadcast('welcome message', [\n    'text' =\u003e 'Hello world',\n]);\n```\n\nFor example, you need to send to all participants in the room except yourself, or other connections.\n\n```php\n$channel-\u003ebroadcast('welcome message', [\n    'text' =\u003e 'Hello world',\n], [$connection]);\n\n$channel-\u003ebroadcast('welcome message', [\n    'text' =\u003e 'Hello world',\n], [$connection1, $connection2, ...]);\n```\n\n#### `destroy(): void`\n\nDelete this channel from channels.\n\n```php\n$channel = server()-\u003echannel('secret channel');\n$channel-\u003edestroy();\n\n// now if use $channel, you get an error\n$channel-\u003edata-\u003eget('foo');\n```\n\n### Lifehack for `Channel`\n\nYou can add channel  to current user as property to `$connection` instance and get it anywhere.\n\n```php\n$channel = channel('secret channel');\n$connection-\u003echannel = \u0026$channel;\n```\n\n\n## Properties\n\n#### `$channel-\u003edata`\n\nData is a simple implement of box for storage your data.\n\nData is a object of powerful [chipslays/collection](https://github.com/chipslays/collection).\n\nSee [documentation](https://github.com/chipslays/collection) for more information how to manipulate this data.\n\n\u003e **NOTICE:** All this data will be deleted when the server is restarted.\n\nTwo of simple-short examples:\n\n```php\n$channel-\u003edata-\u003eset('foo');\n$channel-\u003edata-\u003eget('foo', 'default value');\n$channel-\u003edata-\u003ehas('foo', 'default value');\n\n$channel-\u003edata['foo'];\n$channel-\u003edata['foo'] ?? 'default value';\nisset($channel-\u003edata['foo']);\n\n// see more examples here: https://github.com/chipslays/collection\n```\n\n\n## 🔹 `Payload`\n\nThe payload is the object that came from the client.\n\n#### `payload(string $key, mixed $default = null): mixed`\n\nGet value from data.\n\n```php\n$payload-\u003eget('foo', 'default value');\n\n// can also use like:\n$payload-\u003edata-\u003eget('foo', 'default value');\n$payload-\u003edata['foo'] ?? 'default value';\n```\n\n#### `is(string|array $rule, string $key): bool`\n\nValidate payload data.\n\nSee [documenation \u0026 examples](https://respect-validation.readthedocs.io/en/latest) how to use.\n\n```php\n$payload-\u003eis('StringType', 'username'); // return true if username is string\n$payload-\u003eis(['contains', 'john'], 'username'); // return true if $payload-\u003edata['username'] contains 'john'\n```\n\n## Properties\n\n#### `$payload-\u003etype`\n\nIs a id of event, for example, `welcome message`.\n\n```php\n$payload-\u003etype; // string\n```\n\n#### `$payload-\u003edata`\n\nAn object of values passed from the client.\n\nObject of [chipslays/collection](https://github.com/chipslays/collection).\n\nSee [documentation](https://github.com/chipslays/collection) for more information how to manipulate this data.\n\n```php\n$payload-\u003edata; // Collection\n\n$payload-\u003edata-\u003eset('foo');\n$payload-\u003edata-\u003eget('foo', 'default value');\n$payload-\u003edata-\u003ehas('foo', 'default value');\n\n$payload-\u003edata['foo'];\n$payload-\u003edata['foo'] ?? 'default value';\nisset($payload-\u003edata['foo']);\n\n// see more examples here: https://github.com/chipslays/collection\n```\n\n#### `$payload-\u003erules` [protected]\n\nAuto validate payload data on incoming event.\n\nAvailable only in events as `class`.\n\n```php\nuse Porter\\Server;\nuse Porter\\Payload;\nuse Porter\\Connection;\nuse Porter\\Events\\AbstractEvent;\n\nreturn new class extends AbstractEvent\n{\n    public static string $type = 'hello to';\n\n    protected array $rules = [\n        'username' =\u003e ['stringType', ['length', [3, 18]]],\n    ];\n\n    public function handle(Connection $connection, Payload $payload, Server $server): void\n    {\n        if (!$this-\u003evalidate()) {\n            $this-\u003ereply('bad request', ['errors' =\u003e $this-\u003eerrors]);\n            return;\n        }\n\n        $username = $this-\u003epayload-\u003edata['username'];\n        $this-\u003ereply(data: ['message' =\u003e \"Hello, {$username}!\"]);\n    }\n};\n```\n\n## 🔹 `Events`\n\nEvents can be as a separate class or as an anonymous function.\n\n### Event class\n\nBasic ping-pong example:\n\n```php\nuse Porter\\Server;\nuse Porter\\Payload;\nuse Porter\\Events\\AbstractEvent;\nuse Porter\\Connection;\n\nclass Ping extends AbstractEvent\n{\n    public static string $type = 'ping';\n\n    public function handle(Connection $connection, Payload $payload, Server $server): void\n    {\n        $this-\u003ereply('pong');\n    }\n}\n\n// and next you need add (register) this class to events:\nserver()-\u003eaddEvent(Ping::class);\n```\n\n\u003e **NOTICE:** The event class must have a `handle()` method.\n\u003e\n\u003e This method handles the event. You can also create other methods.\n\n#### `AbstractEvent`\n\n#### Properties\n\nEach child class get following properties:\n\n* `Connection $connection` - from whom the event came;\n* `Payload $payload` - contain data from client;\n* `Server $server` - server instance;\n* `Collection $data` - short cut for payload data (as \u0026link).;\n\n#### Magic properties \u0026 methods.\n\nIf client pass in data `channel_i_d` with channel id or `target_id` with id of connection, we got a magic properties and methods.\n\n```php\n// this is a object of Channel, getted by `channel_id` from client.\n$this-\u003echannel;\n$this-\u003echannel();\n\n$this-\u003echannel()-\u003ebroadcast('new message', [\n    'text' =\u003e $this-\u003epayload-\u003eget('text'),\n    'from' =\u003e $this-\u003econnection-\u003enickname,\n]);\n```\n\n```php\n// this is a object of Channel, getted by `target_id` from client.\n$this-\u003etarget;\n$this-\u003etarget();\n\n$this-\u003eto($this-\u003etarget, 'new message', [\n    'text' =\u003e $this-\u003epayload-\u003eget('text'),\n    'from' =\u003e $this-\u003econnection-\u003enickname,\n]);\n```\n\n#### Methods\n\n##### `to(TcpConnection|Connection|array $connection, string $event, array $data = []): self`\n\nSend event to connection.\n\n```php\n$this-\u003eto($connection, 'ping');\n```\n\n##### `reply(string $event, array $data = []): ?bool`\n\nReply event to incoming connection.\n\n```php\n$this-\u003ereply('ping');\n\n// analog for:\n$this-\u003eto($this-\u003econnection, 'ping');\n```\n\nTo reply with the current `type`, pass only the `$data` parameter.\n\n**On front-end:**\n```javascript\nclient.send('hello to', {username: 'John Doe'}, payload =\u003e {\n    console.log(payload.data.message); // Hello, John Doe!\n});\n```\n\n**On back-end:**\n```php\n$username = $this-\u003epayload-\u003edata['username'];\n$this-\u003ereply(data: ['message' =\u003e \"Hello, {$username}!\"]);\n```\n\n##### `raw(string $string): bool|null`\n\nSend raw data to connection. Not a event object.\n\n```php\n$this-\u003eraw('ping');\n\n// now client will receive just a 'ping', not event object.\n```\n\n##### `broadcast(string $event, array $data = [], TcpConnection|Connection|array $excepts = []): void`\n\nSend event to all connections.\n\nYes, to **all connections** on server.\n\n```php\n$this-\u003ebroadcast('chat message', [\n    'nickname' =\u003e 'John Doe',\n    'message' =\u003e 'Hello World!',\n]);\n```\n\nSend event to all except for the connection from which the event came.\n\n```php\n$this-\u003ebroadcast('user join', [\n    'text' =\u003e 'New user joined to chat.',\n], [$this-\u003econnection]);\n```\n\n#### `validate(): bool`\n\nValidate payload data.\n\nPass custom rules. Default use $rules class attribute.\n\nReturns `false` if has errors.\n\n```php\nif (!$this-\u003evalidate()) {\n    return $this-\u003ereply(/** ... */);\n}\n```\n\n#### `hasErrors(): bool`\n\nReturns `true` if has errors on validate payload data.\n\n```php\nif ($this-\u003ehasErrors()) {\n    return $this-\u003ereply('bad request', ['errors' =\u003e $this-\u003eerrors]);\n}\n```\n\n```textplain\n// $this-\u003eerrors contains:\n\n^ array:1 [\n  \"username\" =\u003e array:1 [\n    \"length\" =\u003e \"username failed validation: length\"\n  ]\n]\n```\n\n#### `payload(string $key, mixed $default = null): mixed`\n\nYet another short cut for payload data.\n\n```php\npublic function handle(Connection $connection, Payload $payload, Server $server)\n{\n    $this-\u003eget('nickname');\n\n    // as property\n    $this-\u003edata['nickname'];\n    $this-\u003edata-\u003eget('nickname');\n\n    // form payload instance\n    $payload-\u003edata['nickname'];\n    $payload-\u003edata-\u003eget('nickname');\n\n    $this-\u003epayload-\u003edata['nickname'];\n    $this-\u003epayload-\u003edata-\u003eget('nickname');\n}\n```\n\n\n\n\n#### `Anonymous function`\n\nIn anonymous function instead of `$this`, use `$event`.\n\n```php\nuse Porter\\Events\\Event;\n\nserver()-\u003eon('new message', function (Event $event) {\n    // $event has all the same property \u0026\u0026 methods as in the example above\n\n    $event-\u003eto($event-\u003etarget, 'new message', [\n        'text' =\u003e $this-\u003epayload-\u003eget('text'),\n        'from' =\u003e $this-\u003econnection-\u003enickname,\n    ]);\n\n    $event-\u003echannel()-\u003ebroadcast('new message', [\n        'text' =\u003e $this-\u003epayload-\u003eget('text'),\n        'from' =\u003e $this-\u003econnection-\u003enickname,\n    ]);\n});\n```\n\n## 🔹 `TcpConnection|Connection $connection`\n\nIt is a global object, changing in one place, it will contain the changed data in another place.\n\nThis object has already predefined properties:\n\nSee all `$connection` methods [here](https://doc.hotexamples.com/class/workerman.connection/TcpConnection).\n\nYou can set different properties, functions to this object.\n\n```php\n$connection-\u003efirstName = 'John';\n$connection-\u003elastName = 'Doe';\n$connection-\u003egetFullName = fn () =\u003e $connection-\u003efirstName . ' ' . $connection-\u003elastName;\n\ncall_user_func($connection-\u003egetFullname); // John Doe\n```\n\n### Custom property `channels`\n\n```php\n$connection-\u003echannels; // object of Porter\\Connection\\Channels\n```\n\n#### List of methods `Porter\\Connection\\Channels`\n\n```php\n/**\n * Get connection channels.\n *\n * @return Channel[]\n */\npublic function all(): array\n```\n\n```php\n/**\n * Get channels count.\n *\n * @return int\n */\npublic function count(): int\n```\n\n```php\n/**\n * Method for when connection join to channel should detach channel id from connection.\n *\n * @param string $channelId\n * @return void\n */\npublic function delete(string $channelId): void\n```\n\n```php\n/**\n * Leave all channels for this connection.\n *\n * @return void\n */\npublic function leaveAll(): void\n```\n\n\u003e **NOTICE:** On disconnect client connection will leave of all the channels where he was.\n\n```php\n/**\n * When connection join to channel should attach channel id to connection.\n *\n * You don't need to use this method, it will automatically fire inside the class.\n *\n * @param string $channelId\n * @return void\n */\npublic function add(string $channelId): void\n```\n\n\n\n\n\n\n\n## 🔹 `Client (PHP)`\n\nSimple implementation of client.\n\nSee basic example of client [here](/examples/client-php/client.php).\n\n##### `__construct(string $host, array $context = [])`\n\nCreate client.\n\n```php\n$client = new Client('ws://localhost:3737');\n$client = new Client('wss://example.com:3737');\n```\n\n##### `setWorker(Worker $worker): void`\n\nSet worker.\n\n\u003e **NOTICE:** Worker instance auto init in constructor. Use this method if you need to define worker with specific settings.\n\n##### `getWorker(): Worker`\n\nGet worker.\n\n##### `send(string $type, array $data = []): ?bool`\n\nSend event to server.\n\n```php\n$client-\u003eon('ping', function (AsyncTcpConnection $connection, Payload $payload, Client $client) {\n    $client-\u003esend('pong', ['time' =\u003e time()]);\n});\n```\n\n##### `raw(string $payload): ?bool`\n\nSend raw payload to server.\n\n```php\n$client-\u003eraw('simple message');\n```\n\n##### `onConnected(callable $handler): void`\n\nEmitted when a socket connection is successfully established.\n\n```php\n$client-\u003eonConnected(function (AsynTcpConnection $connection) {\n    //\n});\n```\n\n##### `onDisconnected(callable $handler): void`\n\nEmitted when the server sends a FIN packet.\n\n```php\n$client-\u003eonDisconnected(function (AsynTcpConnection $connection) {\n    //\n});\n```\n\n##### `onError(callable $handler): void`\n\nEmitted when an error occurs with connection.\n\n```php\n$client-\u003eonError(function (AsyncTcpConnection $connection, $code, $message) {\n    //\n});\n```\n\n##### `onRaw(callable $handler): void`\n\nHandle non event messages (raw data).\n\n```php\n$client-\u003eonRaw(function (string $payload, AsyncTcpConnection $connection) {\n    if ($payload == 'ping') {\n        $connection-\u003esend('pong');\n    }\n});\n```\n\n##### `on(string $type, callable $handler): void`\n\nEvent handler as callable.\n\n```php\n$client-\u003eon('pong', function (AsyncTcpConnection $connection, Payload $payload, Client $client) {\n    //\n});\n```\n\n##### `listen(): void`\n\nConnect to server and listen.\n\n```php\n$client-\u003elisten();\n```\n\n\n\n\n\n## 🔹 `Storage`\n\nStorage is a part of server, all data stored in flat files.\n\nTo get started you need set a path where files will be stored.\n\n```php\nserver()-\u003estorage()-\u003eload(__DIR__ . '/server-storage.data'); // you can use any filename\n```\n\nYou can get access to storage like property or method:\n\n```php\nserver()-\u003estorage();\n```\n\n\u003e **NOTICE:** Set path only after if you booting server by (`server()-\u003eboot($worker)` method, `Storage::class` can use anywhere and before booting server.\n\n\u003e **WARNING:** If you not provide path or an incorrect path, data will be stored in RAM. After server restart you lose your data.\n\n#### `Storage::class`\n\n```php\n// as standalone use without server\n$store1 = new Porter\\Storage(__DIR__ . '/path/to/file1');\n$store2 = new Porter\\Storage(__DIR__ . '/path/to/file2');\n$store3 = new Porter\\Storage(__DIR__ . '/path/to/file3');\n```\n\n#### `load(?string $path = null): self`\n\n```php\nserver()-\u003estorage()-\u003eload(__DIR__ . '/path/to/file'); // you can use any filename\n```\n\n#### `put(string $key, mixed $value): void`\n\n```php\nserver()-\u003estorage()-\u003eput('foo', 'bar');\n```\n\n#### `get(string $key, mixed $default = null): mixed`\n\n```php\nserver()-\u003estorage()-\u003eget('foo', 'default value'); // foo\nserver()-\u003estorage()-\u003eget('baz', 'default value'); // default value\n```\n\n#### `remove(string ...$keys): self`\n\n```php\nserver()-\u003estorage()-\u003eremove('foo'); // true\n```\n\n#### `has(string $key): bool`\n\n```php\nserver()-\u003estorage()-\u003ehas('foo'); // true\nserver()-\u003estorage()-\u003ehas('baz'); // false\n```\n\n#### `filename(): string`\n\nReturns path to file.\n\n```php\nserver()-\u003estorage()-\u003egetPath();\n```\n\n## 🔹 Helpers (functions)\n\n#### `server(): Server`\n\n```php\nserver()-\u003eon(...);\n\n// will be like:\nuse Porter\\Server;\nServer::getInstance()-\u003eon(...);\n```\n\n#### `worker(): Worker`\n\n```php\nworker()-\u003econnections;\n\n// will be like:\nuse Porter\\Server;\nServer::getInstance()-\u003egetWorker()-\u003econnections;\n```\n\n#### `channel(string $id, string|array $key = null, mixed $default = null): mixed`\n\n```php\n$channel = channel('secret channel'); // get channel instance\n$channel = server()-\u003echannel('secret channel');\n$channel = server()-\u003echannels()-\u003eget('secret channel');\n```\n\n💡 See all helpers [here](/helpers.php).\n\n## 🔹 Mappable methods (Macros)\n\nYou can extend the class and map your own methods on the fly..\n\nBasic method:\n```php\nserver()-\u003emap('sum', fn(...$args) =\u003e array_sum($args));\necho server()-\u003esum(1000, 300, 30, 5, 2); // 1337\necho server()-\u003esum(1000, 300, 30, 5, 3); // 1338\n```\n\nAs singletone method:\n```php\nserver()-\u003emapOnce('timestamp', fn() =\u003e time());\necho server()-\u003etimestamp(); // e.g. 1234567890\nsleep(1);\necho server()-\u003etimestamp(); // e.g. 1234567890\n```\n\n# 🔹 Front-end\n\nThere is also a [small class](#javascript) for working with websockets on the client side.\n\n```javascript\nif (location.hostname == '127.0.0.1' || location.hostname == 'localhost') {\n    const ws = new WebSocket(`ws://${location.hostname}:3737`); // on local dev\n} else {\n    const ws = new WebSocket(`wss://${location.hostname}:3737`); // on vps with ssl certificate\n}\n\n// options (optional, below default values)\nlet options = {\n    pingInterval: 30000, // 30 sec.\n    maxBodySize: 1e+6, // 1 mb.\n}\n\nconst client = new Porter(ws, options);\n\n// on client connected to server\nclient.connected = () =\u003e {\n    // code...\n}\n\n// on client disconected from server\nclient.disconnected = () =\u003e {\n    // code...\n}\n\n// on error\nclient.error = () =\u003e {\n    // code...\n}\n\n// on raw `pong` event (if you needed it)\nclient.pong = () =\u003e {\n    // code...\n}\n\n// close connection\nclient.close();\n\n// event handler\nclient.on('ping', payload =\u003e {\n    // available properties\n    payload.type;\n    payload.data;\n\n    console.log(payload.data.foo) // bar\n});\n\n// send event to server\nclient.send('pong', {\n    foo: 'bar',\n});\n\n// chain methods\nclient.send('ping').on('pong', payload =\u003e console.log(payload.type));\n\n// send event and handle answer in one method\nclient.send('get online users', {/* ... */}, payload =\u003e {\n    console.log(payload.type); // contains same event type 'get online users'\n    console.log(payload.data.online); // and server answer e.g. '1337 users'\n});\n\n// pass channel_id and target_id for magic properties on back-end server\nclient.send('magical properties example', {\n    channel_id: 'secret channel',\n    target_id: 1337,\n\n    // on backend php websocket server we can use $this-\u003echannel and $this-\u003etarget magical properties.\n});\n\n// send raw websocket data\nclient.raw.send('hello world');\n\n// send raw websocket data as json\nclient.raw.send(JSON.stringify({\n    foo: 'bar',\n}));\n\n// handle raw websocket data from server\nclient.raw.on('hello from server', data =\u003e {\n    console.log(data); // hello from server\n});\n\n// dont forget start listen websocket server!\nclient.listen();\n```\n\n# Used by\n\n* [naplenke.online](https://naplenke.online?ref=porter) — The largest online cinema in Russia. Watching movies together.\n\n# Credits\n\n* [Chipslays](https://github.com/chipslays)\n* [Workerman](https://github.com/walkor/workerman) by [walkor](https://github.com/walkor)\n* [All contributors](https://github.com/chipslays/porter/graphs/contributors)\n\n# License\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchipslays%2Fporter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchipslays%2Fporter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchipslays%2Fporter/lists"}