# Template Struktur Folder & Core Logic PHP Native

## 1. Struktur Folder Lengkap

```text
/ (Root Directory)
├── app/
│   ├── Config/
│   │   ├── database.php        # Konfigurasi koneksi database
│   │   └── config.php          # Konfigurasi global (Base URL, ENV)
│   ├── Controllers/
│   │   ├── AuthController.php  # Login, Register, Logout
│   │   ├── DashboardCtrl.php   # Dashboard (Author/Editor)
│   │   ├── ArticleCtrl.php     # Submission naskah
│   │   └── Controller.php      # Base Controller class
│   ├── Core/
│   │   ├── App.php             # Front Controller / Router dispatcher
│   │   ├── Database.php        # Base Model untuk PDO (Prepared Statement)
│   │   ├── Router.php          # Custom routing system
│   │   └── Security.php        # CSRF Generator & Validator, XSS Cleaner
│   ├── Helpers/
│   │   ├── auth_helper.php     # Cek session login aktif
│   │   └── url_helper.php      # Redirect, base_url generator
│   ├── Models/
│   │   ├── UserModel.php       # Interaksi tabel users
│   │   └── ArticleModel.php    # Interaksi tabel articles
│   └── Services/
│       ├── GeminiAI.php        # Fungsi cURL ke API Gemini
│       └── Mailer.php          # Fungsi kirim email notifikasi
├── public/                     # DOCUMENT_ROOT cPanel (Hanya folder ini yang bisa diakses publik)
│   ├── index.php               # Entry point semua request (Front Controller)
│   ├── .htaccess               # URL Rewriting ke index.php & proteksi direktori
│   ├── assets/
│   │   ├── css/
│   │   ├── js/                 # Alpine.js logic jika ada custom
│   │   └── img/
│   └── uploads/                # File PDF jurnal & Pasfoto
└── views/
    ├── layouts/
    │   ├── header.php          # Tag <head>, sisipan CDN Tailwind & Alpine
    │   ├── footer.php          # Penutup tag, sisipan skrip JS
    │   └── sidebar.php         # Menu navigasi dashboard
    ├── auth/
    │   └── login.php           # Form login
    └── dashboard/
        ├── author.php          # View dashboard untuk Author
        └── editor.php          # View dashboard untuk Editor
```

## 2. Fungsi Setiap Folder
- **app/**: Inti dari aplikasi backend. Segala logika PHP, Controller, Model, dan class core diletakkan di sini.
- **app/Config/**: Menyimpan data sensitif seperti kredensial DB dan variabel environment.
- **app/Core/**: "Mesin" dari MVC ini. Menangani routing, PDO wrapper, dan pengaman aplikasi.
- **app/Helpers/**: Fungsi utilitas sederhana (prosedural) yang sering dipanggil.
- **public/**: Satu-satunya titik akses dari browser. File eksekusi utama adalah `index.php`.
- **public/assets & uploads/**: Direktori penyimpanan file statis dan berkas user.
- **views/**: Lapisan presentasi. Pure HTML dan sedikit PHP untuk _echo_ variabel atau _foreach_ data.

## 3. Contoh Alur Request dari URL ke Controller
1. User mengetik: `https://jurnal.com/dashboard/author`
2. `.htaccess` di `/public` mendeteksi request dan melemparnya ke `public/index.php`.
3. `index.php` memanggil class `Router.php` (Core).
4. `Router` membaca URL, mencocokkannya dengan rute terdaftar, lalu memanggil `DashboardCtrl.php` method `authorIndex()`.
5. `DashboardCtrl` mengecek session via `auth_helper`.
6. `DashboardCtrl` memanggil `ArticleModel` untuk mengambil data artikel milik author tersebut (dari MySQL via PDO).
7. `DashboardCtrl` mengirimkan data array ke `views/dashboard/author.php`.
8. View me-render struktur HTML dengan TailwindCSS + Data PHP dan dikirim sebagai respons HTML ke browser user.

## 4. Contoh Routing Sederhana (`app/Core/Router.php`)

```php
<?php
class Router {
    protected $routes = [];

    public function get($uri, $controller) {
        $this->routes['GET'][$uri] = $controller;
    }

    public function post($uri, $controller) {
        $this->routes['POST'][$uri] = $controller;
    }

    public function route($uri, $method) {
        if (array_key_exists($uri, $this->routes[$method])) {
            list($controllerName, $methodName) = explode('@', $this->routes[$method][$uri]);
            require_once '../app/Controllers/' . $controllerName . '.php';
            $controller = new $controllerName();
            return $controller->$methodName();
        }
        // Handle 404
        die("404 Not Found");
    }
}
```

## 5. Contoh Base Controller (`app/Controllers/Controller.php`)

```php
<?php
class Controller {
    // Fungsi memanggil view
    public function view($view, $data = []) {
        // Ekstrak data menjadi variabel
        extract($data);
        
        // Memastikan file view ada
        if (file_exists('../views/' . $view . '.php')) {
            require_once '../views/' . $view . '.php';
        } else {
            die("View does not exist.");
        }
    }

    // Fungsi utilitas load model
    public function model($model) {
        require_once '../app/Models/' . $model . '.php';
        return new $model();
    }
}
```

## 6. Contoh Base Model dengan PDO (`app/Core/Database.php`)

```php
<?php
class Database {
    private $host = "localhost";
    private $user = "db_user";
    private $pass = "db_password";
    private $dbname = "journal_db";

    protected $dbh; // Database Handler
    protected $stmt;

    public function __construct() {
        $dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
        $options = [
            PDO::ATTR_PERSISTENT => true,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Lempar exception jika gagal
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC // Default ambil sebagai Array Assoc
        ];

        try {
            $this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
        } catch (PDOException $e) {
            die("Connection Error: " . $e->getMessage());
        }
    }

    // Wrapper untuk Prepared Statements
    public function query($query) {
        $this->stmt = $this->dbh->prepare($query);
    }

    public function bind($param, $value, $type = null) {
        if (is_null($type)) {
            switch (true) {
                case is_int($value): $type = PDO::PARAM_INT; break;
                case is_bool($value): $type = PDO::PARAM_BOOL; break;
                case is_null($value): $type = PDO::PARAM_NULL; break;
                default: $type = PDO::PARAM_STR;
            }
        }
        $this->stmt->bindValue($param, $value, $type);
    }

    public function execute() {
        return $this->stmt->execute();
    }

    public function resultSet() {
        $this->execute();
        return $this->stmt->fetchAll();
    }

    public function single() {
        $this->execute();
        return $this->stmt->fetch();
    }
}
```

## 7. Contoh View Layout Utama (`views/layouts/header.php`)

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= $title ?? 'NexusScholar Journal' ?></title>
    <!-- TailwindCSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Alpine.js CDN -->
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
    <!-- Chart.js CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="bg-gray-50 text-gray-800 font-sans antialiased">
    <!-- Navbar & Sidebar structure here -->
```

## 8. Catatan Keamanan Khusus untuk cPanel / Shared Hosting

1. **DOCUMENT_ROOT Protection**: 
   Arahkan _domain document root_ (di menu Addon Domains cPanel) langsung ke folder `/public`, BUKAN ke root project `/`. Ini memastikan folder `/app`, `/views`, dan `/config` mustahil diakses langsung via URL browser.
2. **.htaccess Protection**:
   Jika `DOCUMENT_ROOT` tidak bisa diubah (karena primary domain `public_html`), buat file `.htaccess` di root project yang me-rewrite semua request ke `public/index.php`, dan buat `.htaccess` dengan tulisan `Deny from all` di dalam folder `/app` dan `/views`.
3. **Mencegah SQL Injection**:
   Metode PDO pada contoh di poin 6 **sudah sangat aman** dari SQL Injection, selama Anda selalu menggunakan `$this->bind()` dan JANGAN PERNAH menyisipkan variabel langsung ke dalam string query SQL.
4. **Mencegah Akses File Langsung**:
   Tambahkan kode `<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>` di baris pertama setiap file _Controller_ dan _Model_ (Optional).
5. **Session Hijacking**:
   Gunakan `session_regenerate_id(true);` di dalam fungsi login yang berhasil untuk menghindari pencurian ID sesi lama.
6. **Upload PDF Aman**:
   Di `upload/`, buat `.htaccess` khusus agar tidak ada file PHP yang bisa dieksekusi di dalam folder itu (misal: `php_flag engine off` atau `AddType text/plain .php`). Validasi MIME type file secara ketat di backend, bukan hanya mengecek ekstensi `.pdf`.
