Классы протоколов
Фактически, создание своего протокола - довольно простая задача. Обычно протокол подразумевает 2 логические операции:
- Определение размера данных (они могут делиться на части)
 - Интерпретация данных (кодирование/декодирование)
 
В Localzet Server мы определили интерфейс для стандартизации класса протокола. Он включает в себя метод определения
размера данных (input) и методы интерпретации (encode и decode).
Как это работает?
- При получении запроса сервер, запущенный с определённым протоколом, вызывает метод 
inputдля определения длинны пакета. Метод должен вернуть длину пакета или0, в случае если длина ещё не известна (сервер будет ждать ещё данные). - Сервер проверит, соответствует ли длина пакета длине из 
input. Если нет - сервер продолжит ожидать догрузку данных. - После полной загрузки пакета сервер отправит его в метод 
decodeдля обработки. - Обработанные данные отправляются в качестве аргумента в callback-метод 
onMessageдля выполнения основной логики проекта. - Когда бизнес-логике потребуется отправить данные, перед отправкой сервер вызовет метод 
encodeдля обработки ответа. 
Пример протокола
К примеру, давайте напишем такой протокол, данные в котором будут представлены в формате простого JSON,
а символом окончания данных (для определения размера) будет символ переноса строки \n.
Для того чтобы класс стал протоколом необходимо, чтобы он находился в namespace Protocols;.
Для упрощения можно использовать интерфейс \localzet\Server\Protocols\ProtocolInterface.
<?php
namespace Protocols;
use \localzet\Server\Protocols\ProtocolInterface;
class SuperJSON
{
    /**
     * Проверка целостности пакета
     * Если можно получить длину пакета в $recv_buffer, возвращается длина всего пакета
     * В противном случае возвращается 0, что означает, что необходимо дождаться дополнительных данных.
     * Если возникает ошибка в протоколе, можно вернуть false, что приведет к разрыву соединения с текущим клиентом
     * 
     * @param string $recv_buffer Буфер полученных данных
     * @param \localzet\Server\Connection\ConnectionInterface $connection Соединение с клиентом
     * @return int|false Длина пакета, 0 если данных недостаточно, false при ошибке
     */
    public static function input(string $recv_buffer, \localzet\Server\Connection\ConnectionInterface $connection): int|false
    {
        // Получение позиции символа переноса строки "\n"
        $pos = strpos($recv_buffer, "\n");
        // Если его нет - невозможно получить длину пакета, вернуть 0 и ожидать дополнительных данных
        if($pos === false)
        {
            return 0;
        }
        // Если "\n" нашёлся - вернуть длину текущего пакета (включая символ переноса строки)
        return $pos+1;
    }
    /**
     * Упаковка, автоматически вызывается при отправке данных клиенту
     * 
     * @param mixed $data Данные для отправки
     * @param \localzet\Server\Connection\ConnectionInterface $connection Соединение с клиентом
     * @return string Закодированные данные для отправки
     */
    public static function encode(mixed $data, \localzet\Server\Connection\ConnectionInterface $connection): string
    {
        // Формирование JSON и добавление символа переноса строки в качестве пометки окончания запроса
        return json_encode($data, JSON_THROW_ON_ERROR) . "\n";
    }
    /**
     * Разбор, автоматически вызывается, когда количество полученных байтов равно возвращенному значению input (значение больше 0)
     * и передается как параметр $data в обратный вызов onMessage
     * 
     * @param string $buffer Буфер данных для декодирования
     * @param \localzet\Server\Connection\ConnectionInterface $connection Соединение с клиентом
     * @return mixed Декодированные данные
     */
    public static function decode(string $buffer, \localzet\Server\Connection\ConnectionInterface $connection): mixed
    {
        // Удаление символа переноса строки и превращение в массив
        $decoded = json_decode(trim($buffer), true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \RuntimeException('Ошибка декодирования JSON: ' . json_last_error_msg());
        }
        return $decoded;
    }
}
Теперь протокол SuperJSON реализован и готов к использованию, например:
<?php
use localzet\Server;
use localzet\Server\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$server = new Server("SuperJSON://0.0.0.0:2000");
$server->onMessage = function(TcpConnection $connection, $data)
{
    // $data - это данные, отправленные клиентом, которые уже были обработаны методом SuperJSON::decode
    echo $data;
    // Данные, отправленные через $connection->send, автоматически упаковываются методом SuperJSON::encode и затем отправляются клиенту
    $connection->send(['code' => 0, 'msg' => 'ok']);
};
Server::runAll();

