Files
2026-06-10 19:48:00 +07:00

172 lines
6.3 KiB
PHP

<?php
// includes/helpers.php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../config/database.php';
/* ============================================================
HTTP Response helpers
============================================================ */
function jsonSuccess($data = [], string $message = 'OK', int $code = 200): void {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => true, 'message' => $message, 'data' => $data], JSON_UNESCAPED_UNICODE);
exit;
}
function jsonError(string $message = 'Error', int $code = 400, array $errors = []): void {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['success' => false, 'message' => $message, 'errors' => $errors], JSON_UNESCAPED_UNICODE);
exit;
}
/* ============================================================
Input sanitization
============================================================ */
function sanitize(string $val): string {
return htmlspecialchars(strip_tags(trim($val)), ENT_QUOTES, 'UTF-8');
}
function getBody(): array {
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
return is_array($data) ? $data : [];
}
function required(array $data, array $fields): array {
$errors = [];
foreach ($fields as $f) {
if (!isset($data[$f]) || trim((string)$data[$f]) === '') {
$errors[] = "Field '$f' wajib diisi";
}
}
return $errors;
}
/* ============================================================
Session / Auth
============================================================ */
function startSession(): void {
if (session_status() === PHP_SESSION_NONE) {
session_name(SESSION_NAME);
session_start();
}
}
function currentUser(): ?array {
startSession();
return $_SESSION['user'] ?? null;
}
function requireAuth(array $allowedRoles = []): array {
$user = currentUser();
if (!$user) jsonError('Unauthorized - silakan login', 401);
if (!empty($allowedRoles) && !in_array($user['id_role'], $allowedRoles)) {
jsonError('Forbidden - hak akses tidak cukup', 403);
}
return $user;
}
function login(string $username, string $password): array|false {
$db = getDB();
$stmt = $db->prepare(
"SELECT u.*, r.nama_role FROM user u JOIN role r ON u.id_role = r.id
WHERE u.username = ? AND u.aktif = 1 LIMIT 1"
);
$stmt->execute([$username]);
$user = $stmt->fetch();
if (!$user || !password_verify($password, $user['password_hash'])) return false;
// Update last_login
$db->prepare("UPDATE user SET last_login = NOW() WHERE id = ?")->execute([$user['id']]);
unset($user['password_hash']);
return $user;
}
/* ============================================================
Activity Log
============================================================ */
function logActivity(string $aksi, string $tabel = '', string $idRecord = '',
array $dataLama = [], array $dataBaru = []): void {
try {
$user = currentUser();
$db = getDB();
$db->prepare(
"INSERT INTO log_aktivitas (id_user, aksi, tabel, id_record, data_lama, data_baru, ip_address)
VALUES (?, ?, ?, ?, ?, ?, ?)"
)->execute([
$user['id'] ?? null,
$aksi, $tabel, $idRecord,
$dataLama ? json_encode($dataLama, JSON_UNESCAPED_UNICODE) : null,
$dataBaru ? json_encode($dataBaru, JSON_UNESCAPED_UNICODE) : null,
$_SERVER['REMOTE_ADDR'] ?? null,
]);
} catch (Throwable $e) { /* silent */ }
}
/* ============================================================
Haversine distance (km)
============================================================ */
function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float {
$R = 6371;
$dLat = deg2rad($lat2 - $lat1);
$dLng = deg2rad($lng2 - $lng1);
$a = sin($dLat/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng/2)**2;
return $R * 2 * asin(sqrt($a));
}
/* ============================================================
Pagination helper
============================================================ */
function paginate(array $data, int $page, int $perPage = 20): array {
$total = count($data);
$pages = (int)ceil($total / $perPage);
$offset = ($page - 1) * $perPage;
return [
'items' => array_slice($data, $offset, $perPage),
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'pages' => $pages,
];
}
/* ============================================================
Validasi umur vs pendidikan
============================================================ */
function validasiPendidikan(int $umur, string $pendidikanTerakhir, string $statusPendidikan): array {
$warnings = [];
if ($umur >= 7 && $umur <= 15) {
if ($statusPendidikan === 'tidak_sekolah') {
$warnings[] = "Anak usia $umur tahun seharusnya masih sekolah (wajib belajar 9 tahun)";
}
if (!in_array($pendidikanTerakhir, ['SD','SMP','tidak_sekolah'])) {
$warnings[] = "Pendidikan terakhir tidak sesuai untuk usia $umur tahun";
}
} elseif ($umur >= 16 && $umur <= 18) {
if ($statusPendidikan === 'tidak_sekolah' && in_array($pendidikanTerakhir, ['tidak_sekolah','SD'])) {
$warnings[] = "Remaja usia $umur tahun disarankan melanjutkan ke SMA/SMK";
}
}
return $warnings;
}
/* ============================================================
Upload foto helper
============================================================ */
function uploadFoto(array $file, string $subfolder = 'foto_laporan'): string|false {
$allowed = ['image/jpeg','image/png','image/webp'];
if (!in_array($file['type'], $allowed)) return false;
if ($file['size'] > 5 * 1024 * 1024) return false;
$dir = UPLOAD_DIR . $subfolder . '/';
if (!is_dir($dir)) mkdir($dir, 0755, true);
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = uniqid('img_', true) . '.' . strtolower($ext);
$dest = $dir . $filename;
if (!move_uploaded_file($file['tmp_name'], $dest)) return false;
return $subfolder . '/' . $filename;
}