PHP'de dışarıdan gelen bir değerin beklenen biçimde olup olmadığını kontrol etmek için filter_var() kullanılır: filter_var($deger, FILTER_VALIDATE_INT) geçerli bir tam sayıyı int olarak döndürür, geçersizse false verir. Aynı yöntemle e-posta (FILTER_VALIDATE_EMAIL), URL (FILTER_VALIDATE_URL), IP adresi (FILTER_VALIDATE_IP), ondalıklı sayı ve mantıksal değer doğrulanır. Doğrulanmış veriyi HTML'e yazarken ise ayrı bir adım gerekir: htmlspecialchars() ile çıktıyı kaçırmak.
Bu iki adım sık karıştırılır. Doğrulama "bu değer kabul edilebilir mi?" sorusunu girişte cevaplar; çıktı kaçırma ise değerin yazıldığı bağlamda (HTML, SQL, URL) kod gibi yorumlanmasını engeller. Bu yazıda filter_var() fonksiyonunun dönüş kurallarını, seçenek ve bayrak kullanımını, kılavuzun "çok hoşgörülü" diye uyardığı filtreleri, PHP 8.5 ile gelen FILTER_THROW_ON_FAILURE bayrağını ve htmlspecialchars() kullanımını, PHP 8.5.12 ile çalıştırılmış örneklerle ele alıyoruz. Serinin genel planı PHP öğrenme rehberi sayfasında.
filter_var nasıl çalışır?

İmza şöyledir: filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed. Başarıda filtrelenmiş veriyi, başarısızlıkta false döndürür; FILTER_NULL_ON_FAILURE bayrağı verilirse başarısızlıkta null döner. Kılavuz iki noktayı özellikle vurgular. Birincisi, skaler değerler filtrelenmeden önce içeride string'e çevrilir. İkincisi, varsayılan filtre FILTER_DEFAULT aslında FILTER_UNSAFE_RAW için bir takma addır ve hiçbir filtreleme yapmaz. Yani ikinci parametreyi unutursanız değer olduğu gibi geri gelir.
Üçüncü parametre ya bayrakların bit maskesi (FILTER_FLAG_IPV4 gibi) ya da ilişkisel bir dizidir. Dizi biçiminde seçenekler 'options', bayraklar 'flags' anahtarı altında verilir. Filtreler iki gruptadır: FILTER_VALIDATE_* doğrulama filtreleri değeri kabul eder ya da reddeder, FILTER_SANITIZE_* temizleme filtreleri ise değeri değiştirerek döndürür. Özel kurallar için FILTER_CALLBACK ile kendi fonksiyonunuzu da verebilirsiniz.
Tam sayı doğrulama: FILTER_VALIDATE_INT
FILTER_VALIDATE_INT değerin tam sayı olup olmadığını kontrol eder ve başarılıysa int tipine çevirir. Kılavuza göre string değerler doğrulamadan önce trim() ile kırpılır; bu yüzden baştaki ve sondaki boşluklar sorun çıkarmaz. min_range ve max_range seçenekleri izin verilen aralığı, default seçeneği ise başarısızlıkta dönecek değeri belirler.
<?php
var_dump(filter_var('42', FILTER_VALIDATE_INT)); // çıktı: int(42)
var_dump(filter_var(' 42 ', FILTER_VALIDATE_INT)); // çıktı: int(42)
var_dump(filter_var('42abc', FILTER_VALIDATE_INT)); // çıktı: bool(false)
var_dump(filter_var('4.2', FILTER_VALIDATE_INT)); // çıktı: bool(false)
var_dump(filter_var('0', FILTER_VALIDATE_INT)); // çıktı: int(0)
var_dump(filter_var('150', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 120]])); // çıktı: bool(false)
var_dump(filter_var('abc', FILTER_VALIDATE_INT, ['options' => ['default' => 18]])); // çıktı: int(18)(int) dönüşümüyle farkı burada görülür: (int) '42abc' sessizce 42 verirdi, filtre ise değeri reddeder. Ondalık içeren bir değer de tam sayı sayılmaz. Sekizlik veya onaltılık gösterimi kabul etmek isterseniz FILTER_FLAG_ALLOW_OCTAL ve FILTER_FLAG_ALLOW_HEX bayrakları vardır. PHP'nin tip dönüşüm kuralları eşitlik ve tip dönüşümü yazısında ayrıntılı anlatılıyor.
En sık hata: sonucu ! ile kontrol etmek
0 geçerli bir tam sayıdır ama PHP'de false gibi değerlendirilir. Kılavuzdaki örnek de tam olarak bu tuzağı gösterir:
<?php
$yas = '0';
if (!filter_var($yas, FILTER_VALIDATE_INT)) {
echo "yanlış kontrol: '$yas' reddedildi\n";
}
if (filter_var($yas, FILTER_VALIDATE_INT) === false) {
echo "geçersiz\n";
} else {
echo "geçerli\n";
}
/*
yanlış kontrol: '0' reddedildi
geçerli
*/Sonucu her zaman === false ile (ya da FILTER_NULL_ON_FAILURE kullandıysanız === null ile) kontrol edin. Aynı kural sıfır döndürebilen her doğrulama için geçerlidir: miktar, stok, puan gibi alanlar.
Ondalık sayı ve mantıksal değer
FILTER_VALIDATE_FLOAT değeri float tipine çevirir. Varsayılan ondalık ayırıcı noktadır; Türkçe yazımda kullanılan virgülü kabul etmek için decimal seçeneğini ',' yapabilirsiniz. FILTER_FLAG_ALLOW_THOUSAND bayrağı binlik ayırıcı olarak virgülü kabul eder. PHP 7.4'ten beri bu filtrede de min_range ve max_range kullanılabilir.
<?php
var_dump(filter_var('3.14', FILTER_VALIDATE_FLOAT)); // çıktı: float(3.14)
var_dump(filter_var('3,14', FILTER_VALIDATE_FLOAT)); // çıktı: bool(false)
var_dump(filter_var('3,14', FILTER_VALIDATE_FLOAT, ['options' => ['decimal' => ',']])); // çıktı: float(3.14)
var_dump(filter_var('1,250.5', FILTER_VALIDATE_FLOAT, FILTER_FLAG_ALLOW_THOUSAND)); // çıktı: float(1250.5)FILTER_VALIDATE_BOOL (PHP 8.0 öncesindeki adıyla FILTER_VALIDATE_BOOLEAN) "1", "true", "on" ve "yes" için true; "0", "false", "off", "no" ve boş string için false döndürür. Karşılaştırma büyük-küçük harfe duyarsızdır. Diğer değerlerde sonuç FILTER_NULL_ON_FAILURE bayrağına bağlıdır: Bayrak varsa null, yoksa false döner. Bayraksız kullanımda "geçersiz değer" ile "hayır" ayırt edilemez.
<?php
foreach (['evet', 'yes', 'on', 'off', '', 'belki'] as $d) {
var_dump(filter_var($d, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
}
/*
NULL
bool(true)
bool(true)
bool(false)
bool(false)
NULL
*/Dikkat edin: Filtre yalnızca İngilizce sözcükleri tanır; "evet" geçersiz sayılır. Türkçe seçenekler sunan bir formda değerleri kendiniz eşlemeniz gerekir. Örnekte görüldüğü gibi "on" ve "yes" true, "off" ve boş string false olarak döndü.
E-posta doğrulama
FILTER_VALIDATE_EMAIL değeri RFC 822'deki addr-spec sözdizimine göre kontrol eder; ancak yorumlar, boşluk katlama ve noktasız alan adları desteklenmez ve reddedilir. FILTER_FLAG_EMAIL_UNICODE bayrağı (PHP 7.1) yerel kısımda, yani @ işaretinden önceki bölümde Unicode karakterlere izin verir.
<?php
var_dump(filter_var('ayse@example.com', FILTER_VALIDATE_EMAIL)); // çıktı: string(16) "ayse@example.com"
var_dump(filter_var('ayse@localhost', FILTER_VALIDATE_EMAIL)); // çıktı: bool(false)
var_dump(filter_var('ayşe@example.com', FILTER_VALIDATE_EMAIL)); // çıktı: bool(false)
var_dump(filter_var('ayşe@example.com', FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE)); // çıktı: string(17) "ayşe@example.com"
var_dump(filter_var('ali@şirket.com', FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE)); // çıktı: bool(false)localhost noktasız bir alan adı olduğu için reddedildi. Unicode bayrağı yalnızca yerel kısmı kapsadığı için Türkçe karakterli alan adı (şirket.com) yine geçmedi. Kılavuzun bu filtre için yaptığı uyarı önemlidir: E-posta doğrulaması karmaşıktır ve bir adresin gerçekten geçerli olduğunu ve var olduğunu anlamanın tek kesin yolu o adrese e-posta göndermektir. Filtre yalnızca biçimi kontrol eder.
URL doğrulama ve kılavuzun uyarıları

FILTER_VALIDATE_URL değeri RFC 2396'ya göre kontrol eder. FILTER_FLAG_PATH_REQUIRED ve FILTER_FLAG_QUERY_REQUIRED bayrakları yol ve sorgu bölümünü zorunlu kılar. Eski FILTER_FLAG_SCHEME_REQUIRED ve FILTER_FLAG_HOST_REQUIRED bayrakları PHP 8.0'da kaldırıldı, çünkü bu kontroller filtrenin kendisinde zaten yapılıyor. Kılavuz bu filtre için üç uyarı yapar: Geçerli bir URL'nin http:// kullanması gerekmez; filtre yalnızca ASCII URL'lerde çalışır, bu yüzden uluslararası alan adları (IDN) her zaman reddedilir; ve filtre çok hoşgörülüdür, http ve https dışındaki şemalarda ana makine kısmını doğrulamaz, şemanın kendisini de doğrulamaz.
<?php
var_dump(filter_var('https://www.example.com/blog?s=1', FILTER_VALIDATE_URL)); // çıktı: string(32) "https://www.example.com/blog?s=1"
var_dump(filter_var('www.example.com', FILTER_VALIDATE_URL)); // çıktı: bool(false)
var_dump(filter_var('javascript://x%0aalert(1)', FILTER_VALIDATE_URL)); // çıktı: string(25) "javascript://x%0aalert(1)"
function guvenliUrl(string $girdi): ?string
{
$url = filter_var($girdi, FILTER_VALIDATE_URL);
if ($url === false) {
return null;
}
$sema = strtolower((string) parse_url($url, PHP_URL_SCHEME));
return in_array($sema, ['http', 'https'], true) ? $url : null;
}
var_dump(guvenliUrl('javascript://x%0aalert(1)')); // çıktı: NULL
var_dump(guvenliUrl('https://example.com/')); // çıktı: string(20) "https://example.com/"Şemasız www.example.com reddedilirken javascript:// ile başlayan değer filtreden geçti. Bu değeri bir bağlantının href özelliğine yazmak tehlikeli olabilir. Bu yüzden kullanıcıdan alınan bağlantılarda filtrenin üstüne şema kontrolü ekleyin: parse_url() ile şemayı alıp yalnızca http ve https değerlerine izin vermek, örnekteki guvenliUrl() fonksiyonunun yaptığı iştir. in_array() çağrısındaki üçüncü parametre true, katı karşılaştırma yapar.
IP adresi doğrulama
FILTER_VALIDATE_IP varsayılan olarak hem IPv4 hem IPv6 kabul eder. FILTER_FLAG_IPV4 veya FILTER_FLAG_IPV6 ile tek bir türe sınırlarsınız. FILTER_FLAG_NO_PRIV_RANGE özel ağ adreslerini (IPv4'te 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), FILTER_FLAG_NO_RES_RANGE ise RFC 6890'da ayrılmış olarak işaretlenen aralıkları (örneğin 127.0.0.0/8) reddeder. PHP 8.2 ile gelen FILTER_FLAG_GLOBAL_RANGE yalnızca genel (global) adreslere izin verir.
<?php
var_dump(filter_var('192.168.1.10', FILTER_VALIDATE_IP)); // çıktı: string(12) "192.168.1.10"
var_dump(filter_var('192.168.1.10', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)); // çıktı: bool(false)
var_dump(filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)); // çıktı: bool(false)
var_dump(filter_var('2001:db8::1', FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)); // çıktı: bool(false)
var_dump(filter_var('2001:db8::1', FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); // çıktı: string(11) "2001:db8::1"
var_dump(filter_var('999.1.1.1', FILTER_VALIDATE_IP)); // çıktı: bool(false)Kullanıcının verdiği bir adrese sunucunuzdan istek atacaksanız (örneğin bir webhook adresi), özel ve ayrılmış aralıkları reddetmek, isteğin iç ağdaki bir makineye yönelmesini önlemeye yardım eder. Bu kontrol tek başına yeterli değildir, çünkü alan adları farklı IP'lere çözülebilir; yine de iyi bir ilk adımdır.
Bir formu tek seferde doğrulamak: filter_var_array
filter_var_array() ilişkisel bir diziyi, her anahtar için ayrı kural tanımlayarak tek çağrıda doğrular. Kural ya doğrudan bir filtre sabiti ya da 'filter', 'flags' ve 'options' anahtarlarını içeren bir dizidir. Üçüncü parametre $add_empty varsayılan olarak true olduğu için, girdide bulunmayan anahtarlar sonuca null olarak eklenir.
<?php
$form = [
'ad' => ' Ayşe ',
'yas' => '29',
'eposta' => 'ayse@example',
'bulten' => 'on',
];
$kurallar = [
'ad' => FILTER_DEFAULT,
'yas' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 18, 'max_range' => 120]],
'eposta' => FILTER_VALIDATE_EMAIL,
'bulten' => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
'sehir' => FILTER_DEFAULT,
];
var_dump(filter_var_array($form, $kurallar));
/*
array(5) {
["ad"]=>
string(9) " Ayşe "
["yas"]=>
int(29)
["eposta"]=>
bool(false)
["bulten"]=>
bool(true)
["sehir"]=>
NULL
}
*/Sonuçta yas artık int, bulten bool oldu; noktasız alan adı içeren e-posta false döndü; formda olmayan sehir null olarak eklendi. ad alanındaki boşluklar ise korundu, çünkü FILTER_DEFAULT hiçbir şey yapmaz; metin alanlarını trim() ile kendiniz kırpmalısınız. Uzunluk kontrolü için mb_strlen, belirli bir desen için FILTER_VALIDATE_REGEXP ya da doğrudan preg_match kullanabilirsiniz.
Web isteğinde doğrudan $_GET veya $_POST içinden okumak için filter_input(INPUT_POST, 'yas', FILTER_VALIDATE_INT) da kullanılabilir. Kılavuza göre bu fonksiyon değişken hiç gönderilmemişse null, filtre başarısızsa false döndürür. Ayrıca süper küresel dizinin kendisini değil, SAPI'nin sağladığı ham girdiyi filtreler; diziyi kodunuzda değiştirdiyseniz filter_var() kullanmanız gerekir.
PHP 8.5: FILTER_THROW_ON_FAILURE
PHP 8.5 ile filtre fonksiyonlarına FILTER_THROW_ON_FAILURE bayrağı eklendi. Bu bayrakla doğrulama başarısız olduğunda false dönmek yerine Filter\FilterFailedException fırlatılır. Geçiş belgesine göre bu bayrak FILTER_NULL_ON_FAILURE ile birlikte kullanılamaz; ikisini birleştirmek ValueError üretir. Aşağıdaki örnek PHP 8.5 veya sonrasını gerektirir:
<?php
try {
filter_var('abc', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
} catch (Filter\FilterFailedException $e) {
echo get_class($e), "\n";
echo $e->getMessage(), "\n";
}
var_dump(filter_var('12', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE)); // çıktı: int(12)
/*
Filter\FilterFailedException
filter validation failed: filter int not satisfied by 'abc'
*/Bu yaklaşım, birden çok alanı doğruladığınız bir fonksiyonda her dönüş değerini ayrı ayrı kontrol etmek yerine tek bir try/catch kullanmanıza izin verir. İstisna yönetimi try/catch ve exception, 8.5 ile gelen diğer değişiklikler PHP 8.5 yenilikleri yazısında.
Temizleme filtreleri ve sınırları
FILTER_SANITIZE_* filtreleri değeri reddetmez, istenmeyen karakterleri çıkarır. FILTER_SANITIZE_NUMBER_INT rakam, artı ve eksi dışındaki her şeyi siler. FILTER_SANITIZE_NUMBER_FLOAT aynı şeyi yapar; kılavuz, FILTER_FLAG_ALLOW_FRACTION verilmezse ondalık ayırıcının da silineceğini ve değerin değişeceğini uyarır. FILTER_SANITIZE_EMAIL ise Latin harfleri, rakamları ve belirli özel karakterleri bırakır.
<?php
var_dump(filter_var('12.34 TL', FILTER_SANITIZE_NUMBER_FLOAT)); // çıktı: string(4) "1234"
var_dump(filter_var('12.34 TL', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); // çıktı: string(5) "12.34"
var_dump(filter_var('0532-123 45 67', FILTER_SANITIZE_NUMBER_INT)); // çıktı: string(12) "0532-1234567"
var_dump(filter_var('ay se(at)@exa mple.com', FILTER_SANITIZE_EMAIL)); // çıktı: string(18) "ayseat@example.com"Bu çıktılar temizlemenin neden doğrulamanın yerini tutmadığını gösterir: 12.34 sessizce 1234 oldu ve bozuk bir e-posta, geçerli görünen ama kullanıcının kastetmediği bir adrese dönüştü. Genel kural, önce doğrulamak ve geçersiz girdiyi kullanıcıya hata olarak göstermektir. Eski FILTER_SANITIZE_STRING sabiti PHP 8.1'den beri kullanımdan kaldırılmıştır ve kullanıldığında uyarı üretir:
<?php
var_dump(filter_var('x', FILTER_SANITIZE_STRING));
/*
Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in /yol/ornek.php on line 2
string(1) "x"
*/Çıktıyı kaçırmak: htmlspecialchars
Doğrulamadan geçmiş bir metin bile HTML'e olduğu gibi yazıldığında tarayıcı tarafından kod olarak yorumlanabilir. htmlspecialchars() HTML'de özel anlamı olan beş karakteri karşılık gelen varlıklara (entity) çevirir: &, çift tırnak, tek tırnak, < ve >. PHP 8.1'den beri varsayılan bayraklar ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 olduğu için tek tırnak da kodlanır ve geçersiz bayt dizileri boş string yerine Unicode yerine koyma karakteriyle (U+FFFD) değiştirilir. Kılavuz, default_charset ayarına güvenmek yerine kodlamayı açıkça vermenizi önerir; örneğin htmlspecialchars($metin, encoding: 'UTF-8').
<?php
$yorum = '<script>alert("x")</script> Merhaba & hoş geldiniz \'dostum\'';
echo htmlspecialchars($yorum), "\n";
/*
<script>alert("x")</script> Merhaba & hoş geldiniz 'dostum'
*/
echo htmlspecialchars('& zaten kodlu', double_encode: false), "\n"; // çıktı: & zaten kodlu
var_dump(htmlspecialchars("bozuk \xC3 bayt")); // çıktı: string(14) "bozuk � bayt"Türkçe harfler değişmeden kaldı, çünkü htmlspecialchars() yalnızca özel karakterlere dokunur; adlandırılmış karşılığı olan tüm karakterleri çevirmek isterseniz htmlentities() vardır. double_encode: false zaten kodlanmış varlıkların ikinci kez kodlanmasını engeller. Son satırdaki geçersiz \xC3 baytı ENT_SUBSTITUTE sayesinde yerine koyma karakterine dönüştü. HTML varlıkları hakkında genel bilgi HTML entity nedir yazısında, XSS saldırılarına karşı katmanlı koruma ve Content Security Policy XSS ve CSP koruma yazısında anlatılıyor.
Hangi araç hangi iş için?

| Amaç | Araç | Not |
|---|---|---|
| Sayı, e-posta, URL, IP doğrulamak | filter_var + FILTER_VALIDATE_* | sonucu === false ile kontrol edin |
| Formu toplu doğrulamak | filter_var_array | eksik anahtarlar null olur |
| HTML içine yazmak | htmlspecialchars | kodlamayı açıkça verin |
| SQL sorgusuna değer koymak | PDO prepare + execute | filtre bunun yerini tutmaz |
| Şifre saklamak | password_hash | şifreyi filtrelemeyin |
| Desen kontrolü | FILTER_VALIDATE_REGEXP veya preg_match | u bayrağı ile UTF-8 |
Tablodaki SQL satırı önemlidir: Doğrulama, değerin biçimini kontrol eder ama SQL enjeksiyonuna karşı tek başına koruma sağlamaz. Değerleri her zaman PDO hazırlanmış sorguları ile gönderin. Şifreler ise doğrulama veya temizleme filtresinden geçirilmeden password_hash ile saklanır.
Sık yapılan hatalar
- Filtre sonucunu
if (!filter_var(...))ile kontrol etmek.0vefalsekarışır;=== falsekullanın. - İkinci parametreyi vermeyip
FILTER_DEFAULTile filtrelendiğini sanmak. Bu filtre hiçbir şey yapmaz. FILTER_VALIDATE_URLgeçen her değeri güvenli bağlantı saymak. Şemayı ayrıca kontrol edin.FILTER_VALIDATE_BOOLkullanırkenFILTER_NULL_ON_FAILUREvermemek. Geçersiz değer ile "hayır" ayırt edilemez.- Temizleme filtresini doğrulama yerine kullanmak. Değer sessizce değişir.
- Girişte
htmlspecialchars()uygulayıp veritabanına kodlanmış metin yazmak. Kaçırmayı çıktı anında, yazıldığı bağlamda yapın. - Eski kodlardaki
FILTER_SANITIZE_STRINGsabitini kullanmaya devam etmek. PHP 8.1'den beri deprecated;htmlspecialchars()kullanın.
Sık Sorulan Sorular
filter_var ile is_numeric arasındaki fark nedir?
is_numeric() yalnızca değerin sayısal bir string olup olmadığını söyler ve ondalıklı değerleri de kabul eder. FILTER_VALIDATE_INT ise yalnızca tam sayıyı kabul eder, aralık kontrolü yapabilir ve sonucu doğrudan int olarak verir.
FILTER_VALIDATE_EMAIL Türkçe karakterli adresleri kabul eder mi?
Varsayılan olarak hayır. FILTER_FLAG_EMAIL_UNICODE bayrağı yerel kısımda Unicode karakterlere izin verir; alan adı kısmındaki Türkçe karakterler bu bayrakla da kabul edilmez.
htmlspecialchars mı, strip_tags mı?
htmlspecialchars() metni değiştirmeden güvenli biçimde gösterir; kullanıcı ne yazdıysa o görünür. strip_tags() etiketleri siler ve metni değiştirir. Kullanıcı içeriğini göstermek için genellikle htmlspecialchars() tercih edilir.
Doğrulama istemci tarafında yapılıyorsa sunucuda gerekli mi?
Evet. Tarayıcıdaki kontroller atlanabilir; sunucuya gelen istek her zaman doğrudan da gönderilebilir. Sunucu tarafı doğrulama zorunludur, istemci tarafı yalnızca kullanıcı deneyimini iyileştirir.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PHP öğrenme rehberi · XSS ve CSP koruma · HTML entity nedir · PHP PDO · PHP preg_match