Email dan URL adalah dua format input yang paling sering diminta form — dan dua format yang paling sering salah divalidasi. Regex buatan sendiri untuk email terkenal hampir selalu keliru, sementara pemeriksaan yang terlalu longgar membiarkan sampah masuk ke database. PHP menjawab kebutuhan ini dengan dua validator siap pakai di extension filter: FILTER_VALIDATE_EMAIL dan FILTER_VALIDATE_URL. Pada chapter ini kita uji keduanya dengan contoh valid dan invalid, membedah batas-batasnya (email Unicode, panjang alamat, scheme yang tak dikenal), lalu belajar menyusun beberapa validator menjadi satu pemeriksaan berlapis.
Validasi Email dengan filter_var #
Fungsi filter_var() dengan FILTER_VALIDATE_EMAIL mengembalikan email itu sendiri bila sah, atau false bila tidak — bukan true/false seperti dugaan banyak orang. Karena itu pembanding yang tepat adalah === false atau !== false. Mari kita uji delapan alamat dengan berbagai tingkat kerusakan:
<?php
$daftarEmail = [
'[email protected]',
'[email protected]',
'[email protected]',
'budi',
'budi@',
'@example.com',
'budi@@example.com',
'budi@example',
];
foreach ($daftarEmail as $email) {
$hasil = filter_var($email, FILTER_VALIDATE_EMAIL);
$status = $hasil === false ? 'TOLAK' : 'SAH ';
echo "$status $email\n";
}Output yang Dihasilkan:
SAH [email protected]
SAH [email protected]
SAH [email protected]
TOLAK budi
TOLAK budi@
TOLAK @example.com
TOLAK budi@@example.com
TOLAK budi@exampleTiga alamat pertama sah: titik di dalam nama diperbolehkan, domain berakhiran ganda seperti .co.id tidak masalah, dan budi+toko sah karena tanda plus adalah mekanisme nyata (plus addressing) untuk menyaring email di banyak penyedia. Lima sisanya ditolak dengan alasan yang bisa ditebak: tanpa domain, tanpa nama lokal, tanpa @, @ ganda, dan domain tanpa titik. Perhatikan kasus terakhir — budi@example ditolak karena filter menuntut domain berbentuk umum; alamat teoretis seperti budi@localhost memang sah menurut RFC, tetapi hampir pasti merupakan salah ketik di konteks form publik.
Email Unicode dan Batas Panjang #
Filter ini punya dua sifat yang jarang disadari. Pertama, secara bawaan filter hanya menerima karakter ASCII — email internasional seperti bü[email protected] ditolak:
<?php
$email = 'bü[email protected]';
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE));Output yang Dihasilkan:
bool(false)
string(17) "bü[email protected]"Pemanggilan pertama mengembalikan false; pemanggilan kedua — dengan flag FILTER_FLAG_EMAIL_UNICODE — mengembalikan alamatnya karena flag tersebut mengizinkan karakter non-ASCII pada bagian lokal (sebelum @). Apakah form Anda perlu menerima email Unicode adalah keputusan bisnis: banyak sistem lawas belum siap, jadi tolak dengan pesan yang jelas atau sediakan opsi alamat ASCII.
Sifat kedua menyenangkan: filter ini sudah menerapkan batas panjang RFC — bagian lokal maksimal 64 karakter dan total alamat maksimal 254. Kami memverifikasi bahwa bagian lokal sepanjang 64 karakter lolos sedangkan 65 karakter ditolak, tanpa perlu pemeriksaan tambahan dari kita. Pemeriksaan panjang sendiri tetap relevan untuk aturan bisnis yang lebih ketat daripada RFC, misalnya kolom database VARCHAR(50):
<?php
function validasiEmail(string $email): array
{
$errors = [];
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors[] = 'Format email tidak sah.';
}
if (mb_strlen($email) > 50) {
$errors[] = 'Email maksimal 50 karakter.';
}
return $errors;
}
print_r(validasiEmail('[email protected]'));
print_r(validasiEmail('budi@'));
print_r(validasiEmail(str_repeat('ab', 24) . '@example.com'));Output yang Dihasilkan:
Array
(
)
Array
(
[0] => Format email tidak sah.
)
Array
(
[0] => Email maksimal 50 karakter.
)Alamat pertama lolos tanpa masalah. Alamat kedua hanya melanggar format. Alamat ketiga — 60 karakter dengan bagian lokal 48 karakter — lolos format RFC namun melanggar aturan bisnis 50 karakter, sehingga hanya mengumpulkan error panjang. Susunan seperti ini mengikuti pola fungsi validasi dari Chapter Validasi Server-Side : beberapa aturan, satu array error.
Memeriksa Domain dengan checkdnsrr #
Format yang sah belum menjamin domainnya ada: [email protected] lolos FILTER_VALIDATE_EMAIL padahal domainnya tidak pernah terdaftar. Langkah lanjutannya adalah bertanya ke DNS lewat checkdnsrr(), yang memeriksa keberadaan record — MX (mail server) atau A (alamat) — pada sebuah domain:
<?php
$email = '[email protected]';
$domain = substr((string) strrchr($email, '@'), 1);
// Memerlukan koneksi jaringan — hasil tergantung DNS
var_dump(checkdnsrr($domain, 'MX'));php -r 'var_dump(checkdnsrr("example.com", "MX"));'
php -r 'var_dump(checkdnsrr("domain-fiktif-xyz.com", "MX"));'Pada pengujian kami, checkdnsrr('example.com', 'MX') mengembalikan true karena domain tersebut memiliki record MX, sedangkan domain fiktif mengembalikan false. Tiga hal perlu diperhitungkan sebelum memakai fungsi ini pada form publik. Pertama, pemeriksaan butuh koneksi jaringan — form bisa terasa lambat dan berperilaku beda saat DNS bermasalah. Kedua, hasil DNS berubah antar waktu dan wilayah. Ketiga, domain yang ada belum berarti kotak posnya ada: satu-satunya pembuktian sejati bahwa sebuah email dapat menerima surat adalah mengirim email konfirmasi dengan tautan aktivasi — pendekatan yang dipakai hampir semua aplikasi serius.
Validasi URL dengan filter_var #
Saudara kembarannya bekerja dengan cara sama: FILTER_VALIDATE_URL mengembalikan URL yang sah atau false. Mari kita uji lima kandidat:
<?php
$daftarUrl = [
'https://www.example.com',
'https://www.example.com/produk/7',
'https://www.example.com/cari?kata=kopi',
'www.example.com',
'htp://example.com',
];
foreach ($daftarUrl as $url) {
$hasil = filter_var($url, FILTER_VALIDATE_URL);
$status = $hasil === false ? 'TOLAK' : 'SAH ';
echo "$status $url\n";
}Output yang Dihasilkan:
SAH https://www.example.com
SAH https://www.example.com/produk/7
SAH https://www.example.com/cari?kata=kopi
TOLAK www.example.com
SAH htp://example.comTiga URL pertama sah lengkap dengan path dan query string. URL tanpa scheme (www.example.com) ditolak — scheme adalah bagian yang wajib. Namun perhatikan baris terakhir: htp://example.com sah menurut filter ini. FILTER_VALIDATE_URL memeriksa struktur URL, bukan daftar scheme yang dikenalnya; scheme typo sekalipun lolos selama strukturnya benar. Bila aplikasi Anda hanya menerima alamat web, scheme harus diperiksa sendiri — kita lakukan tepat di bagian komposisi.
Filter ini juga menerima flag tambahan untuk menuntut bagian tertentu hadir. FILTER_FLAG_PATH_REQUIRED menuntut path, FILTER_FLAG_QUERY_REQUIRED menuntut query string:
<?php
$url = 'https://www.example.com/cari?kata=kopi';
var_dump(filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
var_dump(filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));
var_dump(filter_var('https://www.example.com', FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));Output yang Dihasilkan:
string(38) "https://www.example.com/cari?kata=kopi"
string(38) "https://www.example.com/cari?kata=kopi"
bool(false)URL lengkap lolos kedua flag, sedangkan URL tanpa query string gagal flag QUERY_REQUIRED. Flag seperti ini menggantikan pemeriksaan str_contains($url, '?') yang rawan salah — tanda tanya bisa saja ada di bagian lain URL.
Menyusun Beberapa Validator #
Validasi dunia nyata hampir tidak pernah satu lapis. Untuk kolom “URL profil” kita bisa menuntut tiga hal sekaligus: strukturnya URL yang sah, schemenya https, dan hostnya berbentuk nama domain yang benar. Ketiganya disusun dari blok-blok kecil yang sudah kita kenal:
<?php
function validasiUrlProfil(string $url): array
{
$errors = [];
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
$errors[] = 'Format URL tidak sah.';
return $errors;
}
$bagian = parse_url($url);
if (($bagian['scheme'] ?? '') !== 'https') {
$errors[] = 'URL profil harus memakai https.';
}
if (filter_var($bagian['host'] ?? '', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) {
$errors[] = 'Host URL tidak sah.';
}
return $errors;
}
print_r(validasiUrlProfil('https://www.example.com/budi'));
print_r(validasiUrlProfil('htp://example.com'));
print_r(validasiUrlProfil('bukan url'));Output yang Dihasilkan:
Array
(
)
Array
(
[0] => URL profil harus memakai https.
)
Array
(
[0] => Format URL tidak sah.
)Mari kita bedah lapisan-lapisannya:
- Lapis pertama — format:
FILTER_VALIDATE_URLmenolakbukan urllebih dulu, lalureturnlebih awal karena pemeriksaanparse_url()tidak bermakna untuk string yang bukan URL. - Lapis kedua — scheme:
parse_url()memecah URL yang sudah terbukti sah, lalu$bagian['scheme']diperbandingkan dengan'https'. Inilah jawaban atas kejutanhtp://di bagian sebelumnya. - Lapis ketiga — host:
FILTER_VALIDATE_DOMAINdenganFILTER_FLAG_HOSTNAMEmemastikan host berupa nama domain yang well-formed, bukan misalnya alamat IP atau string acak.
Pola komposisinya berlaku umum: validator kecil yang masing-masing mengembalikan keputusan, dirangkai dari murah ke mahal (format → scheme → domain → DNS), dengan error yang dikumpulkan ke satu array seperti pada Chapter Validasi Server-Side
. Lapisan DNS dengan checkdnsrr() boleh menjadi lapis terakhir — atau dilewati demi kecepatan form, dengan mengandalkan email konfirmasi.
Ringkasan #
FILTER_VALIDATE_EMAIL dan FILTER_VALIDATE_URL memberi validasi format yang jauh lebih dapat dipercaya daripada regex buatan sendiri — lengkap dengan batas panjang RFC yang sudah terpasang di dalamnya. Batas-batasnya pun perlu dipahami: email Unicode butuh flag khusus, scheme bebas seperti htp:// bisa lolos, dan domain yang sah secara format belum tentu ada di DNS. Validasi sesungguhnya disusun berlapis — format, scheme, host, hingga DNS dan email konfirmasi — dengan error yang terkumpul rapi. Sejauh ini semua contoh kita berupa field teks; pada chapter berikutnya kita menangani warga form yang paling banyak kejutan: checkbox yang tidak dicentang tidak pernah ikut terkirim.