PHP 8 ile gelen yenilikler

Bildiğiniz üzere PHP’nin yeni stabil versiyonu 8 kısa bir süre önce hayatımıza girdi. Peki PHP 8'deki yenilikler ve hata düzeltmeleri neler?

Dikkat: PHP 8 büyük bir güncelleme ve önemli değişiklikler içeriyor. Sisteminizi güncellemeden önce yapabileceğiniz en iyi şey yükseltme belgesindeki son değişikliklerin tam listesine bakmanız olucaktır.

PHP 8'deki Yenilikler

Bildiğiniz üzere PHP’nin önceki versiyonlarında bir değişkenin tipini belirleyebiliyorduk. Örneğin insan(string $isim, int $cinsiyet) fonksiyonu kişinin cinsiyetini döndürüyor ise function insan(string $isim, int $cinsiyet): stdClass gibi.

Artık birden fazla değişken tipi belirleyebiliyoruz. İnsan fonksiyonu \Dunya\Canli\Insan sınıfını ve $cinsiyet değişkeni \Dunya\Canli\Erkek veya \Dunya\Canli\Kadin sınıfınıda olabilir.

Peki bunu nasıl kullanabiliriz? Eskiden tipleri sadece string olarak belirtiyorsak artık string|int|boolean gibi uzatabiliyoruz. Örneğin;

function insan(?string $isim, \Dunya\Canli\Erkek|\Dunya\Canli\Kadin|null $cinsiyet = null): \Dunya\Canli\Insan
{
return Insan::bilgiler($isim, $cinsiyet);
}
$isim = "nur";
$cinsiyet = new \Dunya\Canli\Kadin;
print_r(insan($isim, $cinsiyet));

Açılımı “Just In Time” Türkçeye “Tam Zamanında” olarak çevirilen yeni PHP derleyicisi HTTP istekleri öncelikli olmasada önemli performans iyileştirmeleri vaat ediyor. Detaylı bir makaleyi stitcher.io adresinden bulabilirsiniz.

Bu özelliği anlamanız için bir seneryo oluşturalım. Örneğin $insanlar değişkeniniz var ve insanlar whatsapp değişkenine sahipler fakat birinde bu değişken eksik ve bu altdaki kod bloğunda olduğu bir kod hata veriyor.

foreach($insanlar as $insan) {
merhaba($insan->whatsapp);
}

Bu hatayı basit olarakisset($insan->whatsapp) ? $insan->whatsapp : null şeklinde giderebiliriz ancak nullsafe işleci sayesinde bunu $insan?->whatsapp olarak düzeltebiliyoruz. Bu yeniliği ilerleyen zamanlarda sık sık göreceğiz frameworkler ve yazım standartlarda kullanılmaya başlayacağı şüphesiz.

class Insan {
public function __construct(
public string $isim,
public int $yas,
) {}
}

Yukardaki kod PHP dışında bir dile ait gibi görünebilir, evet artık sadece fonksiyon oluşturarak bir sınıfın içinde public bir değişken oluşturabiliyoruz. Ayrıca fonksiyonu çağırırken değişken isimlerini belirtebilirsiniz örneğin:

$insan = new Insan(
isim: "İsa",
yas: 19
);

Öznitellikler, docblock’ları ayrıştırmak zorunda kalmadan sınıflara meta veri eklemenin bir yolunu sunar. Kısa bir örnekle:

// OrnekAttribute.php
#[Attribute]
class OrnekAttribute {
public $value;
public function __construct($value) {
$this->value = $value;
}
}

Yukarıdaki kod bloğu ile bir öznitelik sınıfı oluşturmuş olduk şimdi aşağıdaki kod bloğundaki örnekteki gibi kullanıyoruz.

// Sini.php
$[OrnekAttribute]
class Sinif {
#[OrnekAttribute]
public int $sinif = 12;
#[OrnekAttribute]
public string $sute = 'B';
#[OrnekAttribute]
public function ogrenciEkle(#[OrnekAttribute] $ogrenci) {}
}

Buna switch’in kısayolu diyebiliriz. match, değerleri döndürebilir, break ifadeleri gerektirmez, koşulları birleştirebilir, katı tür karşılaştırmaları kullanır ve herhangi bir tür zorlaması yapmaz. Kullanımı altdaki kod bloğundaki gibidir.

$kod = 200;
$cevap = match($kod) {
200, 300 => null,
404 => 'bulunamadı',
500 => 'sunucu hatası',
default => 'bilinmeyen durum',
};

Artık sınıflarınızın içindeki fonksyonlarıdan static bir veri döndürebilirsiniz. Örneğin:

class Kullanici {
public function olustur($degerler): static {
$kullanici = clone $this;
$kullanici->olustur($degerler);
return $kullanici;
}
}

Daha önceden türleri birden fazla belirleyemiyorduk, PHP 8 ile birden fazla tür ile birlikte mixed türüde geldi. Bu türün anlamı ise değerin listedeki tiplerden herhangi biri olabiliceği anlamına geliyor.

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Örnek:

function kullaniciBul(): mixed {}

Not: mixed değeri nullable olamaz

Artık hataları kısaltılmış ifadeler ile kullanabiliyoruz. Örneğin;

$resim = $resimler[0] ?? throw new Hatalar\ResimYok();

Öncede, PHP aynı miras denetimlerini public, private ve protected methodlarına uygulardı. Başka bir deyişle: private methodlar, protected ve public methodlar ile aynı şekilde çalışıyordu. Private methodlara alt classlar tarafından erişilemiyordu ve bu mantıklı değildi.

Bu davranış değiştirildi, böylece bu devralma kontrolleri artık private methodlarda gerçekleştirilmiyor. Dahası, final private function kullanılması da mantıklı değildi, bu nedenle bunu yapmak artık bir uyarı tetkliyor:

Warning: Private methods cannot be final as they are never overridden by other classes

Bu yeni özellik, objelerde ::class kullanımına izin veriyor.

$ornek = new Ornek();
var_dump($ornek::class);

Önceden try/catch kullanımında hatayı almak zorundaydık artık PHP 8'de hatayı bir değişkene atamak zorunda değiliz. Örneğin önceden aşağıdaki gibi bir kod kullanmak zorundayken;

try {
// hata
} catch (Exception $exception) {
Log::error("Hata oluştu");
}

Artık burada $exception değişkenini atamak zorunda değiliz:

try {
// hata
} catch (Exception) {
Log::error("Hata oluştu");
}

Not: Her zaman türü belirtmeniz gerektiğini, boş bir exception hakkına sahip olamayacağınızı unutmayın. Tüm istisnaları ve hataları yakalamak istiyorsanız, yakalama türü olarak Throwable’ı kullanabilirsiniz.

Bildiğiniz üzere Array’larda zaten sona virgül eklenebiliyordu, PHP 8'de artık bir fonksiyon oluştururken parametrelerin sonunada virgül ekleyebileceğiz. Yani aşağıdaki kod hata vermeyecektir.

public function(string $ilkParametre, int $ikinciParametre,) {
// ...
}

Artık DateTimeInterface kullanarak DateTime objesi oluşturabiliyoruz. Bunun için iki yeni fonksiyon eklendi: DateTime::createFromInterface() ve DatetimeImmutable::createFromInterface()

DateTime::createFromInterface(DateTimeInterface $interface);  DateTimeImmutable::createFromInterface(DateTimeInterface $interface);

Artık Stringable interface’ini kullanabilirsiniz. Örnek kullanımı:

class Ornek implements \Stringable {
public function __toString(): string {
return "Merhaba Dünya!";
}
}
function ornekFonksyion(string|Stringable $yazi) {
var_dump($yazi);
}
ornekFonksiyon(new Ornek);
ornekFonksiyon("Merhaba Dünya!");

Önceden bir string içeriğinde aramak yapmak için aşağıdaki yöntem kullanılıyordu:

if (strpos("merhaba dünya", "merhaba") !== false) {}

Artık PHP 8'de bunu kullanabiliyoruz:

if (str_contains("merhaba dünya", "merhaba")) {}

Sonunda bu iki fonksiyon PHP’ye eklendi. Kullanımları:

str_starts_with('Merhaba Dünya', 'Merhaba'); // true
str_ends_with('Merhaba Dünya', 'Dünya'); // true

Yeni eklenen fdiv() fonksiyonu aslında fmod() ve intdiv() fonksiyonlarının bir benzeri. İşlevi 0'a bölmeye izin verir. Hata yerine duruma bağlı olarak INF, -INF veya NAN alırsınız.

get_debug_type() fonksiyonu değişkenin tipini döndürür.

Örneğin, getype() ile \Ornek sınıfındaki bir değişkeni kontrol ettiğinizde object döndüğünü görüceksiniz fakat get_debug_type() fonksiyonu class’ı döndürücektir.

get_debug_type() ve gettype() fonksiyonları arasındaki farkın tüm listesini buradan bulabilirsiniz.

Resource’lar, PHP’de harici resourcelara atıfta bulunan özel değişkenlerdir. Örnek olarak MySQL bağlantılarını veya dosyaları verebiliriz.

Bu resourceların her birine bir id atanır, ancak daha önce bu resourceları bilmenin tek yolu resource’u int’ye çevirmekti.

$resourceId = (int) $resource;

PHP 8'de bu fonksiyon ile bu işlem daha açık ve tür açısından daha güvenli hale getirildi.

$resourceId = get_resource_id($resource);
trait Ornek {
abtract public function test(int $degisken): int;
}
class Kullanim {
use Ornek;
public function test($degisken) {
return $degisken;
}
}

PHP 8'de değişkenlerin tiplerini belirtmemiz gerekmekte yani bu kod aşağıdaki gibi olmalı.

class Kullanim {
use Ornek;
public function test(int $degisken): int {
return $degisken;
}
}

Birçok insan tüm dahili fonksiyonlara açıklama yazmak için bir araya geldi. Uzun zamandır devam eden bu işlem sonunda tamamlandı ve PHP’nin yeni versiyonunda dahili fonksiyonların ve methodların detaylı bir dökümasyonu bulunmakta.

Önceden PHP’yi JSON uzantısı etkinleştirilmeden derlemek mümkündü, bu artık mümkün değil. JSON çok yaygın olarak kullanıldığından, geliştiriciler, uzantının önce var olduğundan emin olmak yerine, her zaman orada olduğuna güvenebilirler.

PHP’deki kullanıcı tanımlı işlevler zaten TypeError’ı vericektir, ancak dahili fonksiyonlar ve methodlar bunu yapmıyordu, bunun yerine uyarılar veriyor ve boş veri döndürüyorlardı. PHP 8'den itibaren dahili fonksiyon ve methodların davranışı tutarlı hale getirildi.

Daha önce yalnızca uyarıları veya bildirimleri tetikleyen birçok hata, doğrudan hatalara dönüştürüldü. Aşağıdaki uyarılar değiştirildi.

  • Undefined variable: notice yerine Error
  • Undefined array index: notice yerine warning
  • Division by zero: warning yerine DivisionByZeroError
  • Attempt to increment/decrement property ‘%s’ of non-object: warning yerineError
  • Attempt to modify property ‘%s’ of non-object: warning yerine Error
  • Attempt to assign property ‘%s’ of non-object: warning yerine Error
  • Creating default object from empty value: warning yerine Error
  • Trying to get property ‘%s’ of non-object: notice yerine warning
  • Undefined property: %s::$%s: notice yerine warning
  • Cannot add element to the array as the next element is already occupied: warning yerine Error
  • Cannot unset offset in a non-array variable: warning yerine Error
  • Cannot use a scalar value as an array: warning yerine Error
  • Only arrays and Traversables can be unpacked: warning yerine TypeError
  • Invalid argument supplied for foreach(): warning yerine TypeError
  • Illegal offset type: warning yerine TypeError
  • Illegal offset type in isset or empty: warning yerine TypeError
  • Illegal offset type in unset: warning yerine TypeError
  • Array to string conversion: notice yerine warning
  • Resource ID#%d used as offset, casting to integer (%d): notice yerine warning
  • String offset cast occurred: notice yerine warning
  • Uninitialized string offset: %d: notice yerine warning
  • Cannot assign an empty string to a string offset: warning yerine Error
  • Supplied resource is not a valid stream resource: warning yerine TypeError

Bu değişikliğin PHP 8'den önce gizlenmiş hataları ortaya çıkarması mümkün. Production modundaki sunucularınızda display_errors=off ayarını yaptığınızdan emin olun!

E_NOTICE ve E_DEPRECATED dışında her şey yerine artık E_ALL. Bu muhtemelen PHP 8'den önce mevcut olsa da, daha önce yok sayılmış bir çok hatanın ortaya çıkabileceği anlamına geliyor.

RFC’den: “PDO için geçerli varsayılan hata modu sessizdir. Bir SQL hatası olduğunda, geliştirici kendi hata işlemesini uygulamadıkça hiçbir hata veya uyarı verilemeyeceği ve hiçbir istisna atılmayacağı anlamına gelir.”

RFC, varsayılan hatayı değiştirerek PHP 8'de PDO::ERRMODE_EXCEPTION olarak değişirdi.

Böyle bir şey yazarsanız:

echo "örnek: " . $x + $y;

PHP önceden bunu altdaki gibi derleyecekti

echo ("örnek: " . $x) + $y;

Fakat PHP 8'de işlem önceliği değiştirildi ve bu işlem aşağdaki gibi çalıştırılıyor.

echo "örnek: " . ($x + $y);

PHP 8'den önce dizilere, kaynaklara veya nesnelere aritmetik veya bitsel operatörler uygulamak mümkündü. Bu artık mümkün değil ve bir TypeError atacak:

[] % [42];
$object + 4;

PHP, bir namespace’in her bir parçasını bir dizi belirteç olarak yorumlamak için kullanılırdı. Bu davranış değiştirildi ve isimler artık namespacelerde kullanılabilir.

PHP’de 0 == ‘test’ ifadesinin true olarak döndüğü garip bir hata düzeltildi.

PHP 8'den önce sıralama algoritmaları kararsızdı. Bu, eşit değerlerin sırasının garanti edilemediği anlamına geliyor. PHP 8'de tüm sıralama işlevlerinin davranışını geliştirildi.

RFC’den: “Uyumsuz yöntem imzalarından kaynaklanan devralma hataları, şu anda hatanın nedenine ve devralma hiyerarşisine bağlı olarak önemli bir hata veya bir uyarı verir.”

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store