Files
tripz-llc/backend/auth/passenger/register.php
T
Hamza-AyedandClaude Opus 5 4d8414c96b feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل
قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة
ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ.

الخريطة:
  backend · payment_server · loction_server · ride_server ·
  passenger_server · docker · dashboard · stress_test  → الجذر
  siro_rider  → apps/rider          siro_driver  → apps/driver
  siro_admin  → dashboards/admin    siro_service → dashboards/service
  android_bot → apps/android_bot    socialBot    → apps/socialBot

نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب)
لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً:
كل ما يلي يصير فرقاً مقروءاً مقابل المصدر.

لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز،
سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh
(ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في
مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و
dashboards/transit-web).

⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة:
1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر):
   كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner.
2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist)
   يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً.
3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع →
   يجب ضمّ الحزم داخله أسوة بـ apps/rider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:14:13 +03:00

199 lines
8.6 KiB
PHP

<?php
// File: register_passenger.php
// إعدادات إظهار الأخطاء
ini_set('display_errors', 0);
error_reporting(E_ALL);
$allowRegistration = true;
require_once __DIR__ . '/../../connect.php';
// Rate Limiting: الحماية من البرمجيات الخبيثة والتسجيل العشوائي
$rateLimiter = new RateLimiter($redis);
$rateLimiter->enforce(RateLimiter::identifier(), 'register_passenger');
// تعريف بادئة للوج (Tag) لسهولة البحث عنها في ملف الأخطاء
$logTag = "[Register_Debug_passenger]";
$step = 0;
try {
// ======================================================
// Step 1: استقبال البيانات
// ======================================================
$step = 1;
$phoneNumber = filterRequest("phone_number");
$firstName = filterRequest("first_name");
$lastName = filterRequest("last_name");
$email = filterRequest("email");
// طباعة وصول البيانات (مع إخفاء جزء من الرقم)
error_log("$logTag Step 1: Received request. Phone: " . substr($phoneNumber, 0, 7) . "*****");
// ======================================================
// Step 2: التحقق من المدخلات
// ======================================================
$step = 2;
if (empty($phoneNumber) || empty($firstName) || empty($lastName)) {
error_log("$logTag Step 2 Error: Missing required fields.");
jsonError("Required fields are missing.");
exit();
}
// ======================================================
// Step 3: معالجة الإيميل
// ======================================================
$step = 3;
if (empty($email)) {
$email = $phoneNumber . '@intaleqapp.com';
error_log("$logTag Step 3: Email was empty, generated default: " . substr($email, 0, 5) . "***");
}
// ======================================================
// Step 4: تشفير البيانات
// ======================================================
$step = 4;
error_log("$logTag Step 4: Encrypting data...");
if (!isset($encryptionHelper)) {
throw new Exception("Encryption Helper class is missing.");
}
$phoneNumber_encrypted = $encryptionHelper->encryptData($phoneNumber);
$firstName_encrypted = $encryptionHelper->encryptData($firstName);
$lastName_encrypted = $encryptionHelper->encryptData($lastName);
$email_encrypted = $encryptionHelper->encryptData($email);
$uniqueId = substr(md5($phoneNumber), 0, 20);
$password_hashed = password_hash($email . $uniqueId, PASSWORD_DEFAULT);
$unknown_encrypted = $encryptionHelper->encryptData("unknown yet");
// ======================================================
// Step 4.5: التحقق الفعلي من ملكية رقم الهاتف (🔥 Fix)
// ======================================================
// كانت هذه النقطة تسمح بإنشاء حساب راكب بأي رقم هاتف بدون إثبات
// ملكيته فعلياً — auth/otp/verify.php يُعلّم الصف verified=1 لكن
// register_passenger.php لم يكن يتحقق من ذلك إطلاقاً. الآن نشترط
// وجود صف تحقق ناجح (verified=1) لنفس رقم الهاتف خلال آخر 30 دقيقة
// (مهلة أوسع من صلاحية الرمز نفسه [5 دقائق] لإعطاء وقت كافٍ لإكمال
// نموذج التسجيل بعد التحقق مباشرة).
$step = 4.5;
$verifyCheckStmt = $con->prepare(
"SELECT id FROM phone_verification_passenger
WHERE phone_number = ? AND verified = 1 AND created_at > DATE_SUB(NOW(), INTERVAL 30 MINUTE)
LIMIT 1"
);
$verifyCheckStmt->execute([$phoneNumber_encrypted]);
if ($verifyCheckStmt->rowCount() === 0) {
error_log("$logTag Step 4.5 Error: Phone number not verified via OTP.");
jsonError("Phone number must be verified before registration.");
exit();
}
// ======================================================
// Step 5: إنشاء ID فريد
// ======================================================
$step = 5;
// $uniqueId = substr(md5(uniqid(mt_rand(), true)), 0, 20);
// $uniqueId is now generated earlier
error_log("$logTag Step 5: Generated Unique ID: $uniqueId");
// ======================================================
// Step 6: التحقق من وجود المستخدم (Database Check)
// ======================================================
$step = 6;
// كشف التكرار عبر الفهرس الأعمى + المقارنة القديمة: بدون الفهرس يفشل
// الكشف بعد الانتقال إلى GCM فيُسمح بتسجيل نفس الرقم مرتين.
global $blindIndex;
$phoneBidx = $blindIndex ? $blindIndex->index('passengers.phone', $phoneNumber) : null;
$emailBidx = $blindIndex ? $blindIndex->index('passengers.email', $email) : null;
$nameBidx = $blindIndex ? $blindIndex->index('passengers.name', trim("$firstName $lastName")) : null;
// مفتاح ربط جداول التحقق — يجب أن يطابق otpPhoneKey() حرفياً
$phoneKey = otpPhoneKey($phoneNumber);
$checkStmt = $con->prepare(
"SELECT id FROM passengers WHERE phone = ? OR (? IS NOT NULL AND phone_bidx = ?)"
);
$checkStmt->execute([$phoneNumber_encrypted, $phoneBidx, $phoneBidx]);
if ($checkStmt->rowCount() > 0) {
error_log("$logTag Step 6 Error: User already exists.");
jsonError("User with this phone number or email already exists.");
exit();
}
// ======================================================
// Step 7: الإضافة (Insert User)
// ======================================================
$step = 7;
error_log("$logTag Step 7: Inserting into passengers table...");
$insertStmt = $con->prepare("
INSERT INTO passengers (id, first_name, last_name, email, phone, password, gender, birthdate, site, sosPhone, education, employmentType, maritalStatus, status, created_at, updated_at, phone_bidx, email_bidx, name_bidx, phone_key)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW(), NOW(), ?, ?, ?, ?)
");
$success = $insertStmt->execute([
$uniqueId,
$firstName_encrypted,
$lastName_encrypted,
$email_encrypted,
$phoneNumber_encrypted,
$password_hashed,
$unknown_encrypted,
$unknown_encrypted,
$unknown_encrypted,
$unknown_encrypted,
$unknown_encrypted,
$unknown_encrypted,
$unknown_encrypted,
// فهارس البحث: تُكتب مع السجل حتى يكون قابلاً للبحث فوراً
$phoneBidx,
$emailBidx,
$nameBidx,
$phoneKey
]);
if (!$success) {
$errorInfo = $insertStmt->errorInfo();
// طباعة تفاصيل خطأ الـ SQL في اللوج
error_log("$logTag Step 7 Error: SQL Insert Failed. Details: " . json_encode($errorInfo));
jsonError("Failed to create user account.");
exit();
}
// ======================================================
// Step 9: جلب البيانات لإعادتها
// ======================================================
$step = 9;
$userStmt = $con->prepare("SELECT * FROM passengers WHERE id = ?");
$userStmt->execute([$uniqueId]);
$newUser = $userStmt->fetch(PDO::FETCH_ASSOC);
// ======================================================
// Step 10: فك التشفير وإرسال الرد
// ======================================================
$step = 10;
if ($newUser) {
unset($newUser['password']);
foreach ($newUser as $key => &$value) {
if ($key !== 'id' && $key !== 'status' && $key !== 'created_at' && $key !== 'updated_at' && !is_null($value)) {
$value = $encryptionHelper->decryptData($value);
}
}
}
error_log("$logTag Success: User registered successfully.");
jsonSuccess(["status" => "registration_success", "data" => $newUser]);
} catch (PDOException $e) {
// طباعة خطأ قاعدة البيانات في اللوج
error_log("$logTag PDO Exception at Step $step: " . $e->getMessage());
jsonError("Database Error.");
} catch (Exception $e) {
// طباعة الأخطاء العامة في اللوج
error_log("$logTag General Exception at Step $step: " . $e->getMessage());
jsonError("General Error.");
}
?>