<?php

/**
 * 消息系统服务
 * php messageWorker.php -d start 守护进程方式启动 Websocket, 其它 start,restart,stop, ps -aux | grep messageWoker
 * 服务端 composer require workerman/workerman
 * 客户端 composer require textalk/websocket
 */

use xmvc\daemon\lib\Online;
use Workerman\Worker;
use Workerman\Lib\Timer;

require_once realpath(__DIR__ . '/../composer/vendor/autoload.php');
require_once __DIR__ . '/lib/online.php';

define('IS_PRINT', true); // 是否打印内容到控制台, 开发与调试时才开启
$title = 'XMVC MESSAGE SYSTEM';
$pid = getmypid();

if (!cli_set_process_title($title)) {
    echo "无法设置 PID 的进程标题 $pid...\n";
    exit(1);
} else {
    echo "{$title} 的进程ID $pid 已经开始工作!\n";
    sleep(1);
}

function send($idArr, $data, $online)
{
    foreach ($idArr as $id => $v) {
        $online->lastSendTime($id);
        $online->list[$id]['connection']->send($data);
        printLine('to', $online, $id);
    }
}

function printLine($action, $online, $id)
{
    if (IS_PRINT) {
        if ($online && isset($online->list[$id])) {
            printf("%s: %s [%d]" . PHP_EOL, $action, $online->list[$id]['userid'], $id);
        } else {
            printf("%s" . PHP_EOL, $action);
        }
    }
}

// 端口
$port = cfg('websocket>port');

// 域名白名单
$tmp = cfg('websocket>whiteList');
$tmp = explode("\n", $tmp);
$domainWhiteList = [];
foreach ($tmp as $key => $value) {
    $value = trim($value, " \r/");
    if ($value) {
        $domainWhiteList[$key] = $value;
    }
}
unset($tmp);

// 在线人员
$online = new Online();

// 创建 websocket 服务
$worker = new Worker('websocket://0.0.0.0:' . $port);

//启动1个进程对外提供服务
$worker->count = 1;

$worker->onMessage = function ($connection, $data) use ($online, $title) {
    $data = json_decode($data, true);
    static $sign_count; // 签名统计计数
    $id = $connection->id;
    $cmd = $data['cmd'];
    $value = $data['value'];
    if ($cmd == 'SIGN') { //签名
        $online->set($connection, $value);
        $connection->send(json_encode(["type" => "welcome", "data" => "HELLO [{$value}] WELCOME TO {$title}"]));
        printLine('SIGN', $online, $connection->id);
        if (++$sign_count > 10000) {
            // 签名数达到10000后退出当前进程，主进程会自动重启一个新的进程
            Worker::stopAll();
        }
    } elseif ($cmd == 'HEARTBEAT') { //心跳
        $online->updateReadTime($id);
    } elseif ($cmd == 'SEND') { //发送
        $to = $value['to'];
        $senddata = json_encode($value['data']);
        if ($to == 'ALL') {
            send($online->list, $senddata, $online);
        } else {
            if (is_string($to)) {
                $to = [$to];
            }
            foreach ($to as $userid) {
                $ids = $online->getIdsByUserid($userid);
                send($ids, $senddata, $online);
            }
        }
    }
};

$worker->onWorkerStart = function ($worker) use ($online) {
    if ($worker->id === 0) {
        Timer::add(120, function () use ($online) {
            $online->clear();
        });
    }
};

$worker->onConnect = function ($connection) use ($online, $domainWhiteList) {
    //$connection->getRemoteIp(); 远程IP
    $connection->onWebSocketConnect = function ($connection, $httpBuffer) use ($online, $domainWhiteList) {
        // 可以在这里判断连接来源是否合法，不合法就关掉连接
        // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket连接
        // onWebSocketConnect 里面$_GET $_SERVER是可用的
        // var_dump($_GET, $_SERVER);
        if (isset($_SERVER['HTTP_ORIGIN']) && !in_array($_SERVER['HTTP_ORIGIN'], $domainWhiteList)) {
            $msg = $_SERVER['HTTP_ORIGIN'] . ' 不在白名单中';
            printLine($msg, [], 0);
            $connection->send(json_encode(['type' => 'error', 'data' => $msg]));
            // 延迟1.5秒关闭连接
            Timer::add(1.5, function () use ($connection) {
                $connection->close();
            }, [], false);
        } else {
            printLine('Connected', $online, $connection->id);
        }
    };
};

$worker->onClose = function ($connection) use ($online) {
    printLine('Closed', $online, $connection->id);
    $online->del($connection->id);
};

Worker::runAll();
