Почему при onWorkerStart при вызове функции timer система не может получить user_id и receiver_id из json?

<?php

use Workerman\Worker;
use Workerman\Lib\Timer;

require './vendor/autoload.php';
$connect = require './system/db.php';

$ws_worker = new Worker("websocket://0.0.0.0:2346");
$ws_worker->onConnect = function ($connection) {
  echo "соединение открыто\n";
};

$ws_worker->onClose = function ($connection) {
  echo "соединение закрыто\n";
};

$ws_worker->onWorkerStart = function () use ($ws_worker) {

  Timer::add(1, function () use ($ws_worker) {
    global $userId, $receiverId, $connect;

    $allMessages = getMessages($userId, $receiverId);

    $newMessages = getNewMessages($userId, $receiverId);

    foreach ($ws_worker->connections as $connection) {
      $connection->send(json_encode(['all_messages' => $allMessages, 'new_messages' => 
$newMessages]));

      markMessageAsRead($userId, $receiverId);
    }
  });
};

$ws_worker->onMessage = function ($connection, $data) {
  global $userId, $receiverId, $message_text, $connect;

  $decodedData = json_decode($data, true);

  $userId = isset($decodedData['user_id']) ? $decodedData['user_id'] : null;
  $receiverId = isset($decodedData['receiverId']) ? $decodedData['receiverId'] : null;
  $message_text = isset($decodedData['message_text']) ? $decodedData['message_text'] : 
 null;

  echo '$userId: ' . $userId . PHP_EOL;
  echo '$receiverId: ' . $receiverId . PHP_EOL;

  if (!empty($message_text)) {
    sendMessage($userId, $receiverId, $message_text);
  }
};


function sendMessage($userId, $receiverId, $messageText)
{
  global $connect;

  $query = "INSERT INTO messages (sender_id, receiver_id, message_text, date, time, 
 read_status, status) 
              VALUES (:userId, :receiverId, :messageText, NOW(), NOW(), 0, 0)";
  $stmt = $connect->prepare($query);
  $stmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $stmt->bindParam(':receiverId', $receiverId, PDO::PARAM_INT);
  $stmt->bindParam(':messageText', $messageText, PDO::PARAM_STR);
  $stmt->execute();
}
function getMessages($userId, $receiverId)
{
  global $connect;

  $query = "SELECT * FROM messages 
              WHERE ((sender_id = :userId AND receiver_id = :receiverId) 
              OR (sender_id = :receiverId AND receiver_id = :userId))";
  $stmt = $connect->prepare($query);
  $stmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $stmt->bindParam(':receiverId', $receiverId, PDO::PARAM_INT);
  $stmt->execute();

  return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
function getNewMessages($userId, $receiverId)
{
  global $connect;

  $query = "SELECT * FROM messages 
              WHERE read_status = 0 AND status = 1 
              AND ((sender_id = :userId AND receiver_id = :receiverId) 
              OR (sender_id = :receiverId AND receiver_id = :userId))
              ORDER BY date, time";
  $stmt = $connect->prepare($query);
  $stmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $stmt->bindParam(':receiverId', $receiverId, PDO::PARAM_INT);
  $stmt->execute();

  $updateQuery = "UPDATE messages SET read_status = 1, status = 1 
                    WHERE receiver_id = :userId AND sender_id = :receiverId AND 
read_status = 0";
  $updateStmt = $connect->prepare($updateQuery);
  $updateStmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $updateStmt->bindParam(':receiverId', $receiverId, PDO::PARAM_INT);
  $updateStmt->execute();

  return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
function markMessageAsRead($userId, $receiverId)
{
  global $connect;

  $updateQuery = "UPDATE messages SET read_status = 1, status = 1 
                    WHERE receiver_id = :userId AND sender_id = :receiverId AND 
 read_status = 0";
  $updateStmt = $connect->prepare($updateQuery);
  $updateStmt->bindParam(':userId', $userId, PDO::PARAM_INT);
  $updateStmt->bindParam(':receiverId', $receiverId, PDO::PARAM_INT);
  $updateStmt->execute();
}

Worker::runAll();

вот при вызове класса timer внутри onWorkerStart система не получает id получателя/отправителя. и при вызове ЕДИНОЖДЫ этой части кода

 echo '$userId: ' . $userId . PHP_EOL;
 echo '$receiverId: ' . $receiverId . PHP_EOL;

отладка в консоли вызывается дважды т.е. $userId: $receiverId: $userId: 1 $receiverId: 9 т.е в первый раз система не получила id отправителя/получателя, а во второй раз получила и вывела корректные данные. Вышеописанную проблему насчет класса Timer внутри onWorkerStart это теория. Почему я слелал такой вывод? спросите вы. Т.к. я вызвал onWorkerStart первее нежели onMessage поэтому он выдал пустые значения, а onMessage позже и поэтому он вывел корректные значения


Ответы (1 шт):

Автор решения: anonymus

Происходит неправильное использование глобальных переменных в рамках того, как устроен Workerman и таймеры. Когда таймер запускается внутри onWorkerStart, он выполняется внутри отдельного цикла событий и не имеет доступа к локальным переменным, которые устанавливаются в onMessage.

В первом же вызове таймера глобальные переменные $userId и $receiverId не инициализированы, потому что onMessage еще не было вызвано и не установило эти значения. Поэтому и происходит вызов методов getMessages и getNewMessages с пустыми параметрами.

Чтобы исправить эту проблему, вам необходимо передавать user_id и receiver_id в таймер после того, как они были получены в onMessage. Вы могли бы использовать свойства соединения для хранения этих идентификаторов. Например, вы можете устанавливать user_id и receiver_id как свойства объекта $connection, а затем использовать эти значения внутри таймера.

Вот пример того, как можно использовать свойства соединения:

$ws_worker->onMessage = function ($connection, $data) {
  $decodedData = json_decode($data, true);

  if (isset($decodedData['user_id']) && isset($decodedData['receiverId'])) {
    $connection->userId = $decodedData['user_id'];
    $connection->receiverId = $decodedData['receiverId'];
    // Вызов sendMessage и другой логики, если необходимо
  }

  // Остальной код обработки сообщений
};

А внутри таймера вы могли бы получать эти значения таким образом:

Timer::add(1, function () use ($ws_worker) {
  foreach ($ws_worker->connections as $connection) {
    if (isset($connection->userId) && isset($connection->receiverId)) {
      $allMessages = getMessages($connection->userId, $connection->receiverId);
      $newMessages = getNewMessages($connection->userId, $connection->receiverId);
      // Отправка сообщений, пометка сообщений как прочитанных и т.д.
    }
  }
});

Обратите внимание, что данный подход предполагает, что каждое соединение будет иметь уникальные userId и receiverId.

→ Ссылка