PHP 8 ile gelen yenilikler

Isa Eken
7 min readJan 9, 2021

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

Union Types (Birleşik Tipler)

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));

JIT (Just In Time / Tam Zamanında)

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.

Nullsafe İşleci

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.

Adlandırılmış Değişkenler (Named arguments)

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 (Attributes)

Ö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) {}
}

Match İfadesi

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',
};

“static” Dönüş Türü

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;
}
}

“mixed” Dönüş Türü

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

Throw ifadesi

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

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

Özel fonksiyonlar için güncelleme

Ö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

Objelerde ::class

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

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

Hataları almadan hata yakalamak

Ö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.

Parametre listesinde son virgül

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,) {
// ...
}

Interface kullanarak DateTime objesi oluşturmak

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);

Stringable

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!");

str_contains

Ö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")) {}

str_starts_with ve str_ends_with fonksiyonları

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

fdiv() fonksiyonu

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

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.

get_resource_id() fonksiyonu

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);

Traitlerde değişken tipleri

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;
}
}

Dahili fonksiyonlar için tür açıklamaları

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.

ext-json her zaman kullanılabilir

Ö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.

Tutarlı tür hataları

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.

Uyarılar yeniden sınıflandırıldı

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

@ operatorü artık önemli hataları susturmuyor

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!

Varsayılan hata seviyesi

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.

Varsayılan PDO hata modu

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.

Birleştirme önceliği

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);

Aritmetik ve bitsel operatörler için daha katı tip kontrolleri

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;

Namespace’li isimler artık tek bir belirteç

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.

string ve number karşılaştırmaları

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

Kararlı sıralama

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.

Uyumsuz methodlar için önemli hata düzeltmesi

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.”

--

--