116 lines
3.7 KiB
PHP
116 lines
3.7 KiB
PHP
<?php
|
|
// core/Controller.php
|
|
|
|
class Controller {
|
|
protected Database $db;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
}
|
|
|
|
protected function view(string $view, array $data = [], ?string $layout = 'main'): void {
|
|
extract($data);
|
|
$viewFile = APP_ROOT . '/app/views/' . str_replace('.', '/', $view) . '.php';
|
|
|
|
if (!file_exists($viewFile)) {
|
|
die("View not found: $viewFile");
|
|
}
|
|
|
|
if ($layout) {
|
|
$layoutFile = APP_ROOT . '/app/views/layouts/' . $layout . '.php';
|
|
if (file_exists($layoutFile)) {
|
|
// Capture view content
|
|
ob_start();
|
|
require $viewFile;
|
|
$content = ob_get_clean();
|
|
require $layoutFile;
|
|
return;
|
|
}
|
|
}
|
|
|
|
require $viewFile;
|
|
}
|
|
|
|
protected function json(mixed $data, int $code = 200): void {
|
|
http_response_code($code);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
exit;
|
|
}
|
|
|
|
protected function redirect(string $url): void {
|
|
// Flush session ke disk sebelum redirect agar data tidak hilang
|
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
|
session_write_close();
|
|
}
|
|
header("Location: $url");
|
|
exit;
|
|
}
|
|
|
|
protected function back(): void {
|
|
$referer = $_SERVER['HTTP_REFERER'] ?? APP_URL;
|
|
$this->redirect($referer);
|
|
}
|
|
|
|
protected function csrf(): string {
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
return $_SESSION['csrf_token'];
|
|
}
|
|
|
|
protected function verifyCsrf(): bool {
|
|
// Pastikan session aktif sebelum membaca token
|
|
Auth::init();
|
|
$token = $_POST['_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
|
return hash_equals($_SESSION['csrf_token'] ?? '', $token);
|
|
}
|
|
|
|
protected function input(string $key, mixed $default = null): mixed {
|
|
return $_POST[$key] ?? $_GET[$key] ?? $default;
|
|
}
|
|
|
|
protected function sanitize(string $value): string {
|
|
return htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
protected function flash(string $type, string $message, array $extra = []): void {
|
|
$_SESSION['flash'] = ['type' => $type, 'message' => $message] + $extra;
|
|
}
|
|
|
|
protected function getFlash(): ?array {
|
|
if (isset($_SESSION['flash'])) {
|
|
$flash = $_SESSION['flash'];
|
|
unset($_SESSION['flash']);
|
|
return $flash;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function uploadFile(array $file, string $dir = 'uploads'): ?string {
|
|
if ($file['error'] !== UPLOAD_ERR_OK) return null;
|
|
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
$allow = ['jpg', 'jpeg', 'png', 'webp'];
|
|
if (!in_array($ext, $allow)) return null;
|
|
|
|
$maxSize = 5 * 1024 * 1024; // 5MB
|
|
if ($file['size'] > $maxSize) return null;
|
|
|
|
$filename = uniqid('img_', true) . '.' . $ext;
|
|
$destDir = APP_ROOT . '/public/images/' . $dir . '/';
|
|
if (!is_dir($destDir)) mkdir($destDir, 0755, true);
|
|
|
|
if (move_uploaded_file($file['tmp_name'], $destDir . $filename)) {
|
|
return $filename;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected function paginate(int $total, int $perPage = 20): array {
|
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
|
$offset = ($page - 1) * $perPage;
|
|
$pages = (int)ceil($total / $perPage);
|
|
return compact('page', 'offset', 'perPage', 'pages', 'total');
|
|
}
|
|
} |