PHP 8.0: Yeniliklere Genel Bakış (1/4)
PHP 8.0 sürümü yayınlandı. Daha önceki hiçbir sürümde olmadığı kadar yeniliklerle dolu. Tanıtımları tam dört ayrı makale gerektirdi. Bu ilkinde, dil açısından neler getirdiğine bakacağız.

PHP'ye dalmadan önce, Nette'nin mevcut sürümünün sekizinci sürüm için tamamen hazır olduğunu bilin. Dahası, hediye olarak Nette 2.4 de yayınlandı ve onunla tamamen uyumlu, bu nedenle framework açısından yeni sürümü kullanmaya başlamanıza hiçbir engel yok.
Adlandırılmış Argümanlar
Ve hemen oyunun kurallarını değiştiren olarak nitelendirilebilecek bir bomba ile başlayalım. Artık fonksiyonlara ve metotlara argümanlar sadece konumsal olarak değil, aynı zamanda isimlerine göre de iletilebilir. Bu, bir metodun gerçekten çok sayıda parametresi olduğunda kesinlikle harika:
class Response implements IResponse
{
public function setCookie(
string $name,
string $value,
string|DateInterface|null $time,
string $path = null,
string $domain = null,
bool $secure = null,
bool $httpOnly = null,
string $sameSite = null
) {
...
}
}
İlk iki argümanı konumsal olarak, diğerlerini isme göre ileteceğiz: (adlandırılmış olanlar her zaman konumsal olanlardan sonra gelmelidir)
$response->setCookie('lang', $lang, sameSite: 'None');
// çılgınca yerine
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Bu özelliğin gelmesinden önce, Nette'de çerez göndermek için yeni bir
API oluşturma planı vardı, çünkü setCookie()
parametrelerinin
sayısı gerçekten arttı ve konumsal yazım anlaşılmazdı. Artık buna gerek
yok, çünkü adlandırılmış argümanlar bu durumda en pratik API'dir.
IDE'ler onlara ipucu verecek ve tip kontrolüne sahip olacaklar.
Mantıksal parametreleri açıklamak için de harikadırlar; burada
kullanımları zorunlu olmasa da, tek başına true
veya
false
pek bir şey ifade etmez:
// eskiden
$db = $container->getService(Database::class, true);
// şimdi
$db = $container->getService(Database::class, need: true);
Parametre adları artık genel API'nin bir parçası haline geliyor. Şimdiye kadar olduğu gibi keyfi olarak değiştirilemezler. Bu nedenle, Nette de tüm parametrelerin uygun adlandırmaya sahip olup olmadığını kontrol etmek için bir denetimden geçiyor.
Adlandırılmış argümanlar variadics ile birlikte de kullanılabilir:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args içinde ['b' => 2, 'c' => 3] olacak
Artık $args
dizisi sayısal olmayan anahtarlar da içerebilir,
bu da belirli bir BC (Geriye Dönük Uyumluluk) kırılmasıdır. Aynı durum,
$args
dizisindeki anahtarların artık önemli bir rol oynadığı
call_user_func_array($func, $args)
fonksiyonunun davranışı için
de geçerlidir. Tersine, func_*()
ailesindeki fonksiyonlar
adlandırılmış argümanlardan etkilenmez.
Adlandırılmış argümanlarla yakından ilgili olan bir diğer gerçek de,
splat operatörü ...
'nın artık ilişkisel dizileri de
açabilmesidir:
variadics(...['b' => 2, 'c' => 3]);
Şaşırtıcı bir şekilde, bu henüz diziler içinde çalışmıyor:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Adlandırılmış argümanların ve variadics'in birleşimi, örneğin
presenter'ın link()
metodu için nihayet sabit bir sözdizimine
sahip olma imkanı verir; şimdi ona adlandırılmış argümanları konumsal
olanlar gibi iletebiliriz:
// eskiden
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // dizi olmak zorundaydı
// şimdi
$presenter->link('Product:detail', $id, page: 1);
Adlandırılmış argümanlar için sözdizimi, diziler yazmaktan çok daha
seksidir, bu nedenle Latte
bunu hemen benimsedi, örneğin {include}
ve
{link}
etiketlerinde kullanılabilir:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Adlandırılmış parametrelere üçüncü bölümde niteliklerle bağlantılı olarak geri döneceğiz.
İfade bir istisna fırlatabilir
Bir istisna fırlatmak artık bir ifadedir. Örneğin, parantez içine alıp
bir if
koşuluna ekleyebilirsiniz. Hmmm, bu pek pratik
görünmüyor. Ama bu daha ilginç:
// eskiden
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('değer ayarlanmadı');
}
$value = $arr['value'];
// şimdi, throw bir ifade olduğunda
$value = $arr['value'] ?? throw new \InvalidArgumentException('değer ayarlanmadı');
Ok fonksiyonları şimdiye kadar yalnızca tek bir ifade içerebildiğinden, bu özellik sayesinde istisnalar fırlatabilirler:
// sadece tek ifade
$fn = fn() => throw new \Exception('hay aksi');
Match İfadeleri
switch-case
yapısının iki büyük kusuru vardır:
===
yerine katı olmayan==
karşılaştırmasını kullanır- yanlışlıkla
break
unutmamaya dikkat etmelisiniz
Bu nedenle PHP, katı karşılaştırma kullanan ve tersine
break
kullanmayan yeni match
yapısı şeklinde bir
alternatifle geliyor.
switch
kodu örneği:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('tamam');
break;
case 400:
$message = $this->formatMessage('bulunamadı');
break;
case 500:
$message = $this->formatMessage('sunucu hatası');
break;
default:
$message = 'bilinmeyen durum kodu';
break;
}
Ve aynısı (sadece katı karşılaştırma ile) match
kullanılarak yazılmış:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('tamam'),
400 => $this->formatMessage('bulunamadı'),
500 => $this->formatMessage('sunucu hatası'),
default => 'bilinmeyen durum kodu',
};
match
'in switch
gibi bir kontrol yapısı değil,
bir ifade olduğuna dikkat edin. Örnekte sonuç değerini bir değişkene
atıyoruz. Aynı zamanda, bireysel “seçenekler” de ifadelerdir, bu nedenle
switch
durumunda olduğu gibi birden fazla adım yazılamaz.
Seçeneklerden hiçbiriyle eşleşme olmazsa (ve default yan tümcesi yoksa),
UnhandledMatchError
istisnası fırlatılır.
Bu arada, Latte'de de {switch}
, {case}
ve
{default}
etiketleri vardır. İşleyişleri tam olarak yeni
match
'e karşılık gelir. Katı karşılaştırma kullanırlar,
break
gerektirmezler ve case
'de virgülle ayrılmış
birden fazla değer belirtilebilir.
Nullsafe operatörü
İsteğe bağlı zincirleme (optional chaining), null ile
karşılaştığında değerlendirmesi duran bir ifade yazmayı sağlar. Ve bu,
yeni ?->
operatörü sayesinde olur. Aksi takdirde tekrar tekrar
null kontrolü yapacak birçok kodu değiştirir:
$user?->getAddress()?->street
// yaklaşık olarak şu anlama gelir
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Neden “yaklaşık olarak anlamına gelir”? Çünkü gerçekte ifade daha
akıllıca değerlendirilir ve hiçbir adım tekrarlanmaz. Örneğin,
$user->getAddress()
yalnızca bir kez çağrılır, bu nedenle
metodun ilk ve ikinci kez farklı bir şey döndürmesinden kaynaklanan bir
sorun oluşamaz.
Bu taze güzellik bir yıl önce Latte tarafından getirildi. Şimdi PHP'nin kendisine geliyor. Harika.
Constructor property promotion
Tipin iki kez ve değişkenin dört kez yazılmasından tasarruf sağlayan sözdizimsel şeker. Keşke bugün bizim için yazan bu kadar akıllı IDE'lere sahip olmadığımız bir zamanda gelmeseydi 🙂
class Facade
{
private Nette\Database\Connection $db;
private Nette\Mail\Mailer $mailer;
public function __construct(Nette\Database\Connection $db, Nette\Mail\Mailer $mailer)
{
$this->db = $db;
$this->mailer = $mailer;
}
}
class Facade
{
public function __construct(
private Nette\Database\Connection $db,
private Nette\Mail\Mailer $mailer,
) {}
}
Nette DI ile çalışır, hemen kullanmaya başlayabilirsiniz.
Aritmetik ve bit düzeyinde operatörlerin katı davranışı
Bir zamanlar dinamik betik dillerini zirveye taşıyan şey, zamanla en zayıf noktaları haline geldi. PHP bir zamanlar “magic quotes”, global değişkenlerin kaydedilmesi gibi şeylerden kurtuldu ve şimdi gevşek davranış yerini katılığa bırakıyor. PHP'de toplama, çarpma vb. işlemleri neredeyse hiçbir anlam ifade etmeyen hemen hemen her veri tipiyle yapabildiğiniz zamanlar çoktan geride kaldı. Sürüm 7.0'dan başlayarak PHP giderek daha katı hale geldi ve sürüm 8.0'dan itibaren diziler, nesneler veya kaynaklar üzerinde herhangi bir aritmetik/bit düzeyinde operatör kullanma girişimi TypeError ile sonuçlanıyor. İstisna, dizilerin toplanmasıdır.
// aritmetik ve bit düzeyinde operatörler
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Daha mantıklı karakter dizisi ve sayı karşılaştırmaları
Veya gevşek operatörü yeniden harika yapın.
Gevşek operatör ==
için artık yer olmadığı, sadece
===
yazarken yapılan bir yazım hatası olduğu düşünülebilir,
ancak bu değişiklik onu tekrar haritaya geri getiriyor. Madem elimizde var,
mantıklı davransın. Önceki “mantıksız” karşılaştırmanın bir
sonucu, örneğin sizi hoş olmayan bir şekilde yakalayabilen
in_array()
davranışıydı:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// şaşırtıcı bir şekilde true döndürüyordu
// PHP 8.0'dan itibaren false döndürüyor
==
davranışındaki değişiklik, sayıların ve “sayısal”
karakter dizilerinin karşılaştırılmasıyla ilgilidir ve aşağıdaki
tabloyla gösterilir:
Karşılaştırma | Öncesi | PHP 8.0 |
---|---|---|
0 == "0" |
true | true |
0 == "0.0" |
true | true |
0 == "foo" |
true | false |
0 == "" |
true | false |
42 == " 42" |
true | true |
42 == "42 " |
true | true |
42 == "42foo" |
true | false |
42 == "abc42" |
false | false |
"42" == " 42" |
true | true |
"42" == "42 " |
false | true |
Şaşırtıcı olan, bunun dilin temelinde bir BC kırılması olması ve herhangi bir direniş olmadan onaylanmasıdır. Ve bu iyi bir şey. JavaScript burada çok kıskanabilirdi.
Hata Raporlama
Birçok dahili fonksiyon artık kolayca gözden kaçırılabilecek uyarılar
yerine TypeError ve ValueError fırlatıyor. Çekirdekteki bir dizi uyarı
yeniden sınıflandırıldı. Shutup operatörü @
artık
ölümcül hataları susturmuyor. Ve PDO varsayılan modda istisnalar
fırlatıyor.
Nette her zaman bu tür şeyleri bir şekilde çözmeye çalıştı. Tracy, shutup operatörünün davranışını değiştirdi, Database PDO'nun davranışını değiştirdi, Utils, göze çarpmayan uyarılar yerine istisnalar fırlatan standart fonksiyonların yerine geçenleri içerir vb. Nette'nin DNA'sında bulunan katı yönelimin dilin doğal yönelimi haline geldiğini görmek güzel.
Negatif indeksli diziler
$arr[-5] = 'first';
$arr[] = 'second';
İkinci elemanın anahtarı ne olacak? Eskiden 0
idi, PHP 8'den
itibaren -4
.
Sondaki virgül
Sondaki virgülün olamayacağı son yer fonksiyon parametrelerinin tanımıydı. Bu artık geçmişte kaldı:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // sondaki virgül
) {
....
}
$object::class
Sihirli sabit ::class
, $object::class
nesneleriyle
de çalışır ve böylece get_class()
fonksiyonunu tamamen
değiştirir.
Değişkensiz catch
Ve son olarak: catch yan tümcesinde istisna için bir değişken belirtmek gerekli değildir:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // $e yok
$logger->log('....');
}
Gelecek bölümlerde veri tiplerindeki önemli yenilikler bizi bekliyor, niteliklerin ne olduğunu, PHP'de hangi yeni fonksiyonların ve sınıfların ortaya çıktığını göstereceğiz ve Just in Time Compiler'ı tanıtacağız.
Yorum göndermek için lütfen giriş yapın