encryptData($data[$f]); } } // حساسات السيارة $car['vin'] = $encryptionHelper->encryptData($car['vin']); $car['car_plate'] = $encryptionHelper->encryptData($car['car_plate']); $car['owner'] = $encryptionHelper->encryptData($car['owner']); /* ========== 4) هَش كلمة المرور ========== */ $pwdHashed = password_hash(filterRequest('password'), PASSWORD_DEFAULT); /* ========== 5) بدء معاملة ========== */ $con->beginTransaction(); /* ========== 6) فحص تكرار هاتف/ايميل (المشفّرين) ========== */ $dup = $con->prepare("SELECT id FROM driver WHERE phone = :p OR email = :e"); $dup->execute([':p' => $data['phone'], ':e' => $data['email']]); if ($dup->rowCount() > 0) { $con->rollBack(); jsonError("Phone or email already registered."); exit; } /* ========== 7) إدراج السائق ========== */ $sqlDriver = " INSERT INTO driver ( id, phone, email, password, gender, license_type, national_number, name_arabic, issue_date, expiry_date, license_categories, address, licenseIssueDate, status, birthdate, site, first_name, last_name, accountBank, bankCode, employmentType, maritalStatus, fullNameMaritial, expirationDate, created_at, updated_at ) VALUES ( :id, :phone, :email, :pwd, :gender, :license_type, :national_number, :name_arabic, :issue_date, :expiry_date, :license_categories, :address, :licenseIssueDate, :status, :birthdate, :site, :first_name, :last_name, :accountBank, :bankCode, :employmentType, :maritalStatus, :fullNameMaritial, :expirationDate, NOW(), NOW() ) "; $insD = $con->prepare($sqlDriver); $okD = $insD->execute([ ':id' => $driverID, ':phone' => $data['phone'], ':email' => $data['email'], ':pwd' => $pwdHashed, ':gender' => $data['gender'], ':license_type' => $data['license_type'], ':national_number' => $data['national_number'], ':name_arabic' => $data['name_arabic'], ':issue_date' => $data['issue_date'], ':expiry_date' => $data['expiry_date'], ':license_categories'=> !empty($data['license_categories']) ? $data['license_categories'] : 'B', ':address' => $data['address'], ':licenseIssueDate' => $data['licenseIssueDate'], ':status' => !empty($data['status']) ? $data['status'] : 'yet', ':birthdate' => $data['birthdate'], ':site' => $data['site'], ':first_name' => $data['first_name'], ':last_name' => $data['last_name'], ':accountBank' => 'yet', ':bankCode' => 'yet', ':employmentType' => !empty($data['employmentType']) ? $data['employmentType'] : 'yet', ':maritalStatus' => !empty($data['maritalStatus']) ? $data['maritalStatus'] : 'yet', ':fullNameMaritial' => !empty($data['fullNameMaritial']) ? $data['fullNameMaritial'] : 'yet', ':expirationDate' => !empty($data['expirationDate']) ? $data['expirationDate'] : 'yet', ]); if (!$okD) { $con->rollBack(); jsonError("Failed to insert driver."); exit; } /* ========== 8) إدراج السيارة ========== */ $hasCar = $con->prepare("SELECT 1 FROM CarRegistration WHERE driverID = :d LIMIT 1"); $hasCar->execute([':d' => $driverID]); $isDefault = $hasCar->rowCount() === 0 ? 1 : 0; $sqlCar = " INSERT INTO CarRegistration ( driverID, vin, car_plate, make, model, year, expiration_date, color, owner, color_hex, fuel, isDefault, created_at, status ) VALUES ( :driverID, :vin, :car_plate, :make, :model, :year, :expiration_date, :color, :owner, :color_hex, :fuel, :isDefault, NOW(), 'yet' ) "; $insC = $con->prepare($sqlCar); $okC = $insC->execute([ ':driverID' => $driverID, ':vin' => $car['vin'], ':car_plate' => $car['car_plate'], ':make' => $car['make'], ':model' => $car['model'], ':year' => $car['year'], ':expiration_date' => $car['expiration_date'], ':color' => $car['color'], ':owner' => $car['owner'], ':color_hex' => $car['color_hex'], ':fuel' => $car['fuel'], ':isDefault' => $isDefault, ]); if (!$okC) { $con->rollBack(); jsonError("Failed to insert car registration."); exit; } $carRegID = $con->lastInsertId(); /* ========== 9) التحقّق من الروابط الموقّعة وحفظها ========== */ // دالة مساعدة تتحقّق من شكل الرابط وتستخرج doc_type/ext $validateSignedUrl = function(string $url) use ($allowedDocTypes, $allowedExts) { $parts = parse_url($url); if (!$parts || empty($parts['scheme']) || empty($parts['host']) || empty($parts['path'])) { throw new Exception("Invalid URL format."); } if (!in_array($parts['host'], $ALLOWED_SIGNED_HOSTS, true)) { throw new Exception("URL host not allowed: {$parts['host']}"); } if (stripos($parts['path'], 'secure_image.php') === false) { throw new Exception("URL path not allowed."); } if (empty($parts['query'])) { throw new Exception("URL missing query string."); } parse_str($parts['query'], $q); foreach (['driver_id','doc_type','ext','expires','signature'] as $k) { if (empty($q[$k])) throw new Exception("URL missing param: $k"); } if (!in_array($q['doc_type'], $allowedDocTypes, true)) { throw new Exception("Invalid doc_type in URL."); } if (!in_array(strtolower($q['ext']), $allowedExts, true)) { throw new Exception("Invalid ext in URL."); } return [ 'doc_type' => $q['doc_type'], 'ext' => strtolower($q['ext']), // بإمكانك التحقق من driver_id = $driverID إذا تحب تربطهما 'driver_id_in_url' => $q['driver_id'], ]; }; $docsToInsert = []; // [['doc_type'=>..., 'link'=>..., 'image_name'=>...], ...] foreach ($docUrlKeys as $k) { $link = $docUrls[$k]; $meta = $validateSignedUrl($link); // image_name ليس ضروريًا هنا (الرابط موقّع إلى بوابة قراءة)، احفظ doc_type + link فقط $docsToInsert[] = [ 'doc_type' => $meta['doc_type'], // يجب أن يتطابق مع $k منطقيًا 'link' => $link, 'image_name' => $meta['doc_type'] . '.' . $meta['ext'], // اسماً رمزياً فقط ]; } // إدراج في driver_documents // CREATE TABLE driver_documents ( // id INT AUTO_INCREMENT PRIMARY KEY, // driverID VARCHAR(64) NOT NULL, // doc_type VARCHAR(64) NOT NULL, // image_name VARCHAR(255) NULL, // link VARCHAR(1024) NOT NULL, // upload_date DATETIME NOT NULL, // INDEX(driverID) // ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; $insDoc = $con->prepare(" INSERT INTO driver_documents (driverID, doc_type, image_name, link, upload_date) VALUES (:driverID, :doc_type, :image_name, :link, NOW()) "); foreach ($docsToInsert as $row) { $insDoc->execute([ ':driverID' => $driverID, ':doc_type' => $row['doc_type'], ':image_name' => $row['image_name'], ':link' => $row['link'], ]); } /* ========== 10) إنهاء المعاملة ========== */ $con->commit(); printSuccess([ 'driverID' => $driverID, 'carRegID' => $carRegID, 'documents' => [ 'driver_license_front_url' => $docUrls['driver_license_front_url'], 'driver_license_back_url' => $docUrls['driver_license_back_url'], 'car_license_front_url' => $docUrls['car_license_front_url'], 'car_license_back_url' => $docUrls['car_license_back_url'], ] ]); } catch (Exception $e) { if (isset($con) && $con->inTransaction()) { $con->rollBack(); } error_log("register_driver_and_car ERROR: " . $e->getMessage()); jsonError("Server error"); } catch (PDOException $e) { if (isset($con) && $con->inTransaction()) { $con->rollBack(); } error_log("register_driver_and_car PDO: " . $e->getMessage()); jsonError("Database error."); }