حرس السوكيتات المكرّرة للسائق — سبب «notified 0/0» وتحذيرات null

في لوج الإنتاج ثلاثة اتصالات متزامنة للسائق نفسه (التطبيق يفتح سوكيتاً من
location_controller وآخر من background_service ومحاولات لم تُنظَّف):
    ✅ Driver Connected: #243ea… (×3)

ومعالج disconnect كان ينفّذ unset($connectedDrivers[$driverId]) بلا تحقّق من
أيّ سوكيت أُغلق. فإغلاق نسخة واحدة كان يُطفئ السائق فعلياً وهو متصل:

1) dispatch_order يراه offline فيضع الطلب في طابور الانتظار بدل إرساله،
   ويظهر في اللوج:  🚫 Ride #2569 cancelled — notified 0/0 driver(s).
2) update_location من سوكيت ما زال حياً يجد $driverState[$driverId] محذوفاً،
   و &$driverState[...] يُنشئ مدخلاً null فتُقرأ منه كل الحسابات:
     Warning: Trying to access array offset on value of type null (860-872)
     Warning: Undefined array key "status" (907)

الإصلاحان:
- نحفظ sid (معرّف السوكيت) مع السجلّ، ولا ننظّف في disconnect إلا إذا كان
  المُغلَق هو المسجَّل حالياً؛ وإلا نتركه ونسجّل «stale duplicate».
- حرس على $driverState في update_location: يُعاد بناؤه إن غاب لأي سبب
  (إغلاق نسخة أخرى، إعادة تشغيل) بدل القراءة من null.

سيرفر فقط — لا يحتاج بناء التطبيقات. يبقى فتح التطبيق لعدة سوكيتات هدراً
في الحركة يستحق إصلاحاً في الطرف الآخر لاحقاً.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-27 01:26:20 +03:00
co-authored by Claude Opus 5
parent e6504acdea
commit 899a18b52d
+34 -1
View File
@@ -743,10 +743,18 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
} }
$socket->join('driver_' . $driverId); $socket->join('driver_' . $driverId);
// ‏نحفظ معرّف السوكيت مع السجلّ: التطبيق يفتح أكثر من اتصال للسائق نفسه
// ‏(location_controller و background_service ومحاولات لم تُنظَّف)، وحين يُغلق
// ‏أحدها كان معالج disconnect يمحو السائق من السجلّ كاملاً بينما البقية
// ‏متصلة — فيعتبره dispatch_order غير متصل ويضع الطلب في طابور الانتظار،
// ‏ويظهر في اللوج «notified 0/0 driver(s)».
$socketId = $socket->id;
$connectedDrivers[$driverId] = [ $connectedDrivers[$driverId] = [
'conn' => $socket, 'conn' => $socket,
'platform' => $platform, 'platform' => $platform,
'token' => $fcmToken, 'token' => $fcmToken,
'sid' => $socketId,
]; ];
if (!isset($driverState[$driverId])) { if (!isset($driverState[$driverId])) {
@@ -837,6 +845,21 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
if ($lat === null || $lng === null) return; if ($lat === null || $lng === null) return;
// ‏حرس: لو حُذفت الحالة (إغلاق سوكيت آخر لنفس السائق، أو إعادة تشغيل)
// ‏فإن ‎&$driverState[$driverId]‎ يُنشئ مدخلاً null فتقرأ كل الأسطر التالية
// ‏من null — وهذا مصدر تحذيرات «array offset on value of type null» في
// ‏الأسطر 860‑872 و«Undefined array key status» في 907.
if (!isset($driverState[$driverId]) || !is_array($driverState[$driverId])) {
$driverState[$driverId] = [
'lat' => 0.0,
'lng' => 0.0,
'speed' => -999.0,
'heading' => -999.0,
'status' => '',
'expire_ts' => 0,
];
}
$state = &$driverState[$driverId]; $state = &$driverState[$driverId];
$now = time(); $now = time();
@@ -996,9 +1019,19 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
], $INTERNAL_KEY, $busFwdThrottle); ], $INTERNAL_KEY, $busFwdThrottle);
}); });
$socket->on('disconnect', function () use ($driverId) { $socket->on('disconnect', function () use ($driverId, $socketId) {
global $connectedDrivers, $driverState, $fwdThrottle; global $connectedDrivers, $driverState, $fwdThrottle;
// ‏لا ننظّف إلا إذا كان المُغلَق هو السوكيت المسجَّل حالياً. الجهاز يفتح
// ‏عدة اتصالات للسائق نفسه، وتنظيف السجلّ عند إغلاق نسخة قديمة كان
// ‏يُطفئ السائق فعلياً وهو متصل: التوزيع يراه offline، و update_location
// ‏من السوكيت الحيّ يجد $driverState محذوفاً فتظهر تحذيرات null.
$registeredSid = $connectedDrivers[$driverId]['sid'] ?? null;
if ($registeredSid !== null && $registeredSid !== $socketId) {
logMsg("↩️ Stale duplicate socket closed for #$driverId — registry kept.");
return;
}
unset($connectedDrivers[$driverId]); unset($connectedDrivers[$driverId]);
unset($driverState[$driverId]); unset($driverState[$driverId]);
unset($fwdThrottle[$driverId]); unset($fwdThrottle[$driverId]);