PHP ile Nesne Yönelimli Yapıda Dinamik Site İçi Arama Motoru Nasıl Yapılır?
Modern web projelerinde kullanıcı deneyimini doğrudan etkileyen en kritik özelliklerden biri, veriler arasında hızlı ve doğru sonuçlar sunan arama motorlarıdır. PHP ile nesne yönelimli yapıda (OOP) dinamik bir site içi arama motoru geliştirmek, hem kodunuzun sürdürülebilirliğini artırır hem de veritabanı işlemlerini daha güvenli hale getirmenize olanak tanır. Bu rehberde, 2026 standartlarına uygun, SQL Injection saldırılarına karşı korumalı ve ölçeklenebilir bir arama mekanizmasını adım adım inşa edeceğiz.
Bu eğitim, PHP 8.x ve üzeri sürümlerin sunduğu modern özelliklere (tip belirleme, constructor promotion vb.) odaklanmaktadır. Eğer temel PHP bilginiz varsa ve nesne yönelimli programlama mantığını projelerinize entegre etmek istiyorsanız, bu rehber tam size göre. Sürecin sonunda, veritabanınızdaki içerikleri filtreleyen, güvenli ve performanslı bir arama sınıfına sahip olacaksınız.
Gereksinimler ve Ön Hazırlık
Dinamik bir arama motoru geliştirmek için öncelikle yerel geliştirme ortamınızın (Localhost) hazır olması gerekir. İhtiyacınız olan temel araçlar şunlardır:
- PHP 8.2 veya üzeri: Performans ve güvenlik güncellemeleri için güncel sürüm kullanılması zorunludur.
- MySQL veya MariaDB: Verilerimizi saklayacağımız ilişkisel veritabanı yönetim sistemi.
- PDO (PHP Data Objects): Veritabanı bağlantısı ve güvenli sorgu çalıştırma için standart kütüphane.
- Düzenleyici: VS Code veya PhpStorm gibi modern bir IDE.
Projenize başlamadan önce veritabanınızda en az bir adet içerik tablosu (örneğin: yazilar) olduğundan emin olun. Bu tabloda en az id, baslik ve icerik sütunlarının bulunması, arama motorumuzun işlevselliğini test etmemiz için yeterli olacaktır.
Veritabanı Bağlantısı İçin Sınıf Yapısı
Nesne yönelimli yapıda ilk adım, veritabanı bağlantısını tek bir noktadan yönetmektir. Bunun için bir Database sınıfı oluşturarak PDO bağlantısını güvenli bir şekilde yapılandıracağız.
class Database {
private $host = "localhost";
private $db_name = "proje_db";
private $username = "root";
private $password = "";
public $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8mb4", $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo "Bağlantı hatası: " . $e->getMessage();
}
return $this->conn;
}
}
Yukarıdaki kod bloğu, veritabanı bağlantısını bir sınıf içine hapseder. charset=utf8mb4 kullanımı, Türkçe karakterlerin arama sonuçlarında düzgün görünmesi için kritik önem taşır.
Arama Motoru Sınıfını Oluşturma
Şimdi arama mantığını yürütecek olan SearchEngine sınıfını tanımlayacağız. Bu sınıf, veritabanı bağlantısını alacak ve arama sorgularını işleyecektir. Burada SQL Injection riskini önlemek için mutlaka prepared statements (hazırlanmış ifadeler) kullanmalıyız.
class SearchEngine {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function search($query) {
$sql = "SELECT * FROM yazilar WHERE baslik LIKE :query OR icerik LIKE :query";
$stmt = $this->db->prepare($sql);
// Arama terimini wildcard ile birleştiriyoruz
$searchTerm = "%" . $query . "%";
$stmt->bindParam(":query", $searchTerm);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
Bu sınıf, search metodu ile dışarıdan gelen veriyi güvenli bir şekilde sorguya dahil eder. :query kullanımı, veritabanına doğrudan kullanıcı girdisi gönderilmesini engeller ve sisteminizi SQL Injection saldırılarına karşı korur.
Dinamik Arama Formu ve Arayüz Entegrasyonu
Kullanıcının arama terimini gireceği HTML formunu oluştururken, formun GET metodunu kullanması SEO ve paylaşılabilirlik açısından önemlidir. Böylece kullanıcı arama sonuçlarını bir arkadaşına link olarak gönderebilir.
Ara
Kullanıcı formu gönderdiğinde, arama.php dosyası içerisinde veriyi yakalayıp sınıfımızı tetiklememiz gerekecektir.
Arama Sonuçlarını Listeleme ve Güvenlik
Sonuçları ekrana basarken XSS (Cross-Site Scripting) saldırılarına karşı dikkatli olmalıyız. Veritabanından gelen veriyi ekrana yazdırırken htmlspecialchars() fonksiyonunu kullanmak, kötü niyetli scriptlerin çalışmasını engeller.
$db = new Database();
$connection = $db->getConnection();
$searchEngine = new SearchEngine($connection);
if(isset($_GET['q'])) {
$results = $searchEngine->search($_GET['q']);
foreach($results as $row) {
echo "" . htmlspecialchars($row['baslik']) . "
";
echo "" . htmlspecialchars(substr($row['icerik'], 0, 150)) . "...
";
}
}
Bu kod bloğu, arama sonuçlarını döngüye alarak ekrana basar. substr fonksiyonu ile içeriklerin sadece bir kısmını göstererek arayüzün düzenli kalmasını sağlıyoruz.
Arama Yöntemlerinin Karşılaştırılması
Arama motoru yaparken tercih edebileceğiniz farklı yöntemler bulunmaktadır. Aşağıdaki tablo, farklı yaklaşımların avantaj ve dezavantajlarını özetlemektedir.
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| SQL LIKE | Basit, kurulum gerektirmez. | Büyük veride yavaş çalışır. |
| FULLTEXT Index | Daha hızlı arama sağlar. | Sadece belirli sütunlarda çalışır. |
| Elasticsearch | Profesyonel, çok hızlı. | Karmaşık, sunucu kaynağı ister. |
Kritik Güvenlik Uyarısı: Kullanıcıdan gelen hiçbir veriye güvenmeyin. SQL sorgularında her zaman PDO prepare/execute yapısını kullanın. Ekrana yazdıracağınız verilerde mutlaka htmlspecialchars veya benzeri temizleme fonksiyonlarını tercih edin. Bu basit önlemler, web sitenizin güvenliğini %90 oranında artırır.
Sıkça Sorulan Sorular
Arama motorum neden Türkçe karakterlerde sorun yaşıyor?
Veritabanı bağlantınızda charset=utf8mb4 tanımladığınızdan ve veritabanı tablonuzun collation değerinin utf8mb4_general_ci olduğundan emin olun.
Arama sonuçlarını nasıl sayfalayabilirim?
SQL sorgunuza LIMIT ve OFFSET parametrelerini ekleyerek veritabanından parça parça veri çekebilir ve sayfalama yapısını kurabilirsiniz.
LIKE operatörü çok yavaş kalıyor, ne yapmalıyım?
Veri setiniz büyüdüğünde MySQL üzerinde ilgili sütunlara FULLTEXT index ekleyerek MATCH() AGAINST() sorgularına geçiş yapmanız performansı ciddi oranda artıracaktır.
Arama terimi boşsa ne yapmalıyım?
Kodunuzda if(empty($_GET['q'])) kontrolü yaparak kullanıcıyı boş arama yapmaması konusunda uyarabilir veya anasayfaya yönlendirebilirsiniz.
Bu yapı büyük projeler için uygun mu?
Bu yapı, orta ölçekli projeler için oldukça uygundur. Çok büyük veritabanlarında (milyonlarca satır) Elasticsearch veya Algolia gibi profesyonel arama motorlarına geçiş yapmanız önerilir.
Arama Sonuçlarında Anahtar Kelime Vurgulama (Highlighting)
Kullanıcı deneyimini artırmak için aranan kelimenin sonuçlar içerisinde belirginleştirilmesi oldukça etkili bir yöntemdir. PHP'nin preg_replace fonksiyonunu kullanarak, veritabanından gelen metin içerisinde aranan terimi bulup HTML mark etiketleri arasına alabiliriz.
public function highlightKeyword($text, $keyword) {
if (empty($keyword)) return $text;
// Kelimeyi büyük/küçük harf duyarsız şekilde bul ve mark içine al
$pattern = '/' . preg_quote($keyword, '/') . '/i';
return preg_replace($pattern, '$0', $text);
}
// Kullanımı:
$baslik = $searchEngine->highlightKeyword($row['baslik'], $_GET['q']);
echo "" . $baslik . "
";
Arama Performansını İyileştirmek İçin İleri İpuçları
Veritabanı tablonuz büyüdükçe LIKE '%terim%' sorguları indeksleri kullanamadığı için yavaşlayacaktır. Performansı optimize etmek adına aşağıdaki stratejileri uygulayabilirsiniz:
- Full-Text Index Kullanımı: MySQL üzerinde metin alanlarına (başlık, içerik)
FULLTEXTindeksi ekleyerekMATCH() AGAINST()sorgularını kullanın. Bu, LIKE operatörüne göre çok daha hızlı sonuç verir. - Önbellekleme (Caching): Çok sık aranan popüler terimlerin sonuçlarını Redis veya basit bir dosya sistemi üzerinde önbelleğe alarak veritabanı yükünü azaltın.
- Arama Loglama: Kullanıcıların hangi kelimeleri arattığını bir tabloda tutarak, en çok aranan terimler için özel içerik stratejileri geliştirebilirsiniz.
Full-Text Arama Örneği
Eğer tablonuzda FULLTEXT indeksi tanımlıysa, sorgunuzu şu şekilde optimize edebilirsiniz:
public function searchFullText($keyword) {
$sql = "SELECT *, MATCH(baslik, icerik) AGAINST(:keyword IN BOOLEAN MODE) as score
FROM makaleler
WHERE MATCH(baslik, icerik) AGAINST(:keyword IN BOOLEAN MODE)
ORDER BY score DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute(['keyword' => $keyword]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Arama Motoru İçin Hata Ayıklama ve İzleme
Geliştirme aşamasında arama motorunun nasıl çalıştığını anlamak için sorgu sürelerini ve dönen sonuç sayılarını loglamak önemlidir. Aşağıdaki basit yapı ile arama performansını izleyebilirsiniz:
public function debugSearch($keyword) {
$start = microtime(true);
$results = $this->search($keyword);
$end = microtime(true);
$executionTime = $end - $start;
error_log("Arama süresi: " . $executionTime . " saniye. Terim: " . $keyword);
return $results;
}
Not: Canlı (production) ortamda error_log kullanımına dikkat edin. Çok fazla arama trafiği olan bir sitede, log dosyalarınızın gereksiz büyümesini engellemek için sadece hata durumlarında loglama yapmanız önerilir.
Sonuç
PHP ile nesne yönelimli yapıda bir arama motoru geliştirmek, hem kodunuzun temiz kalmasını sağlar hem de projenizin modülerliğini artırır. Bu rehberde öğrendiğiniz PDO kullanımı, SQL Injection engelleme ve sınıf tabanlı yapı, profesyonel web geliştirme dünyasının temel taşlarıdır. Bir sonraki adım olarak, arama sonuçlarına "anahtar kelime vurgulama" (highlight) özelliği ekleyebilir veya arama sonuçlarını AJAX ile sayfa yenilenmeden getiren bir yapı kurmayı deneyebilirsiniz. Başarılar dilerim.


Yorumlar (0)
Yorum Yaz