PHP 7.4 / 8.0'dan PHP 8.3 ve Sonrasına Geçiş: 7 Uyumluluk Sorunu ve Çözümü

Eski bir PHP projesini güncellerken yalnızca sunucudaki sürümü değiştirmek yeterli olmayabilir. Daha önce çalışan kodlar hata verebilir, loglarda yeni uyarılar belirebilir veya bir karşılaştırmanın sonucu sessizce değişebilir.

Bu rehber, PHP 7.4 veya 8.0 tabanlı projeleri PHP 8.3, 8.4 ya da 8.5'e taşırken kontrol edilmesi gereken 7 uyumluluk sorununu örneklerle açıklar. Değişikliklerin çoğu PHP 8.0–8.2'de başlamıştır; eski bir sürümden doğrudan yükseltme yaparken bunlar da geçiş kapsamına girer. Odak noktası, eski projelerden taşınan uyumluluk sorunlarıdır; bu yazı PHP 8.5’e özgü değişikliklerin tam bir dökümü değildir.

6 Eylül 2026 itibarıyla: PHP 7.4, 8.0 ve 8.1 destek dışındadır. PHP 8.2 ve 8.3 yalnızca güvenlik güncellemeleri alırken PHP 8.4 ve 8.5 aktif destek almaktadır. Hedef sürümü seçerken uygulamanızın, framework'ünüzün ve eklentilerinizin uyumluluğunu da değerlendirin. PHP destek takvimi

Önce Uyarı ile Hatayı Ayıralım

Her uyumluluk sorunu uygulamayı hemen durdurmaz:

  • Deprecated: Kullanımın terk edilmesi gerektiğini bildirir. PHP normalde çalışmayı sürdürür; uygulamanın hata işleyicisi bu uyarıyı istisnaya dönüştürebilir.
  • TypeError / ArgumentCountError: Yanlış tip veya eksik argüman gibi durumlarda oluşur. Yakalanmayan istisna mevcut çalışmayı sonlandırır.
  • Parse error / Fatal error: Kodun derlenmesini ya da çalışmasını engelleyebilir.
  • Davranış değişikliği: Hata mesajı üretmeden farklı sonuç verebilir; testlerle yakalanmalıdır.

1. Dinamik Özellik Oluşturma Uyarısı — PHP 8.2+

Bir nesneye, sınıfında tanımlanmamış bir özellik atamak PHP 8.2'den itibaren genel olarak deprecated uyarısı üretir. Kabul edilen RFC'ye göre bu kullanım PHP 9.0'da Error olacaktır; izin verilen istisnalar kapsam dışındadır. Dinamik özellikler RFC'si

Sorunlu Örnek

class Kullanici
{
    public string $isim = '';
}

$kullanici = new Kullanici();
$kullanici->yas = 30;
// PHP 8.2+: Deprecated: Creation of dynamic property Kullanici::$yas is deprecated

Kalıcı Çözüm

Özelliği sınıfta açıkça tanımlayın:

class Kullanici
{
    public string $isim = '';
    public ?int $yas = null;
}

Geçici Uyumluluk Seçeneği

Dinamik özelliklere gerçekten ihtiyaç duyan eski bir sınıfta şu öznitelik kullanılabilir:

#[\AllowDynamicProperties]
class EskiVeriNesnesi
{
}

stdClass bu uyarıdan muaftır. __set() ile yönetilen atamalar da ayrı değerlendirilir; ancak __set() içinde tekrar tanımsız bir nesne özelliği oluşturmak uyarıya neden olabilir. Esnek verileri sınıfta tanımlı bir dizide saklamak bir alternatiftir.

2. İsteğe Bağlı Parametrenin Zorunlu Parametreden Önce Gelmesi — PHP 8.0+

Varsayılan değeri olan bir parametreden sonra zorunlu parametre tanımlamak PHP 8.0'dan itibaren deprecated durumundadır. Fonksiyon tanımı uyarı üretir; eksik argümanla çağırmak ayrıca hata oluşturabilir. PHP 8.0 değişikliği

Sorunlu Örnek

function yaziEkle($kategori = 'Genel', $baslik)
{
    // Yazıyı kaydet.
}
// Deprecated: Optional parameter $kategori declared before required parameter
// $baslik is implicitly treated as a required parameter

PHP 8.0’dan yükseltenler için: PHP 8.1’den itibaren bu parametre, adlandırılmış argümanlarla (named arguments) çağrıldığında da zorunlu kabul edilir. Yukarıdaki tanımda yalnızca başlığı vermek artık yeterli değildir. PHP 8.1 parametre değişikliği

// Yukarıdaki sorunlu fonksiyon tanımı için:
yaziEkle(baslik: 'PHP Geçiş Rehberi');
// PHP 8.0: Tanım uyarısına rağmen varsayılan kategoriyle çalışır.
// PHP 8.1+: ArgumentCountError; $kategori argümanı verilmemiştir.

Çözüm

Kategori gerçekten isteğe bağlı olacaksa sona taşıyın ve konumsal argüman kullanan çağrıları güncelleyin:

function yaziEkle($baslik, $kategori = 'Genel')
{
    // Yazıyı kaydet.
}

yaziEkle('PHP Geçiş Rehberi');
yaziEkle('PHP Geçiş Rehberi', 'Yazılım');

İki argüman da zaten her çağrıda veriliyorsa parametre sırasını koruyup gereksiz varsayılan değeri kaldırabilirsiniz:

function yaziEkle($kategori, $baslik)
{
    // Yazıyı kaydet.
}

Bu örnekler alternatif tanımlardır; aynı dosyada birlikte kullanılmamalıdır.

3. String ve Sayı Karşılaştırmalarının Değişmesi — PHP 8.0+

PHP 8.0, sayı ile sayısal olmayan string arasındaki gevşek karşılaştırmanın davranışını değiştirdi. Bu durum herhangi bir hata vermeden koşullarınızın farklı çalışmasına neden olabilir. PHP 8.0 karşılaştırma değişiklikleri

İfade PHP 7.4 PHP 8.0+
0 == "yazi" true false
0 == "" true false
42 == "42" true true
42 == "42foo" true false

Çözüm

Önce verinin beklenen tipini belirleyin, ardından uygun karşılaştırmayı kullanın. Her == ifadesini otomatik olarak === yapmak da mevcut davranışı değiştirebilir:

$durum = '0';

var_dump($durum == 0);  // true
var_dump($durum === 0); // false: string ile int farklı tiplerdir

Örneğin alanınız yalnızca tam sayı 0 veya string '0' değerlerini kabul ediyorsa bunu açıkça ifade edebilirsiniz:

if (in_array($durum, [0, '0'], true)) {
    // Kabul edilen iki gösterimden biri geldi.
}

Kullanıcı girdisini sayıya çevirecekseniz önce geçerliliğini doğrulayın. Kontrolsüz (int) dönüşümü geçersiz bir metni de 0 yaparak sorunu gizleyebilir. Gevşek karşılaştırma kullanan in_array(), array_search() ve switch akışlarını da inceleyin.

4. Dahili Fonksiyonlara Null Verilmesi — PHP 8.1+

PHP 8.1'den itibaren, null kabul etmeyen skaler parametrelere sahip dahili fonksiyonlara null geçirmek, tip dönüştürmenin açık olduğu normal kullanımda deprecated uyarısı üretir. Dönüşüm PHP 8.x'te uyarıyla birlikte devam edebilir. strict_types=1 altında bu tür kullanım TypeError üretir. Kural, null kabul ettiğini açıkça belirten parametreleri kapsamaz. Null argüman RFC'si

Sorunlu Örnek

$veri = null;
$uzunluk = strlen($veri);
// PHP 8.1+, strict_types kapalı:
// Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated

Çözüm

İş kuralınızda null, boş metin anlamına geliyorsa varsayılan değer kullanın:

$uzunluk = strlen($veri ?? '');

Ancak eksik veri bir hataysa boş metne dönüştürerek gizlemeyin; girdiyi doğrulayın ve eksik veri durumunu ayrıca ele alın. ?? yalnızca null veya tanımsız değişken durumunu karşılar; dizi gibi yanlış tipleri düzeltmez.

5. Dizi ve String Erişiminde Süslü Parantezin Kaldırılması — PHP 8.0+

$dizi{'anahtar'} ve $metin{0} biçimindeki erişim PHP 7.4'te deprecated olmuş, PHP 8.0'da kaldırılmıştır. PHP 8.0 uyumsuzlukları

Sorunlu Örnek

$metin = 'Modern PHP';
echo $metin{0};
// Fatal error: Array and string offset access syntax with curly braces is no longer supported

Çözüm

Köşeli parantez kullanın:

$metin = 'Modern PHP';
echo $metin[0]; // M

$dizi = ['anahtar' => 'deger'];
echo $dizi['anahtar']; // deger

Bu değişiklik erişim sözdizimiyle ilgilidir; string içindeki değişken yerleştirme ifadelerine toplu parantez değişimi uygulamayın.

6. Dahili Arayüzlerde Dönüş Tipi Uyumluluğu — PHP 8.1+

Countable, ArrayAccess ve JsonSerializable gibi dahili arayüzlerin bazı metotlarında geçici olarak bildirilen dönüş tipleri bulunur. Bunları uygulayan metotta uyumlu dönüş tipi bulunmaması PHP 8.1'den itibaren deprecated uyarısı oluşturabilir. Dahili metotlarda dönüş tipi uyumluluğu

Sorunlu Örnek

class Depo implements \Countable
{
    public function count()
    {
        return 5;
    }
}
// PHP 8.1+: Depo::count() dönüş tipinin Countable::count(): int ile
// uyumlu olması gerektiğini belirten deprecated uyarısı oluşur.

Kalıcı Çözüm

class Depo implements \Countable
{
    public function count(): int
    {
        return 5;
    }
}

Eski sürümlerle uyumluluk nedeniyle tipi hemen ekleyemiyorsanız ilgili metotta geçici olarak #[\ReturnTypeWillChange] kullanılabilir. Bu öznitelik dönüş tipini düzeltmez; ilgili uyarıyı bastırır.

İleri not: Kalıtımda dönüş tipleri uyumlu olmalıdır; kovaryans daha özel bir dönüş tipine izin verebilir. ReturnTypeWillChange, genel metot imzası hatalarını gidermez. Tip uyumluluğu kuralları

7. MySQLi Hatalarının İstisna Üretmesi — PHP 8.1+

PHP 8.1’de MySQLi’nin varsayılan hata raporlama modu MYSQLI_REPORT_OFF yerine MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT olmuştur. Uygulama bu modu ayrıca değiştirmediyse veritabanı hataları mysqli_sql_exception üretir. Daha önce false dönen sorgu sonucunu kontrol eden kod, artık bu kontrole ulaşmadan istisna oluşturabilir. PHP 8.1 MySQLi değişikliği

Sorunlu Akış

Aşağıdaki örneklerde $db, uygulamanın oluşturduğu açık bir mysqli bağlantısıdır. eski_tablo tablosunun bulunmadığını varsayalım:

$sonuc = $db->query('SELECT id FROM eski_tablo');

if ($sonuc === false) {
    // PHP 8.1+ varsayılan modunda sorgu istisna ürettiğinden
    // hata yönetimi bu satıra ulaşmaz.
}

Çözüm

Hata modunu uygulamanın başlangıcında açıkça belirleyin; sorgu hatalarını uygun katmanda yakalayın:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    $sonuc = $db->query('SELECT id FROM eski_tablo');
    // Başarılı sorgunun sonucunu işle.
} catch (mysqli_sql_exception $e) {
    error_log('Veritabanı sorgusu başarısız: ' . $e->getMessage());
    // İşlemi başarısız olarak sonlandır; çağırana veya kullanıcıya
    // uygulamanın hata akışı üzerinden genel bir hata bildir.
}

Bu örnek sorgu aşamasını gösterir. Bağlantı kurulurken oluşan hataları da uygulamanın hata yönetimine dahil edin. İstisnayı yalnızca loglayıp işlemi başarılıymış gibi sürdürmeyin; transaction kullanıyorsanız geri alma akışını da ele alın. Ayrıntılı hata mesajlarını kullanıcıya göstermeyin.

Ek Not: match İsim Çakışması — PHP 8.0+

match, PHP 8.0’dan itibaren fonksiyon veya sınıf adı olarak kullanılamaz. Böyle bir adınız varsa matches gibi bir isim seçip çağrıları güncelleyin. Sınıf metodu adı olarak match geçerlidir: PHP anahtar kelimeleri

class Router
{
    public function match(string $url): bool
    {
        return $url === '/';
    }
}

$router = new Router();
var_dump($router->match('/')); // true; PHP 8.x'te geçerlidir.

PHP 8.4 ve 8.5 Hedefleyenler İçin Kapsam Notu

Yukarıdaki yedi maddeyi düzeltmek, sonraki sürümlerin bütün değişikliklerini kapsamaz. Örneğin PHP 8.4'te function kaydet(string $ad = null) biçimindeki örtük nullable parametre tanımı deprecated olmuştur. Açık nullable tip kullanın: PHP 8.4 deprecated özellikleri

function kaydet(?string $ad = null)
{
    // null açıkça kabul edilir.
}

Başlangıç ve hedef sürümünüz arasındaki tüm geçiş rehberlerini kontrol edin: PHP 8.0, PHP 8.1, PHP 8.2, PHP 8.3, PHP 8.4 ve PHP 8.5.

Geçiş Öncesi Kontrol Listesi

  1. Hedef ortamı hazırlayın. PHP sürümü, eklentiler ve yapılandırma bakımından canlıya benzeyen bir staging ortamı oluşturun. Komut satırı PHP'si ile web sunucusunun kullandığı PHP sürümünün farklı olabileceğini kontrol edin.
  2. Bağımlılık gereksinimlerini doğrulayın. composer check-platform-reqs komutunu hedef PHP ortamında çalıştırın. Komut, kurulu paketlerin bildirdiği PHP ve eklenti gereksinimlerini gerçek ortamla karşılaştırır; config.platform ayarını dikkate almaz ve uygulama kodunun hatasız çalışacağını garanti etmez. Composer belgesi
  3. Otomatik araçları kontrollü kullanın. Rector ile hedef sürüme uygun dönüşümleri değerlendirin; değişiklikleri inceleyin. PHPStan veya Psalm ile statik analiz yapın. Araçların proje sürümünüzle uyumlu kurulum ve yapılandırmasını kendi belgelerinden takip edin: Rector, PHPStan, Psalm.
  4. Uyarıları görünür hale getirin. Test ortamında E_ALL seviyesinde log toplayın. Canlı ortamda hata ayrıntılarını ziyaretçilere göstermeyin; log üzerinden izleyin.
  5. Kritik akışları test edin. Birim testlerinin yanında giriş, form gönderimi, ödeme, dosya yükleme, API uçları ve zamanlanmış görevler gibi projede kullanılan akışları doğrulayın. Özellikle sessiz karşılaştırma değişikliklerine odaklanın.
  6. Yedek ve geri dönüş planı hazırlayın. Kaynak kodu, bağımlılık kilit dosyasını ve veritabanını yedekleyin. Sorun halinde önceki uygulama ve ortam sürümüne nasıl dönüleceğini belirleyin; varsa veritabanı değişikliklerinin geri dönüş uyumluluğunu da kontrol edin.
  7. Yayın sonrası davranışı izleyin. Hata loglarını, kritik işlemlerin başarı durumunu ve yanıt sürelerini takip edin. Performansı aynı iş yükü altında önceki ortamla karşılaştırın.

Sonuç

PHP sürüm yükseltmesi, desteklenen bir ortamda çalışmak ve kod tabanındaki uyumsuzlukları gidermek için önemli bir bakım adımıdır. Yeni dil özellikleri geliştirmeyi kolaylaştırabilir; performans kazanımı ise uygulamaya, eklentilere ve sunucu yapılandırmasına bağlıdır. Ölçüm yapmadan belirli bir hız artışı beklemeyin.

Geçişi güvenilir kılan yaklaşım; değişiklikleri sürüm bazında incelemek, kodu ve bağımlılıkları birlikte güncellemek, kritik akışları test etmek ve geri dönüş planıyla yayınlamaktır. Bu yedi kontrol, o sürecin başlangıç noktasıdır.

Not: Örnekler konuyu açıklamak içindir; aynı sınıf veya fonksiyonun önce/sonra tanımlarını tek dosyada birlikte çalıştırmayın. Sürüm yükseltmesini önce test ortamında deneyin ve projenize özgü gereksinimleri doğrulayın.