
PHP'de Düzenli İfadeler: preg_match, preg_replace ve Desen Kurma
Düzenli ifadeler metin içinde kalıp aramak için kullanılır. Doğru yerde kullanıldığında onlarca satırlık ayrıştırma kodunun yerini tek bir ifade alır. Yanlış yerde kullanıldığında ise okunması güç, yavaş ve kırılgan bir yapı ortaya çıkar. Ayrım noktası şudur: aranan şey düzenli bir kalıpsa desen uygundur, yapısı olan bir belge biçimiyse ayrıştırıcı kullanılmalıdır.
PHP'de düzenli ifadeler PCRE kitaplığı üzerinden preg_ ön ekli fonksiyonlarla çalışır. Bu bölümde desen sözdizimi, gruplar, değiştirme ve bölme işlemleri ile Türkçe metinde karşılaşılan sorunlar ele alınıyor.
Desen yapısı ve sınırlayıcılar
Desen bir sınırlayıcı çift arasına yazılır. Geleneksel olarak eğik çizgi kullanılır, ancak desen içinde eğik çizgi geçiyorsa # veya ~ seçilerek kaçış karmaşası önlenir. Sınırlayıcıdan sonra gelen harfler bayraktır.
preg_match('/^\d{4}-\d{2}-\d{2}$/', '2026-07-27'); // tarih biçimi
preg_match('#^https?://#i', $adres); // eğik çizgi içeren desen
// sık kullanılan bayraklar
// i : büyük küçük harf ayrımı yok
// u : desen ve konu UTF-8 olarak işlenir
// m : ^ ve $ her satırın başına ve sonuna uyar
// s : nokta satır sonu karakterine de uyar
// x : desen içindeki boşluklar yok sayılır, yorum yazılabilirKullanıcıdan gelen bir değer desene gömülecekse mutlaka preg_quote ile kaçırılmalıdır. Aksi halde girdideki nokta ya da yıldız karakteri desenin anlamını değiştirir.
$aranan = preg_quote($_GET['q'] ?? '', '/');
preg_match("/{$aranan}/iu", $metin);Türkçe metin ve u bayrağı
UTF-8 metinlerde u bayrağı olmadan desen bayt düzeyinde çalışır. Türkçe karakterler iki bayt tuttuğu için karakter sınıfları ve nicelik belirteçleri yanlış sonuç verir. Türkçe içerikle çalışan her desende bu bayrak bulunmalıdır.
$metin = 'çilek';
preg_match('/^.{5}$/', $metin); // 0 - bayt sayısı 6 olduğu için uymaz
preg_match('/^.{5}$/u', $metin); // 1 - karakter sayısı 5
// harf sınıfı da bayrağa bağlıdır
preg_match_all('/\p{L}+/u', 'Şehir: İstanbul', $bulunan);
// $bulunan[0] => ['Şehir', 'İstanbul']\w kısayolu varsayılan olarak yalnızca ASCII harfleri kapsar. Türkçe harflerin de eşleşmesi için ya \p{L} kullanılır ya da sınıfa harfler açıkça eklenir.
Eşleşme yakalamak ve gruplar
preg_match ilk eşleşmeyi, preg_match_all tümünü bulur. Parantezle açılan gruplar sonuç dizisinde sırayla yer alır. Grup sayısı arttıkça sıra numarasını takip etmek zorlaşır; isimli gruplar bu sorunu ortadan kaldırır.
$desen = '/^(?<yil>\d{4})-(?<ay>\d{2})-(?<gun>\d{2})$/';
if (preg_match($desen, '2026-07-27', $p) === 1) {
echo $p['yil']; // 2026
echo $p['ay']; // 07
}
// yakalama gerekmiyorsa grup ?: ile işaretlenir, gereksiz bellek kullanılmaz
preg_match('/^(?:https?|ftp):\/\//', $adres);preg_match_all sonucu varsayılan olarak grup sırasına göre dizilir: birinci boyut grup numarası, ikinci boyut eşleşme sırasıdır. Eşleşme başına gruplanmış bir sonuç isteniyorsa PREG_SET_ORDER bayrağı verilir. Bu bayrak, her eşleşmenin tüm gruplarını tek bir dizide topladığı için döngü içinde okumayı kolaylaştırır.
preg_match_all('/(?<ad>\w+)=(?<deger>\w+)/u', $ham, $p, PREG_SET_ORDER);
foreach ($p as $eslesme) {
echo $eslesme['ad'], ' => ', $eslesme['deger'], PHP_EOL;
}Dönüş değeri üç durumu ayırır: eşleşme sayısı, sıfır ve false. Sonuncusu desenin hatalı olduğunu gösterir ve false ile sıfır gevşek karşılaştırmada birbirine karışır. Bu yüzden karşılaştırma === ile yapılmalıdır.
Değiştirme ve geri çağırma
preg_replace eşleşen bölümleri verilen metinle değiştirir. Değiştirilecek metin bir hesaplama gerektiriyorsa preg_replace_callback kullanılır.
// birden fazla boşluğu teke indirmek
$temiz = preg_replace('/\s+/u', ' ', trim($metin));
// yakalanan grubu geri kullanmak
$html = preg_replace('/\*\*(.+?)\*\*/u', '<strong>$1</strong>', $metin);
// hesaplama gerektiren değişiklik
$sonuc = preg_replace_callback(
'/\b\d+\b/',
static fn (array $e): string => number_format((int) $e[0], 0, ',', '.'),
$metin,
);Metin içinde sabit bir dizgi değiştirilecekse düzenli ifade gereksizdir. str_replace hem daha hızlı çalışır hem de okunması kolaydır. Aynı şekilde bir metnin belirli bir ifadeyle başlayıp başlamadığı str_starts_with ile sorgulanır.
Bölme ve listeleme
Bölme işlemi tek bir ayırıcıyla sınırlıysa explode yeterlidir. Ayırıcı değişken sayıda boşluk içeriyorsa ya da birden fazla karakterden biri olabiliyorsa desen kullanılır.
// birden fazla ayırıcıya göre bölmek
$parcalar = preg_split('/[,;]\s*/u', 'elma, armut; kiraz');
// boş parçaları atmak
$etiketler = preg_split('/\s*,\s*/u', $ham, flags: PREG_SPLIT_NO_EMPTY);
// desene uyan dizi elemanlarını süzmek
$gorseller = preg_grep('/\.(jpe?g|png|webp)$/i', $dosyalar);Uygulamada sık geçen desenler
Aşağıdaki desenler günlük işte en çok tekrarlanan ihtiyaçları karşılar. Her biri tek satırlık görünse de sınır koşulları düşünülerek yazılmıştır: başlangıç ve bitiş çıpaları, Türkçe karakter desteği ve gereksiz yakalama yapılmaması.
// bağlantı adresi üretmek için sadeleştirme
$slug = mb_strtolower(trim($baslik), 'UTF-8');
$slug = strtr($slug, ['ı' => 'i', 'ğ' => 'g', 'ü' => 'u', 'ş' => 's', 'ö' => 'o', 'ç' => 'c']);
$slug = preg_replace('/[^a-z0-9]+/u', '-', $slug);
$slug = trim($slug, '-');
// dosya uzantısı denetimi
$uygun = preg_match('/\.(jpe?g|png|webp)$/i', $ad) === 1;
// on bir haneli telefon numarasını normalleştirmek
$sayilar = preg_replace('/\D+/', '', $ham);
// birden fazla satır sonunu tek boşluğa indirmek
$tekSatir = preg_replace('/[\r\n]+/u', ' ', $metin);Desen çalıştırıldıktan sonra hata durumu preg_last_error ile sorgulanabilir. Geri izleme sınırının aşılması bu yolla yakalanır ve sessiz başarısızlık önlenir.
$sonuc = preg_replace($desen, $yeni, $metin);
if ($sonuc === null && preg_last_error() !== PREG_NO_ERROR) {
throw new RuntimeException('Desen çalıştırılamadı: ' . preg_last_error_msg());
}Performans ve geri izleme tuzağı
İç içe geçmiş belirsiz nicelik belirteçleri, eşleşmeyen bir girdide denenecek yol sayısını katlanarak artırır. Bu durum katastrofik geri izleme olarak adlandırılır ve tek bir isteğin işlemciyi saniyelerce meşgul etmesine yol açar.
// riskli: (a+)+ yapısı eşleşmeyen girdide çok sayıda yol dener
$riskli = '/^(\w+\s?)+$/';
// güvenli karşılığı: belirsizliği kaldırmak
$guvenli = '/^\w+(?:\s\w+)*$/';
// sınır koymak da bir önlemdir
ini_set('pcre.backtrack_limit', '100000');Desenler döngü içinde derlenmez; PCRE derlenmiş desenleri kendi önbelleğinde tutar. Yine de sıcak yollarda desen kullanmak yerine dizgi fonksiyonlarına geçmek ölçülebilir kazanç sağlar.
Uzun desenleri okunabilir tutmak
Bir desen tek satırda okunamayacak kadar uzadığında x bayrağı kullanılır. Bu bayrak desen içindeki boşlukları ve satır sonlarını yok sayar, yorum yazmaya izin verir. Boşluk karakterinin kendisi aranacaksa kaçış ile ya da karakter sınıfı içinde yazılır.
$desen = '/
^
(?<protokol>https?) # protokol
:\/\/
(?<alan>[\w.-]+) # alan adı
(?<yol>\/[^\s]*)? # yol, isteğe bağlı
$
/xu';
if (preg_match($desen, $adres, $p) === 1) {
echo $p['alan'];
}Deseni koda gömmek yerine adlandırılmış bir sabit olarak tanımlamak da okunabilirliği artırır. Aynı desen birden fazla yerde kullanılıyorsa bu yöntem tutarlılığı da korur; kural değiştiğinde tek bir yer güncellenir.
Düzenli ifadenin uygun olmadığı yerler
- HTML ayrıştırmak. İç içe yapı düzenli ifadeyle güvenilir biçimde çözülmez;
DOMDocumentkullanılır. - E-posta doğrulamak. Standartla uyumlu desen aşırı karmaşıktır;
filter_varileFILTER_VALIDATE_EMAILyeterlidir. - URL ayrıştırmak.
parse_urlbileşenleri hazır verir. - Sabit metin aramak.
str_containsdaha hızlı ve okunaklıdır. - JSON okumak. Biçimin kendi ayrıştırıcısı vardır.
Kontrol listesi
- Türkçe metinle çalışan tüm desenlerde
ubayrağı var. - Kullanıcı girdisi desene gömülmeden önce
preg_quoteile kaçırılıyor. - Dönüş değeri
===ile karşılaştırılıyor,falseayrı ele alınıyor. - Yakalanmayacak gruplar
(?:...)biçiminde yazılıyor. - İç içe belirsiz nicelik belirteçleri bulunmuyor.
- Sabit metin işlemleri için dizgi fonksiyonları tercih ediliyor.
İlgili yazılar
PHP eğitim serisi
Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.
Temeller
- Kurulum ve temel sözdizimi
- Döngüler ve akış kontrolü
- Diziler
- Fonksiyonlar
- String fonksiyonları
- Süper globaller
- Form işleme
- Dosya işlemleri
Dilin ayrıntıları
- Tip sistemi ve strict_types
- Hata ve istisna yönetimi
- Düzenli ifadeler (bu yazı)
- Tarih ve saat işlemleri
Nesne yönelimli PHP
- Sınıf ve nesne
- Kalıtım, arayüz ve trait
- Statik üyeler, sabitler ve enum
- Namespace, Composer ve autoload
Veri ve güvenlik
- PDO ile veritabanı işlemleri
- Oturum yönetimi ve kimlik doğrulama
- Güvenli çıktı ve XSS
- JSON ile veri değişimi
Dış dünya ve üretim
Yorumlar
İlk yorumu sen yap.
İlgili Yazılar

PHP Uygulamasını Üretime Almak: Yapılandırma ve Güvenlik
Geliştirme makinesinde çalışan kod ile üretimde güvenle çalışan kod arasındaki fark yapılandırmadır. Ayarlar, izinler, dağıtım akışı ve son kontroller.

PHP ile REST API Yazmak: Yönlendirme ve HTTP Durum Kodları
Bir API'nin kalitesi çoğunlukla ayrıntılarda belli olur: doğru durum kodu, tutarlı hata biçimi ve öngörülebilir adresler. Saf PHP ile uçtan uca kurulum.

PHPUnit ile Birim Test Yazmak: Kurulum, Sahte Nesneler ve Kapsam
Testin amacı hata bulmak değil, değişikliği güvenle yapabilmektir. Kurulum, ilk test, sahte nesneler ve test edilebilir kodun özellikleri.