Files
shopPRO/geocode-cache.php

211 lines
6.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// geocode-cache.php
// ==== KONFIGURACJA ====
const DB_DSN = 'mysql:host=localhost;dbname=host117523_shoppro;charset=utf8mb4';
const DB_USER = 'host117523_shoppro';
const DB_PASS = 'mhA9WCEXEnRfTtbN33hL';
// Klucz trzymaj w .env/konfiguracji, tu tylko przykład:
const GOOGLE_GEOCODE_KEY = 'AIzaSyAViiTpMfvWkyL_Gb7QZLnGw_sjv_PKOIc';
// Domyślne opcje zapytań do Google zawęź region/język wg potrzeb
const GOOGLE_REGION = 'pl';
const GOOGLE_LANGUAGE = 'pl';
// TTL cache w dniach
const CACHE_TTL_DAYS_FORWARD = 365;
const CACHE_TTL_DAYS_REVERSE = 365;
$ALLOWED_ORIGINS = [
'https://twoja-domena.pl',
'https://www.twoja-domena.pl',
'http://localhost:3000',
'https://shoppro.project-dc.pl',
'https://moodo.pl'
];
// ==== NAGŁÓWKI/CORS ====
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if ($origin && in_array($origin, $ALLOWED_ORIGINS, true)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Vary: Origin');
}
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
// ==== POMOCNICZE ====
function normalize_query(string $q): string {
$q = trim($q);
$q = preg_replace('/\s+/u', ' ', $q);
return mb_strtolower($q, 'UTF-8');
}
function now_mysql(): string { return date('Y-m-d H:i:s'); }
function add_days(string $date, int $days): string { return date('Y-m-d H:i:s', strtotime("$date +$days days")); }
// Budowa stabilnego klucza cache
function build_cache_key(?string $q, ?float $lat, ?float $lng): array {
if ($q !== null && $q !== '') {
$raw = $q;
$norm = normalize_query($q);
$mode = 'forward';
} else {
// reverse: lat,lng
$latR = round((float)$lat, 6);
$lngR = round((float)$lng, 6);
$raw = sprintf('%F,%F', $lat, $lng);
$norm = sprintf('latlng:%0.6f,%0.6f', $latR, $lngR);
$mode = 'reverse';
}
return [
'raw' => $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) {
// ===== 4) Fallback: zwróć najświeższe co mamy (też dla reverse) =====
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) {}
http_response_code(502);
echo json_encode(['error' => $e->getMessage()]);
exit;
}