PHP 8.0: Haberlere Genel Bakış (1/4)

3 yıl önce Kimden David Grudl  

PHP 8.0 sürümü şu anda yayınlanıyor. Daha önce hiçbir sürümde olmadığı kadar yeni şeylerle dolu. Tanıtımı dört ayrı makaleyi hak ediyordu. İlkinde dil seviyesinde neler getirdiğine bir göz atacağız.

PHP konusuna girmeden önce, Nette'nin mevcut sürümünün sekizinci sürüm için tamamen hazır olduğunu belirtelim. Ayrıca, bir hediye olarak, tam uyumlu bir Nette 2.4 yayınlandı, bu nedenle çerçeve açısından kullanmanızı engelleyen hiçbir şey yok.

İsimlendirilmiş argümanlar

Hemen bir bomba ile başlayalım, ki bu bomba cesurca bir oyun değiştirici olarak tanımlanabilir. Argümanlar artık fonksiyonlara ve metotlara sadece konumlarına göre değil, isimlerine göre de aktarılabiliyor. Bir metodun gerçekten çok fazla parametresi olması durumunda bu kesinlikle harika bir şey:

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 pozisyonel olarak, diğerleri ise isimleriyle aktarılır: (isimler pozisyonel olanlardan sonra gelmelidir)

$response->setCookie('lang', $lang, sameSite: 'None');

// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');

Bu özellik sunulmadan önce Nette'de çerez göndermek için yeni bir API oluşturma planları vardı, çünkü setCookie() için argümanların sayısı gerçekten artmıştı ve konumsal gösterim kafa karıştırıcıydı. Artık buna gerek yok, çünkü adlandırılmış argümanlar bu durumda en uygun API'dir. IDE bunları işaret edecektir ve tip güvenliği vardır.

Kullanımlarının gerekli olmadığı mantıksal argümanları açıklamak için bile idealdirler, ancak düz bir true veya false bunu kesmez:

// before
$db = $container->getService(Database::class, true);

// now
$db = $container->getService(Database::class, need: true);

Argüman adları artık genel API'nin bir parçasıdır. Artık bunları isteğe bağlı olarak değiştirmek mümkün değil. Bu nedenle Nette bile tüm argümanların uygun bir isme sahip olup olmadığını belirleyen bir denetimden geçiyor.

Adlandırılmış bağımsız değişkenler değişkenlerle birlikte de kullanılabilir:

function variadics($a, ...$args) {
	dump($args);
}

variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]

$args dizisi artık sayısal olmayan anahtarlar bile içerebilir, bu da bir tür BC molasıdır. Aynı durum, $args dizisindeki anahtarların artık çok daha önemli bir rol oynadığı call_user_func_array($func, $args) davranışı için de geçerlidir. Aksine, func_*() ailesinin fonksiyonları isimlendirilmiş argümanlara karşı korumalıdır.

İsimlendirilmiş argümanlar, splat operatörünün ... artık ilişkisel dizileri genişletebilmesiyle yakından ilgilidir:

variadics(...['b' => 2, 'c' => 3]);

Şaşırtıcı bir şekilde şu anda dizilerin içinde çalışmıyor:

$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys

Adlandırılmış argümanlar ve değişkenlerin kombinasyonu, örneğin link() metodu için, adlandırılmış argümanların yanı sıra konumsal argümanları da aktarabileceğimiz sabit bir sözdizimine sahip olma seçeneği sunar:

// before
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // had to be an array

// now
$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ış argümanlara serinin üçüncü bölümünde niteliklerle bağlantılı olarak geri döneceğiz.

Bir ifade bir istisna fırlatabilir

İstisna fırlatmak artık bir ifadedir. Parantez içine alabilir ve bir if koşuluna ekleyebilirsiniz. Hmmm, bu kulağa pek pratik gelmiyor. Ancak, bu çok daha ilginç:

// before
if (!isset($arr['value'])) {
	throw new \InvalidArgumentException('value not set');
}
$value = $arr['value'];


// now, when throw is an expression
$value = $arr['value'] ?? throw new \InvalidArgumentException('value not set');

Ok fonksiyonları şimdiye kadar yalnızca bir ifade içerebildiğinden, bu özellik sayesinde artık istisnalar atabilirler:

// only single expression
$fn = fn() => throw new \Exception('oops');

Eşleşme İfadesi

switch-case deyiminin iki önemli kusuru vardır:

  • == yerine katı olmayan karşılaştırma kullanır. ===
  • break adresini yanlışlıkla unutmamak için dikkatli olmalısınız

Bu nedenle PHP, katı karşılaştırma kullanan ve tersine break kullanmayan yeni bir match ifadesi biçiminde bir alternatifle birlikte gelir.

switch kod örneği:

switch ($statusCode) {
    case 200:
    case 300:
        $message = $this->formatMessage('ok');
        break;
    case 400:
        $message = $this->formatMessage('not found');
        break;
    case 500:
        $message = $this->formatMessage('server error');
        break;
    default:
        $message = 'unknown status code';
        break;
}

Ve aynısı (sadece katı karşılaştırma ile) match kullanılarak yazılmıştır:

$message = match ($statusCode) {
    200, 300 => $this->formatMessage('ok'),
    400 => $this->formatMessage('not found'),
    500 => $this->formatMessage('server error'),
    default => 'unknown status code',
};

match adresinin 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 örneğinde olduğu gibi daha fazla adım yazmak mümkün değildir.

Eşleşme olmaması durumunda (ve varsayılan cümle yoksa), UnhandledMatchError istisnası atılır.

Bu arada, Latte'de {switch}, {case} ve {default} etiketleri de bulunmaktadır. Bunların işlevleri yeni match ile tam olarak örtüşmektedir. Sıkı karşılaştırma kullanırlar, break gerektirmezler ve case içinde virgülle ayrılmış birden fazla değer belirtmek mümkündür.

Nullsafe operatörü

İsteğe bağlı zincirleme, null ile karşılaştığında değerlendirmesi durdurulan bir ifade yazmanıza olanak tanır. Bu, yeni ?-> operatörü sayesinde gerçekleşir. Aksi takdirde tekrar tekrar null olup olmadığını kontrol etmek zorunda kalacak birçok kodun yerini alır:

$user?->getAddress()?->street

// approximately translates to
$user !== null && $user->getAddress() !== null
	? $user->getAddress()->street
	: null

Neden “yaklaşık olarak”? Çünkü gerçekte ifade daha ustaca değerlendiriliyor, böylece hiçbir adım tekrarlanmıyor. Örneğin, $user->getAddress() yalnızca bir kez çağrılır, bu nedenle yöntemin ilk ve ikinci kez farklı bir şey döndürmesinden kaynaklanan sorunla karşılaşılamaz.

Bu özellik bir yıl önce Latte tarafından getirilmişti. Şimdi PHP'nin kendisi bunu benimsiyor. Harika.

Kurucu özellik tanıtımı

Türü iki kez ve değişkeni dört kez yazmaktan kurtaran sözdizimsel şeker. Bugün bunu bizim için yazan bu kadar akıllı IDE'lere sahip olmadığımız bir zamanda gelmemiş olması üzücü 🙂

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, kullanmaya başlayabilirsiniz.

Aritmetik ve bitsel işleçler için daha sıkı tür denetimleri

Bir zamanlar dinamik betik dillerinin öne çıkmasına yardımcı olan şey, onların en zayıf noktası haline geldi. Bir zamanlar PHP “sihirli tırnak işaretlerinden”, global değişken kayıtlarından kurtuldu ve şimdi rahat davranışın yerini katılık alıyor. PHP'de hiçbir anlam ifade etmeyen hemen her veri türünü ekleyebildiğiniz, çarpabildiğiniz vs. zamanlar çoktan geride kaldı. Sürüm 7.0'dan başlayarak, PHP gittikçe daha katı hale geliyor ve sürüm 8.0'dan bu yana, diziler, nesneler veya kaynaklar üzerinde herhangi bir aritmetik / bitsel operatör kullanma girişimi TypeError ile sona eriyor. Bunun istisnası dizilerin eklenmesidir.

// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:

Daha sağlıklı dize-sayı karşılaştırmaları

Ya da gevşek operatörü tekrar harika hale getirin.

Görünüşe göre artık == gevşek operatörüne yer yok, bu sadece === yazarken yapılan bir yazım hatası, ancak bu değişiklik onu tekrar haritaya geri döndürüyor. Eğer zaten varsa, bırakın makul davransın. Örneğin, daha önceki “mantıksız” karşılaştırmanın bir sonucu olarak, in_array() sizi hoş olmayan bir şekilde trolleyebilir:

$validValues = ['foo', 'bar', 'baz'];
$value = 0;

dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false

== adresinin davranışındaki değişiklik, sayıların ve “sayısal” dizelerin karşılaştırılmasıyla ilgilidir ve aşağıdaki tabloda gösterilmiştir:

Karşılaştırma Önce 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ı bir şekilde, herhangi bir dirençle karşılaşmadan onaylanan, dilin tam merkezinde yer alan bir BC kırılmasıdır. Ve bu iyi bir şey. JavaScript bu konuda çok kıskanç olabilir.

Hata raporlama

Birçok dahili fonksiyon artık göz ardı edilmesi kolay olan uyarılar yerine TypeError ve ValueError'ı tetikliyor. Bir dizi çekirdek uyarısı yeniden sınıflandırıldı. Shutup operatörü @ artık ölümcül hataları susturmuyor. Ve PDO varsayılan olarak istisnaları fırlatır.

Nette her zaman bunları bir şekilde çözmeye çalışmıştır. Tracy shutup operatörünün davranışını değiştirdi, Database PDO davranışını değiştirdi, Utils standart fonksiyonların yerine kolay gözden kaçan uyarılar yerine istisnalar atan fonksiyonlar içeriyor vs. Nette'in DNA'sında bulunan katı yönün dilin doğal yönü haline geldiğini görmek güzel.

Negatif dizi anahtarı artışları

$arr[-5] = 'first';
$arr[] = 'second';

İkinci elementin anahtarı ne olacak? Eskiden 0, since PHP 8 it’s -4 idi.

Sondaki virgül

Sondaki virgülün olamayacağı son yer, fonksiyon argümanlarının tanımıydı. Bu artık geçmişte kaldı:

	public function __construct(
		Nette\Database\Connection $db,
		Nette\Mail\Mailer $mailer, // trailing comma
	) {
		....
	}

$object::class

Sihirli sabit ::class aynı zamanda $object::class nesneleriyle de çalışır ve get_class() işlevinin tamamen yerini alır.

İstisnaları yalnızca türe göre yakalayın

Ve son olarak: catch cümlesinde istisna için bir değişken belirtmek gerekli değildir:

try {
	$container->getService(Database::class);

} catch (MissingServiceException) {  // no $e
	$logger->log('....');
}

Sonraki bölümlerde, veri türleriyle ilgili önemli yenilikleri göreceğiz, özniteliklerin ne olduğunu göstereceğiz, PHP'de hangi yeni işlevlerin ve sınıfların ortaya çıktığını göstereceğiz ve Tam Zamanında Derleyiciyi tanıtacağız.