Form yang rapi hanyalah separuh cerita. Di sisi server, PHP menerima kiriman itu sebagai data mentah yang harus dibaca dengan disiplin: field bisa saja tidak hadir, bisa berisi spasi berlebih, dan pada titik tertentu datanya bahkan bertipe array. Selama chapter ini kita membangun pola membaca input yang aman untuk semua kasus tersebut — mulai dari field teks sederhana, penanganan kunci yang absen, sampai grup checkbox yang dikirim sebagai array. Semua pola diuji dengan array $_POST buatan supaya bisa dijalankan langsung dari CLI.

Dari Atribut name ke Kunci $_POST #

Aturan dasarnya satu kalimat: setiap atribut name pada form menjadi kunci di $_POST, dan nilai field menjadi nilainya. Atribut id tidak berperan sama sekali di sisi PHP — id hanya dipakai browser untuk menghubungkan label dengan input. Perhatikan form pendaftaran berikut:

html
<form action="/simpan.php" method="post">
    <label for="nama">Nama lengkap</label>
    <input type="text" id="nama" name="nama" value="Budi">

    <label for="email">Email</label>
    <input type="email" id="email" name="email" value="[email protected]">

    <button type="submit">Kirim</button>
</form>

Handler yang membaca kiriman form di atas cukup mengakses kedua kunci tersebut. Karena CLI tidak menerima request HTTP, contoh-contoh di chapter ini mengoper array $_POST buatan sebagai parameter — persis seperti pola pada Chapter GET vs POST :

php
<?php
function bacaPendaftar(array $post): string
{
    $nama = $post['nama'] ?? '';
    $email = $post['email'] ?? '';

    return "Nama: $nama, Email: $email";
}

echo bacaPendaftar(['nama' => 'Budi', 'email' => '[email protected]']), "\n";

Output yang Dihasilkan:

text
Nama: Budi, Email: [email protected]

Fungsi bacaPendaftar() membaca dua field dengan operator ?? yang menyediakan string kosong sebagai nilai cadangan. Satu fakta penting yang perlu diingat sejak awal: semua nilai form tiba sebagai string. Nilai angka seperti umur "25" tetap berupa string dan baru menjadi integer setelah di-cast atau melewati validasi — perilaku yang sudah kita buktikan pada Chapter PHP Superglobals .

Kunci yang Tidak Hadir #

Field bisa saja absen dari kiriman: form lama yang belum diperbarui, JavaScript yang menonaktifkan field, atau permintaan buatan yang sengaja melewatkan field. Membaca kunci yang absen secara langsung memicu peringatan:

php
<?php
$_POST['nama'] = 'Budi';

echo 'Halo, ' . $_POST['nama'] . "\n";
echo 'Kota: ' . $_POST['kota'] . "\n";

Output yang Dihasilkan:

text
Halo, Budi

Warning: Undefined array key "kota" in /home/z/my-project/php-course/content/php/form-handling-dan-validation/membaca-form-input/.verify_tmp.php on line 5
Kota:

Baris terakhir memicu Warning: Undefined array key "kota" karena kunci tersebut tidak pernah diisi. Nilai kembalinya tetap null dan program berlanjut — itulah mengapa baris Kota: masih tercetak tanpa isi. Peringatan semacam ini bukan sekadar derau di layar; setiap kemunculannya menandakan handler membaca field yang belum dipastikan keberadaannya. Versi yang benar memakai operator ??:

php
<?php
$_POST['nama'] = 'Budi';

$kota = $_POST['kota'] ?? 'Jakarta';

echo 'Halo, ' . $_POST['nama'] . "\n";
echo 'Kota: ' . $kota . "\n";

Output yang Dihasilkan:

text
Halo, Budi
Kota: Jakarta

Sekarang kunci yang absen dialihkan ke nilai cadangan 'Jakarta' tanpa peringatan apa pun. Nilai cadangan seperti ini juga berfungsi sebagai pilihan bawaan yang masuk akal — kota pengiriman bawaan, halaman pertama pada paginasi, dan sebagainya.

isset atau array_key_exists #

PHP menyediakan dua fungsi pemeriksa keberadaan kunci yang mirip tetapi tidak identik: isset() dan array_key_exists(). Bedanya menyatu pada satu kasus — nilai null:

php
<?php
$_POST['nama'] = 'Budi';
$_POST['email'] = null;

var_dump(isset($_POST['nama']));
var_dump(isset($_POST['email']));
var_dump(array_key_exists('email', $_POST));
var_dump(array_key_exists('telepon', $_POST));

Output yang Dihasilkan:

text
bool(true)
bool(false)
bool(true)
bool(false)

isset() mengembalikan false untuk kunci email meskipun kunci tersebut ada, karena nilainya null. array_key_exists() hanya peduli keberadaan kunci, sehingga mengembalikan true untuk email dan false untuk telepon yang memang tidak ada. Untuk kebutuhan form sehari-hari, operator ?? adalah pilihan paling ringkas karena sekaligus memberi nilai cadangan. Gunakan isset() saat makna yang kita maksud memang “ada dan tidak null”, dan array_key_exists() saat nilai null pun tetap dianggap kiriman yang sah.

Merapikan Input dan Memberi Nilai Bawaan #

Pengguna sering menyeret spasi tak sengaja ke dalam field — hasil salin-tempel, kebiasaan menekan spasi sebelum mengisi, atau spasi di akhir kalimat. Fungsi trim() merapikan karakter spasi, tab, dan baris baru di kedua ujung string. Pola lengkapnya bisa dibungkus menjadi satu helper:

php
<?php
function bacaField(array $post, string $kunci, string $bawaan = ''): string
{
    $nilai = $post[$kunci] ?? $bawaan;

    if (!is_string($nilai)) {
        return $bawaan;
    }

    return trim($nilai);
}

$_POST['nama'] = "  Budi  \n";

echo '[' . bacaField($_POST, 'nama') . "]\n";
echo '[' . bacaField($_POST, 'kota', 'Jakarta') . "]\n";

Output yang Dihasilkan:

text
[Budi]
[Jakarta]

Helper bacaField() mengerjakan tiga hal sekaligus: memberi nilai bawaan lewat ??, memeriksa is_string() sebagai pengaman, lalu trim(). Pemeriksaan is_string() tampak berlebihan, tetapi ingat bahwa kiriman form sepenuhnya kendali pengirim — permintaan buatan bisa mengirim nama[] (array) padahal kode kita mengharapkan string, dan pengaman ini mengembalikan nilai bawaan alih-alih memicu error. Sejak PHP 8.4 tersedia pula mb_trim() yang mengenali karakter spasi Unicode seperti non-breaking space — berguna saat form menerima teks dari beragam sumber.

Input Bertipe Array #

Menambahkan tanda kurung siku pada atribut name membuat PHP mengumpulkan field-field tersebut menjadi satu array. Pola ini paling terlihat pada grup checkbox:

html
<form action="/hobi.php" method="post">
    <label><input type="checkbox" name="hobi[]" value="membaca"> Membaca</label>
    <label><input type="checkbox" name="hobi[]" value="ngoding"> Ngoding</label>
    <label><input type="checkbox" name="hobi[]" value="bersepeda"> Bersepeda</label>
    <button type="submit">Simpan</button>
</form>

Bila dua checkbox pertama dicentang, $_POST['hobi'] berisi array ['membaca', 'ngoding']. Pembacaannya mengikuti bentuk datanya:

php
<?php
function bacaDaftar(array $post, string $kunci): array
{
    $nilai = $post[$kunci] ?? [];

    if (!is_array($nilai)) {
        return [];
    }

    return array_map('trim', $nilai);
}

$_POST['hobi'] = [' membaca ', 'bersepeda'];

print_r(bacaDaftar($_POST, 'hobi'));
print_r(bacaDaftar($_POST, 'olahraga'));

Output yang Dihasilkan:

text
Array
(
    [0] => membaca
    [1] => bersepeda
)
Array
(
)

Helper bacaDaftar() memberi array kosong sebagai nilai bawaan, berjaga dengan is_array() terhadap kiriman palsu berbentuk string, lalu merapikan tiap elemen dengan array_map('trim', ...). Pemanggilan kedua membuktikan field yang absen tetap menghasilkan array kosong — bukan null — sehingga pemanggil selalu aman memanggil count() atau foreach.

Textarea, Radio, dan Password #

Tiga jenis input lain punya kebiasaan masing-masing. textarea tidak punya atribut value — teks bawaannya ditulis di antara tag pembuka dan penutup, dan baris baru di dalamnya adalah bagian sah dari isi:

html
<form action="/kontak.php" method="post">
    <label><input type="radio" name="paket" value="reguler" checked> Reguler</label>
    <label><input type="radio" name="paket" value="kilat"> Kilat</label>

    <label for="pesan">Pesan</label>
    <textarea id="pesan" name="pesan" rows="4">Tulis pesan Anda di sini...</textarea>

    <label for="sandi">Kata sandi</label>
    <input type="password" id="sandi" name="sandi">

    <button type="submit">Kirim</button>
</form>

Radio button yang memakai name sama membentuk satu grup: tepat satu nilai terkirim — nilai radio yang terpilih. Checkbox checked pada radio pertama menandanya sebagai pilihan bawaan. Pembacaan ketiganya di sisi PHP tidak berbeda dari field biasa:

php
<?php
function bacaKontak(array $post): string
{
    $paket = $post['paket'] ?? 'reguler';
    $pesan = trim($post['pesan'] ?? '');

    return "Paket $paket, pesan: $pesan";
}

echo bacaKontak(['paket' => 'kilat', 'pesan' => " Tolong cepat ya \n"]), "\n";
echo bacaKontak(['pesan' => 'Halo']), "\n";

Output yang Dihasilkan:

text
Paket kilat, pesan: Tolong cepat ya
Paket reguler, pesan: Halo

Pemanggilan kedua tidak menyertakan kunci paket — kondisi saat tidak ada satu pun radio terpilih — dan nilai bawaan 'reguler' mengambil alih. Untuk textarea, trim() hanya merapikan ujung-ujungnya; baris baru di tengah teks adalah isi yang sah dan dibiarkan apa adanya.

Form Pencarian dengan $_GET #

Form pencarian adalah pengguna klasik method GET — hasilnya layak dibagikan sebagai tautan, seperti kita bahas pada Chapter GET vs POST . Parameternya tiba di $_GET dengan struktur yang sama persis:

html
<form action="/cari.php" method="get">
    <label for="kata">Kata kunci</label>
    <input type="text" id="kata" name="kata">

    <label for="halaman">Halaman</label>
    <input type="number" id="halaman" name="halaman" value="1">

    <button type="submit">Cari</button>
</form>

Karena nilai selalu string, parameter angka perlu di-cast sebelum dipakai sebagai angka:

php
<?php
function bacaParameterCari(array $get): string
{
    $kata = trim($get['kata'] ?? '');
    $halaman = (int) ($get['halaman'] ?? 1);

    if ($kata === '') {
        return 'Menampilkan semua produk';
    }

    return "Mencari '$kata', halaman $halaman";
}

echo bacaParameterCari(['kata' => 'kopi gayo', 'halaman' => '2']), "\n";
echo bacaParameterCari([]), "\n";

Output yang Dihasilkan:

text
Mencari 'kopi gayo', halaman 2
Menampilkan semua produk

Cast (int) mengubah string '2' menjadi integer 2. Hati-hati: cast yang sama pada 'abc' menghasilkan 0, dan pada 'halaman=2korupted' menghasilkan 2 — cast membaca awal angka lalu berhenti. Untuk paginasi yang ketat, validasi dengan filter_var() dan FILTER_VALIDATE_INT jauh lebih disiplin; alat lengkapnya kita bangun pada Chapter Validasi Server-Side .

Berkas contoh.php pada bagian bawah halaman merangkum seluruh pola chapter ini — bacaField(), bacaDaftar(), normalisasi email — dalam satu program utuh yang dapat dijalankan langsung.

Ringkasan #

Membaca input form dengan aman berdiri di atas tiga kebiasaan: akses selalu lewat ?? dengan nilai bawaan yang masuk akal, rapikan ujung string dengan trim(), dan hormati bentuk data — field bertipe name="[]" dibaca sebagai array dengan pengaman is_array(). Kita juga membedakan isset() dari array_key_exists() untuk kasus nilai null, serta mencatat bahwa radio, textarea, dan password tiba sebagai string biasa. Pola pembacaan ini baru setengah jalan — data yang berhasil dibaca masih perlu diperiksa kesahannya sebelum dipercaya, dan itulah fokus Chapter Validasi Server-Side .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.php

Unduh berkas
php
<?php
// contoh.php — merangkum pola membaca input form.
// CLI tidak menerima request, jadi $_POST diisi manual sebagai simulasi.

/**
 * Membaca field string dengan trim dan nilai bawaan.
 */
function bacaField(array $post, string $kunci, string $bawaan = ''): string
{
    $nilai = $post[$kunci] ?? $bawaan;

    if (!is_string($nilai)) {
        return $bawaan;
    }

    return trim($nilai);
}

/**
 * Membaca field bertipe array (name="hobi[]").
 */
function bacaDaftar(array $post, string $kunci): array
{
    $nilai = $post[$kunci] ?? [];

    if (!is_array($nilai)) {
        return [];
    }

    return array_map('trim', $nilai);
}

// Simulasi kiriman form pendaftaran
$_POST = [
    'nama' => '  Budi  ',
    'email' => '[email protected]',
    'hobi' => [' membaca ', 'ngoding'],
];

$nama = bacaField($_POST, 'nama');
$email = mb_strtolower(bacaField($_POST, 'email'));
$hobi = bacaDaftar($_POST, 'hobi');

echo "Nama      : $nama\n";
echo "Email     : $email\n";
echo 'Jumlah hobi: ' . count($hobi) . "\n";

foreach ($hobi as $i => $satuHobi) {
    echo 'Hobi ' . ($i + 1) . "   : $satuHobi\n";
}