$raw, 'norm' => $norm, 'hash' => hash('sha256', $norm), 'mode' => $mode, ]; } // DB try { $pdo = new PDO(DB_DSN, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); } catch (Throwable $e) { http_response_code(500); echo json_encode(['error' => 'DB connection failed']); exit; } // Wejście $q = isset($_GET['q']) ? trim((string)$_GET['q']) : ''; $lat = isset($_GET['lat']) ? (float)$_GET['lat'] : null; $lng = isset($_GET['lng']) ? (float)$_GET['lng'] : null; if ($q === '' && ($lat === null || $lng === null)) { http_response_code(400); echo json_encode(['error' => 'Provide q=address or lat & lng']); exit; } $key = build_cache_key($q, $lat, $lng); $now = now_mysql(); // ===== 1) CACHE LOOKUP (forward i reverse) ===== $stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h AND (expires_at IS NULL OR expires_at > NOW()) LIMIT 1"); $stmt->execute([':h' => $key['hash']]); if ($row = $stmt->fetch()) { $pdo->prepare("UPDATE geocode_cache SET hits = hits + 1, updated_at = NOW() WHERE id = :id")->execute([':id' => $row['id']]); echo json_encode([ 'from_cache' => true, 'source' => 'cache', 'stale' => false, 'lat' => (float)$row['lat'], 'lng' => (float)$row['lng'], 'formatted_address' => $row['formatted_address'], 'place_id' => $row['place_id'], 'provider' => $row['provider'], ], JSON_UNESCAPED_UNICODE); exit; } // ===== 2) PYTANIE DO GOOGLE ===== function google_request(array $params): array { $base = 'https://maps.googleapis.com/maps/api/geocode/json'; $params['key'] = GOOGLE_GEOCODE_KEY; $params += ['language' => GOOGLE_LANGUAGE, 'region' => GOOGLE_REGION]; $url = $base . '?' . http_build_query($params); $ch = curl_init($url); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3]); $res = curl_exec($ch); $err = curl_error($ch); curl_close($ch); if ($res === false) throw new RuntimeException('cURL error: ' . $err); $data = json_decode($res, true); if (!is_array($data)) throw new RuntimeException('Invalid JSON from Google'); if (($data['status'] ?? '') !== 'OK' || empty($data['results'][0])) { $status = $data['status'] ?? 'UNKNOWN'; throw new RuntimeException('Geocoding failed: ' . $status); } return $data; } try { $data = ($key['mode'] === 'forward') ? google_request(['address' => $q]) : google_request(['latlng' => $lat . ',' . $lng]); $top = $data['results'][0]; $latV = (float)$top['geometry']['location']['lat']; $lngV = (float)$top['geometry']['location']['lng']; $fmt = (string)($top['formatted_address'] ?? ''); $pid = (string)($top['place_id'] ?? ''); // ===== 3) ZAPIS DO CACHE (forward i reverse) ===== $ttlDays = ($key['mode'] === 'forward') ? CACHE_TTL_DAYS_FORWARD : CACHE_TTL_DAYS_REVERSE; $expires = add_days($now, $ttlDays); $stmt = $pdo->prepare("INSERT INTO geocode_cache (query_hash, query_raw, query_norm, lat, lng, formatted_address, place_id, provider, hits, created_at, updated_at, expires_at, raw_json) VALUES (:h, :raw, :norm, :lat, :lng, :fmt, :pid, 'google', 1, :now, :now, :exp, :json) ON DUPLICATE KEY UPDATE lat = VALUES(lat), lng = VALUES(lng), formatted_address = VALUES(formatted_address), place_id = VALUES(place_id), updated_at = VALUES(updated_at), expires_at = VALUES(expires_at), raw_json = VALUES(raw_json), hits = hits + 1"); $stmt->execute([ ':h' => $key['hash'], ':raw' => $key['raw'], ':norm'=> $key['norm'], ':lat' => $latV, ':lng' => $lngV, ':fmt' => $fmt, ':pid' => $pid, ':now' => $now, ':exp' => $expires, ':json'=> json_encode($data, JSON_UNESCAPED_UNICODE), ]); echo json_encode([ 'from_cache' => false, 'source' => 'google', 'stale' => false, 'lat' => $latV, 'lng' => $lngV, 'formatted_address' => $fmt, 'place_id' => $pid, 'provider' => 'google', ], JSON_UNESCAPED_UNICODE); exit; } catch (Throwable $e) { try { $stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h LIMIT 1"); $stmt->execute([':h' => $key['hash']]); if ($row = $stmt->fetch()) { http_response_code(200); echo json_encode([ 'from_cache' => true, 'source' => 'cache', 'stale' => true, 'lat' => (float)$row['lat'], 'lng' => (float)$row['lng'], 'formatted_address' => $row['formatted_address'], 'place_id' => $row['place_id'], 'provider' => $row['provider'], ], JSON_UNESCAPED_UNICODE); exit; } } catch (Throwable $ignored) {} // *** fallback „pusty” *** http_response_code(200); echo json_encode([ 'from_cache' => false, 'source' => 'error', 'stale' => false, 'lat' => null, 'lng' => null, 'formatted_address' => '', 'place_id' => '', 'provider' => '', 'error' => $e->getMessage(), ], JSON_UNESCAPED_UNICODE); exit; }