-- PHP

PHP 8.4 Geçişinde Sessizce Kırılan 7 Desen

Giriş

PHP 8.4’e geçiş, birçok geliştirici için “dokunma korkusu” yaratır. Ancak asıl tehlike her zaman sözdizimi hatası vermeyen sessiz davranış değişiklikleridir. Bu yazıda, 8.3’ten 8.4’e geçerken en çok karşılaşılan, göze çarpmayan kırılma desenlerini ve her birinde gerçek bir uygulama örneğini paylaşacağım.

1. Deprecated implicit nullable parametreler

PHP 8.4’te function foo(Foo $bar = null) biçimindeki yazım deprecated ilan edildi. Bu, “varsayılan değeri null olan ama tipi açıkça nullable işaretlenmemiş” parametredir. 8.4’te yalnızca uyarı verir; 9.0’da tamamen yasaklanacak.

// Eski (8.4'te deprecation)
function send(Foo $mailer = null): void {}

// Yeni doğru kullanım
function send(?Foo $mailer = null): void {}

Tespit: Loglarda Implicitly marking parameter as nullable veya PHPStan/Psalm ile tarama. Reflection ile kod tabanını toplu taramak en garanti yöntem.

2. Regex: preg_replace’in null dönme tuzağı

preg_replace desen eşleşmediğinde değil, regex hatası oluştuğunda null döner. Özellikle sonradan gelen PCRE sürümlerinde bir desenin bir sürümde geçerli olup diğerinde geçersiz hale gelmesi yaygın. Sonuç: değer sessizce silinir.

$content = file_get_contents('backup.sql');
// \K başlığı PHP < 7.3'te desteklenmiyordu, şimdilerde farklı davranabiliyor
$result = preg_replace('/#SEC([\s\S]*?)#END/', '', $content);
if ($result === null) {
    // REGEX HATASI! preg_last_error() ile inceleyin, asla sessiz geçmeyin
    throw new RuntimeException('preg_replace hatası: ' . preg_last_error_msg());
}

// Daha da önemlisi: $count parametresi
$count = -1;
$result = preg_replace('/\d+/', 'X', $content, -1, $count);
// değişen satır sayısını görmek için $count'u kullanın

Gerçek bir olay: bir yedekleme scripti, PHP sürüm yükseltmesi sonrası regex deseninin geçersiz hale gelmesiyle null döndü ve script dosyanın tamamı boşaltılmış halini yazdı. Dosya bütünlüğü kontrolü olmadığı için sorun günlerce fark edilmedi. Her regex sonucunda null kontrolü yapın ve dosya yazmadan önce boyut doğrulayın.

3. each() ve eski array fonksiyonlarının yok oluşu

each() PHP 8.0’da deprecated, 8.0’da kaldırılmıştı. Ancak eski kod tabanlarında while (list($k, $v) = each($arr)) hâlâ karşımıza çıkabilir. Aynı şekilde create_function() ve preg_replace()ı /e modifikatörüyle kullanmak çağ dışı.

// Yasak: create_function
$func = create_function('$a', 'return $a * 2;');

// Modern: closure
$func = static fn($a) => $a * 2;

4. Sıkı tipler ve int/string zorlama davranışı

PHP declare(strict_types=1) aktifse, sayısal dizeyi int parametreye geçirmek artık otomatik dönüştürülmez; TypeError fırlatır. Aktif değilse, PHP 8.1’den itibaren bazı otomatik dönüştürmeler kademeli ve deprecation uyarılı hale getirildi.

declare(strict_types=1);
function power(int $n): int { return $n * $n; }
power('4'); // TypeError: Argument #1 must be of type int, string given

5. MySQL ve mysqli: Boş sonuç kümeleri

PHP 8’in mysqli davranışındaki değişikliklerden biri, hata mesajlarının ayrıntı düzeyidir. mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) kullanmıyorsanız hatalar sessizdir ve `false` dönüşlerini elle kontrol etmezseniz hata zinciri kopar.

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
    $result = $db->query('SELECT * FROM posts');
    // artık sorgu hataları exception ile gelir
} catch (mysqli_sql_exception $e) {
    error_log('DB: ' . $e->getMessage());
}

6. Güncelleme sırasında yapılmaması gerekenler

  • Üretime tek adımda geçmeyin: önce geliştirme ortamında E_ALL açın.
  • Logları 48 saat inceleyin — service-provider’larda sessiz deprecation’lar patlar.
  • composer.json içinde eski paketleri okuyun; pek çok eski paket 8.4’le uyumsuzdur.

Özet

PHP 8.4’e geçişteki gerçek risk sözdizimi değil sessizliktir. Özellikle regex, deprecation ve tip zorlaması kontrollerinizi otomatikleştirin; dosya yazma işlemlerinde her sonucu doğrulayın.