Initial commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user