Files
Siro/backend/ride/places_syria/reverse_geocode.php
Hamza-Ayed 3543fdd2cd Fix #21: High-severity fixes (H-01 through H-06)
H-01: Egypt document uploads - added path traversal prevention (basename),
       replaced HTTP_HOST with APP_DOMAIN env var
H-02: 7 remaining hardcoded /home/siro-api/ paths replaced with env vars
       (ENV_FILE_PATH, INTERNAL_SOCKET_KEY_PATH, WEBHOOK_SECRET_KEY_PATH)
H-03: serviceapp/updateDriver.php - added ownership check (user_id must match
       driverID or user must be admin); non-admins blocked from changing
       password/status/email/phone
H-04: ggg.php - replaced weak client-supplied phone auth with proper admin
       JWT authentication via JwtService
H-05: Static IV fallback in encrypt_decrypt.php already documented as legacy
H-06: Wallet shared password noted as design limitation (mitigated by
       fingerprint verification + short token TTL)
- Also fixed functions.php log message (removed hardcoded path)
2026-06-17 07:56:57 +03:00

165 lines
7.4 KiB
PHP

<?php
// ضبط الهيدر لإرجاع JSON بترميز UTF-8
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../../load_env.php';
$envFile = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../../../env/.env');
loadEnvironment($envFile);
// --- إعدادات الاتصال بقاعدة البيانات ---
$servername = getenv('DB_REVERSE_GEO_HOST') ?: 'localhost';
$username = getenv('DB_REVERSE_GEO_USER') ?: '';
$password = getenv('DB_REVERSE_GEO_PASS') ?: '';
$dbname = getenv('DB_REVERSE_GEO_NAME') ?: '';
if (empty($username) || empty($password) || empty($dbname)) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => 'Database configuration error']);
exit;
}
// --- استقبال الإحداثيات من الطلب ---
$input_lat = isset($_GET['lat']) ? (float)$_GET['lat'] : null;
$input_lon = isset($_GET['lon']) ? (float)$_GET['lon'] : null;
// التحقق من وجود الإحداثيات
if ($input_lat === null || $input_lon === null) {
echo json_encode(['status' => 'error', 'message' => 'Missing lat or lon parameters']);
exit;
}
// --- الاتصال بقاعدة البيانات ---
$conn = new mysqli($servername, $username, $password, $dbname);
$conn->set_charset("utf8mb4");
if ($conn->connect_error) {
echo json_encode(['status' => 'error', 'message' => 'Database connection failed: ' . $conn->connect_error]);
exit;
}
// --- دالة لتحليل other_tags (نفس الدالة من السكربت السابق) ---
function parseHstoreValue($hstoreString, $keyToFind) {
if (empty($hstoreString) || empty($keyToFind)) return null;
if (preg_match('/"' . preg_quote($keyToFind, '/') . '"\s*=>\s*"([^"]+)"/', $hstoreString, $matches)) {
$value = $matches[1];
$decodedValue = urldecode($value);
$decodedValue = urldecode($decodedValue);
$cleanedValue = iconv('UTF-8', 'UTF-8//IGNORE', $decodedValue);
return ($cleanedValue === false || trim($cleanedValue) === '') ? null : $cleanedValue;
}
return null;
}
// --- الاستعلام الرئيسي: البحث عن أقرب نقطة باستخدام الفهرس المكاني ---
// نختار الأعمدة الأساسية + أعمدة المناطق المحسوبة مسبقاً + other_tags
$sql = "
SELECT
p.name,
p.neighbourhood_name,
p.city_name,
p.other_tags,
ST_Distance_Sphere(
p.geom,
ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)
) AS distance_meters
FROM
osm_points_with_area p
WHERE
-- استخدام MBRContains للفلترة الأولية السريعة (باستخدام الفهرس المكاني)
MBRContains(
ST_Buffer(ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326), 0.01), -- مربع بحث ~ 1 كم
p.geom
)
-- الترتيب الدقيق حسب المسافة الأقرب (يستخدم الفهرس المكاني بكفاءة)
ORDER BY
p.geom <-> ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)
LIMIT 1"; // نريد أقرب نقطة فقط
$stmt = $conn->prepare($sql);
if ($stmt === false) {
echo json_encode(['status' => 'error', 'message' => 'Failed to prepare statement: ' . $conn->error]);
$conn->close();
exit;
}
// ربط المتغيرات (6 متغيرات: lon, lat مرتين للمربع ومرة للمسافة)
$stmt->bind_param("dddddd", $input_lon, $input_lat, $input_lon, $input_lat, $input_lon, $input_lat);
$stmt->execute();
$result = $stmt->get_result();
// --- تنسيق وإرجاع النتيجة ---
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
// استخراج التفاصيل الإضافية من other_tags
$name_ar = parseHstoreValue($row['other_tags'], 'name:ar');
$addr_street = parseHstoreValue($row['other_tags'], 'addr:street');
$amenity = parseHstoreValue($row['other_tags'], 'amenity');
$shop = parseHstoreValue($row['other_tags'], 'shop');
// بناء اسم وصفي (الأولوية للعربي إن وجد)
$primaryName = $name_ar ?? $row['name'] ?? $addr_street ?? null; // الاسم الأساسي للنقطة
$displayName = $primaryName ?? 'موقع قريب'; // اسم افتراضي إذا لم يوجد اسم
// إضافة اسم الحي والمدينة (المحسوبة مسبقاً)
$addressParts = array_filter([
$row['neighbourhood_name'],
$row['city_name']
]);
if (!empty($addressParts)) {
// تجنب تكرار اسم المدينة إذا كان هو نفسه اسم النقطة
if ($primaryName !== $row['city_name']) {
$displayName .= '، ' . implode('، ', $addressParts);
} elseif ($row['neighbourhood_name'] && $primaryName !== $row['neighbourhood_name']) {
$displayName .= '، ' . $row['neighbourhood_name'];
}
}
// إرجاع النتيجة كـ JSON
echo json_encode([
'status' => 'ok',
'display_name' => $displayName, // الاسم المنسق للعرض
'name' => $row['name'], // الاسم الأصلي (إن وجد)
'name_ar' => $name_ar, // الاسم العربي (إن وجد)
'street' => $addr_street, // اسم الشارع (إن وجد)
'neighbourhood' => $row['neighbourhood_name'], // اسم الحي (المحسوب مسبقاً)
'city' => $row['city_name'], // اسم المدينة (المحسوب مسبقاً)
'amenity' => $amenity, // نوع الخدمة (إن وجد)
'shop' => $shop, // نوع المحل (إن وجد)
'distance_meters' => round($row['distance_meters'], 1) // المسافة لأقرب POI
], JSON_UNESCAPED_UNICODE); // مهم لعرض العربية بشكل صحيح
} else {
// لم يتم العثور على نقطة قريبة، ابحث عن أقرب مدينة/حي كحل بديل
$areaSqlFallback = "
SELECT name, other_tags, place_type
FROM osm_areas
ORDER BY ST_Distance_Sphere(geom, ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')'), 4326)) ASC
LIMIT 1";
$stmtFallback = $conn->prepare($areaSqlFallback);
if ($stmtFallback) {
$stmtFallback->bind_param("dd", $input_lon, $input_lat);
$stmtFallback->execute();
$fallbackResult = $stmtFallback->get_result()->fetch_assoc();
$stmtFallback->close();
if ($fallbackResult) {
$fallbackNameAr = parseHstoreValue($fallbackResult['other_tags'], 'name:ar');
$fallbackDisplayName = $fallbackNameAr ?? $fallbackResult['name'] ?? 'منطقة غير معروفة';
echo json_encode([
'status' => 'ok',
'display_name' => $fallbackDisplayName,
($fallbackResult['place_type'] === 'city' || $fallbackResult['place_type'] === 'town' || $fallbackResult['place_type'] === 'village') ? 'city' : 'neighbourhood' => $fallbackDisplayName
], JSON_UNESCAPED_UNICODE);
} else {
echo json_encode(['status' => 'not_found', 'message' => 'No nearby places or areas found']);
}
} else {
echo json_encode(['status' => 'error', 'message' => 'Fallback query failed: ' . $conn->error]);
}
}
$stmt->close();
$conn->close();
?>