PHP 8.0: Veri Türlerinde Yeni Gelişmeler (2/4)

2 yıl önce Kimden David Grudl  

PHP 8.0 sürümü henüz yayınlandı. Daha önceki hiçbir sürümde olmadığı kadar yeni özelliklerle dolu. Bunların tanıtımı dört ayrı makaleyi hak ediyor. İkinci bölümde, veri türlerine bir göz atacağız.

Geçmişe dönelim. Skaler tip ipuçlarının tanıtılması PHP 7'de önemli bir atılımdı. Neredeyse gerçekleşmiyordu. declare(strict_types=1) sayesinde tamamen geriye dönük uyumlu ve isteğe bağlı olan dahiyane çözümün yazarı Andreu Faulds topluluk tarafından sert bir şekilde reddedildi. Neyse ki, Anthony Ferrara o sırada onu ve öneriyi savundu, bir kampanya başlattı ve RFC çok yakından geçti. Vay be. PHP 8'deki haberlerin çoğu efsanevi Nikita Popov sayesindedir ve hepsi tereyağından kıl çeker gibi oylamadan geçmiştir. Dünya daha iyiye doğru değişiyor.

PHP 8 türleri mükemmelliğe taşıyor. @param , @return ve @var gibi phpDoc ek açıklamalarının büyük çoğunluğu yerel gösterimle değiştirilecektir. Ancak en önemlisi, tipler PHP motoru tarafından kontrol edilecektir. Sadece string[] gibi yapıların açıklamaları veya PHPStan için daha karmaşık ek açıklamalar yorumlarda kalacaktır.

Sendika Türleri

Birlik türleri, bir değişkenin kabul edebileceği iki veya daha fazla türün bir numaralandırmasıdır:

class Button
{
	private string|object $caption;

	public function setCaption(string|object $caption)
	{
		$this->caption = $caption;
	}
}

Bazı birlik türleri PHP'ye daha önce tanıtılmıştı. Örneğin nullable tipler. Örneğin ?string, string|null birlik türüne eşdeğerdir. Soru işareti gösterimi bir kısaltma olarak düşünülebilir. Elbette, PHP 8'de de çalışır, ancak dikey çubuklarla birleştiremezsiniz. Yani ?string|object yerine string|object|null yazmanız gerekir. Ayrıca, iterable her zaman array|Traversable ile eşdeğerdir. float 'nin de bir birlik türü olması sizi şaşırtabilir, çünkü her ikisini de kabul eder int|float, ancak değeri float'a atar.

Birliklerde void ve mixed sözde tiplerini kullanamazsınız çünkü bu bir anlam ifade etmez.

Nette birlik türleri için hazır. Schema'da, Expect::from() bunları kabul eder ve sunum yapanlar da bunları kabul eder. Bunları örneğin render ve action yöntemlerinde kullanabilirsiniz:

public function renderDetail(int|array $id)
{
	...
}

Öte yandan, Nette DI'daki otomatik bağlantı, birleşik türleri reddeder. Şimdiye kadar, kurucunun bir ya da başka bir nesneyi kabul etmesinin mantıklı olacağı bir kullanım durumu yoktur. Elbette, böyle bir kullanım durumu ortaya çıkarsa, konteynerin davranışını buna göre ayarlamak mümkün olacaktır.

Nette\Utils\Reflection adresindeki getParameterType(), getReturnType() ve getPropertyType() yöntemleri, birlik türü durumunda bir istisna oluşturur (bu, sürüm 3.1'de geçerlidir; önceki sürüm 3.0'da, bu yöntemler null uyumluluğunu korumak için geri döner).

mixed

mixed sözde tipi herhangi bir değeri kabul eder.

Parametreler ve özellikler söz konusu olduğunda, herhangi bir tür belirtmediğimizde olduğu gibi aynı davranışla sonuçlanır. Peki, amacı nedir? Bir türün ne zaman sadece eksik olduğunu ve ne zaman kasıtlı olarak eksik olduğunu ayırt etmek mixed.

İşlev ve yöntem geri dönüş değerleri söz konusu olduğunda, tür belirtmemek mixed kullanmaktan farklıdır. İşlevin bir şey döndürmesini gerektirdiği için void'un tam tersi anlamına gelir. Eksik bir dönüş değeri ölümcül bir hata oluşturur.

Pratikte bunu nadiren kullanmanız gerekir, çünkü union tipleri sayesinde değeri tam olarak belirleyebilirsiniz. Bu nedenle yalnızca benzersiz durumlarda uygundur:

function dump(mixed $var): mixed
{
	// print variable
	return $var;
}

false

mixed adresinden farklı olarak, yeni false sözde tipini yalnızca union tiplerinde kullanabilirsiniz. Bu, başarısızlık durumunda tarihsel olarak false döndüren yerel fonksiyonların dönüş tipini tanımlama ihtiyacından doğmuştur:

function strpos(string $haystack, string $needle): int|false
{
}

Bu nedenle, true türü yoktur. Ne tek başına false, ne false|null ne de bool|false kullanabilirsiniz.

static

Pseudotype static yalnızca bir yöntemin dönüş türü olarak kullanılabilir. Yöntemin, nesnenin kendisiyle aynı türde bir nesne döndürdüğünü söyler ( self ise yöntemin tanımlandığı sınıfı döndürdüğünü söyler). Akıcı arayüzleri tanımlamak için mükemmeldir:

class Item
{
	public function setValue($val): static
	{
		$this->value = $val;
		return $this;
	}
}

class ItemChild extends Item
{
	public function childMethod()
	{
	}
}

$child = new ItemChild;
$child->setValue(10)
	->childMethod();

resource

Bu tür PHP 8'de yoktur ve gelecekte de tanıtılmayacaktır. Kaynaklar, PHP'nin nesnelere sahip olmadığı zamanlardan kalma bir kalıntıdır. Yavaş yavaş, kaynakların yerini nesneler alacaktır. Sonunda, tamamen ortadan kalkacaklardır. Örneğin, PHP 8.0 resim kaynağını GdImage nesnesi ile ve curl kaynağını CurlHandle nesnesi ile değiştirir.

Stringable

Sihirli bir yöntemle her nesne tarafından otomatik olarak uygulanan bir arayüzdür __toString().

class Email
{
	public function __toString(): string
	{
		return $this->value;
	}
}

function print(Stringable|string $s)
{
}

print('abc');
print(new Email);

Sınıf tanımında class Email implements Stringable adresini açıkça belirtmek mümkündür, ancak bu gerekli değildir.

Nette\Utils\Html ayrıca eski IHtmlString yerine Nette\HtmlStringable arayüzünü uygulayarak bu adlandırma şemasını yansıtır. Örneğin bu türdeki nesneler Latte tarafından es geçilmez.

Tip varyans, kontravaryans, kovaryans

Liskov İkame İlkesi (LSP), uzantı sınıflarının ve arayüz uygulamalarının asla ebeveynden daha fazlasını gerektirmemesi ve daha azını sağlamaması gerektiğini belirtir. Yani, alt metot ebeveynden daha fazla argüman gerektirmemeli veya parametreler için daha dar bir tür aralığı kabul etmemelidir ve bunun tersi olarak, daha geniş bir tür aralığı döndürmemelidir. Ancak daha az sayıda döndürebilir. Neden mi? Çünkü aksi takdirde kalıtım bozulur. Bir fonksiyon belirli türde bir nesneyi kabul eder, ancak yöntemlerine hangi parametreleri aktarabileceği ve hangi türleri döndürecekleri hakkında hiçbir fikri olmaz. Herhangi bir çocuk bunu bozabilir.

Yani OOP'de alt sınıf şunları yapabilir:

  • parametrelerde daha geniş bir tür aralığını kabul eder (buna contravariance denir)
  • daha dar bir tür aralığı döndürür (kovaryans)
  • ve özellikler tür değiştiremez (değişmezdirler)

PHP bunu 7.4 sürümünden beri yapabilmektedir ve PHP 8.0'da yeni tanıtılan tüm türler de contravariance ve covariance'ı desteklemektedir.

mixed durumunda, çocuk dönüş değerini herhangi bir türe daraltabilir, ancak bir değeri temsil etmediği, daha ziyade yokluğunu temsil ettiği için void olamaz. Çocuk ayrıca bir tür bildirimini de atlayamaz, çünkü bu aynı zamanda bir değer yokluğuna da izin verir.

class A
{
    public function foo(mixed $foo): mixed
    {}
}

class B extends A
{
    public function foo($foo): string
    {}
}

Birlik türleri ayrıca parametrelerde genişletilebilir ve dönüş değerlerinde daraltılabilir:

class A
{
    public function foo(string|int $foo): string|int
    {}
}

class B extends A
{
    public function foo(string|int|float $foo): string
    {}
}

Ayrıca, false parametre olarak bool 'e ya da tam tersi olarak bool dönüş değeri olarak false 'e genişletilebilir.

Kovaryans/karşıt varyansa karşı tüm suçlar PHP 8.0'da ölümcül bir hataya yol açar.

Bu serinin sonraki bölümlerinde, özniteliklerin ne olduğunu, PHP'de hangi yeni işlevlerin ve sınıfların ortaya çıktığını göstereceğiz ve Just in Time Compiler'ı tanıtacağız.