Почему при 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 шт):
Происходит неправильное использование глобальных переменных в рамках того, как устроен 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.