1. Bagaimana Permintaan HTTP Masuk ke Skrip PHP Anda?
Ketika peramban mengirimkan permintaan HTTP ke server web (Nginx) dan diteruskan ke PHP-FPM, PHP menyiapkan lingkungan khusus untuk permintaan tersebut dan mengisi sejumlah array Superglobals tentang permintaan yang baru saja tiba:
| Superglobal | Apa Isinya? | Catatan Keamanan Penting |
|---|---|---|
$_SERVER | Metode HTTP (REQUEST_METHOD), jalur URI (REQUEST_URI), alamat IP (REMOTE_ADDR), dan header HTTP (HTTP_*). | Semua kunci yang diawali HTTP_* (seperti HTTP_HOST, HTTP_USER_AGENT, HTTP_REFERER, HTTP_X_FORWARDED_FOR) dikirim oleh klien dan bisa dipalsukan oleh penyerang! |
$_GET | Parameter query string dari URL (?halaman=2&kategori=php). | Semua nilai di dalam $_GET bertipe string (atau array jika ditulis ?tag[]=a&tag[]=b)! |
$_POST | Data tubuh permintaan dari formulir application/x-www-form-urlencoded atau multipart/form-data. | Tidak terisi jika klien mengirim body berformat application/json. |
$_COOKIE | Nilai cookie yang dikirimkan oleh peramban melalui header Cookie. | Dapat dimodifikasi oleh pengguna di peramban; selalu validasi! |
$_FILES | Metadata berkas yang diunggah lewat multipart/form-data. | Wajib divalidasi dengan finfo dan move_uploaded_file(). |
$_GET atau $_POST Bisa Berupa array!Banyak pengembang mengira $_GET['pencarian'] selalu berupa string lalu langsung menulis trim($_GET['pencarian']). Apa yang terjadi jika pengguna jahil memanggil URL /cari?pencarian[]=halo (dengan tanda kurung siku [])? PHP akan mengubah $_GET['pencarian'] menjadi sebuah array, sehingga trim($_GET['pencarian']) langsung melempar TypeError: trim(): Argument #1 ($string) must be of type string, array given! Solusi: Selalu periksa is_string($nilai) atau gunakan filter_input()!
<?php
declare(strict_types=1);
// AMAN: Memastikan nilai parameter benar-benar bertipe string sebelum di-trim
function ambilQueryString(string $kunci, string $bawaan = ''): string
{
$nilai = $_GET[$kunci] ?? null;
return is_string($nilai) ? trim($nilai) : $bawaan;
}
2. Validasi Ketat dengan filter_input() dan filter_var()
Ekstensi bawaan filter menyediakan validator standar yang aman terhadap serangan tipe data:
<?php
declare(strict_types=1);
// 1. Memvalidasi bilangan bulat dengan rentang minimum dan maksimum yang tegas
$idModul = filter_input(
INPUT_GET,
'id',
FILTER_VALIDATE_INT,
[
'options' => [
'min_range' => 1,
'max_range' => 10_000,
],
],
);
// filter_input mengembalikan:
// - null jika parameter 'id' tidak ada sama sekali di $_GET
// - false jika 'id' ada, tetapi bukan angka bulat antara 1..10000 (mis. "abc" atau "-5" atau array!)
if (!is_int($idModul)) {
http_response_code(400);
exit('Parameter ID modul harus berupa angka bulat positif.');
}
// 2. Memvalidasi alamat surel dan alamat IP
$surel = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
$ipKlien = filter_var($_SERVER['REMOTE_ADDR'] ?? '', FILTER_VALIDATE_IP);
3. Membaca Permintaan JSON (php://input) dan Mengenal request_parse_body() (PHP 8.4)
- Untuk Permintaan
application/json: Gunakanfile_get_contents('php://input')danjson_decode(..., associative: true, flags: JSON_THROW_ON_ERROR): ```php <?php
declare(strict_types=1);
function ambilPayloadJson(): array { $tipeKonten = $_SERVER['CONTENT_TYPE'] ?? ''; if (!str_starts_with(strtolower($tipeKonten), 'application/json')) { throw new InvalidArgumentException('Header Content-Type harus application/json.'); }
$bodyMentah = (string) file_get_contents('php://input'); $data = json_decode($bodyMentah, associative: true, depth: 32, flags: JSON_THROW_ON_ERROR);
if (!is_array($data)) { throw new InvalidArgumentException('Payload JSON harus berupa objek atau array.'); }
return $data; } ```
- Untuk Permintaan
PUT/PATCHMultipart (request_parse_body()— Sejak PHP 8.4): Secara historis, PHP hanya menguraimultipart/form-datasecara otomatis untuk metodePOST. Sejak PHP 8.4, tersedia fungsi bawaan[$post, $files] = request_parse_body();untuk mengurai bodymultipart/form-data(danapplication/x-www-form-urlencoded) pada metode HTTP selainPOST, misalnyaPUTatauPATCH. Body hanya dapat dibaca sekali: jikaphp://inputsudah dibaca lebih dulu,request_parse_body()mengembalikan data kosong.
4. Mengurai dan Memodifikasi URL dengan Ekstensi Uri Baru (Sejak PHP 8.5)
Fungsi lama parse_url() di PHP memiliki perilaku yang tidak konsisten terhadap standar RFC modern. Sejak PHP 8.5, ekstensi bawaan Uri menyediakan dua kelas imutabel standar:
Uri\Rfc3986\Uri: Untuk URI standar RFC 3986.Uri\WhatWg\Url: Untuk URL yang mematuhi spesifikasi peramban WHATWG URL.
<?php
declare(strict_types=1);
use Uri\Rfc3986\Uri;
// Mengurai dan membangun URL pengalihan (redirect) yang aman di PHP 8.5:
$uriTujuan = new Uri('https://kodeloka.asadinedu.com/kelas/php');
// Objek Uri bersifat immutable; metode withQuery() menghasilkan instans Uri baru:
$uriDenganPesan = $uriTujuan->withQuery('status=berhasil&modul=10');
echo $uriDenganPesan->getHost(); // "kodeloka.asadinedu.com"
echo $uriDenganPesan->toString(); // "https://kodeloka.asadinedu.com/kelas/php?status=berhasil&modul=10"
Latihan Praktik: Endpoint Pemroses Pendaftaran Kelas (Form & JSON)
Buatlah berkas proses-request.php:
- Periksa
$_SERVER['REQUEST_METHOD'] === 'POST'(jika bukanPOST, kembalikan status405 Method Not Allowedbeserta headerAllow: POST). - Dukung penerimaan data baik dari formulir biasa (
$_POST) maupun dari body JSON (php://input) berdasarkan pemeriksaan$_SERVER['CONTENT_TYPE']. - Validasi
nama(string 2–80 karakter),email(FILTER_VALIDATE_EMAIL), danid_kelas(FILTER_VALIDATE_INTminimal 1).