Tüm Detayları ile PHP 8 — Union Types

Baris Eser
3 min readApr 24, 2021

Uzun zamandır yazmayı planladığım PHP’nin 8 versiyonu ile ilgili yazı dizisi ile tekrardan merhabalar. Hangi yazılım geliştirme dilini kullanırsamız kullanın her yeni versiyon o dilde geliştirme yapanları heyecanlandırmıştır. PHP dilini kullananlar için bu heyecan neredeyse 7.versiyondan itibaren en yüksek seviyede olmaktadır. Her major versiyonda eksiliğini hissettiğimiz pek çok yeni özellik eklenmektedir.

Yeni versiyonun twitter duyurusu

Bu serinin ilk yazısı da PHP’nin 7 versiyonu ile beraber her versiyonda yeniliklerle karşılaştığımız veri türleri ve son version ile hayatımıza giren Union Types üzerine olacak.

PHP ile yazılım geliştirenlerin bildiği gibi bir property, parametre veya return type için sadece bir veri türü deklare edebiliyorduk.

<?php
class Foo {
private int $foo;

public function getFoo(int $foo): int {
return $foo;
}
}

PHP 7.1 ile bu syntax’a nullable özelliği eklendi. Buna göre bir dönüş tipi null veya deklare edilen türde olabilir. Bunu da ? işaretli ile yapabiliyoruz. Aşağıdaki örnekte fonksiyonumuzun int veya null değer dönebileceğimiz deklare ediyoruz.

<?php
class Foo {
public function getFoo(int $foo): ?int {
return $foo;
}
}

Bu örnekte ise fonksiyonun aldığı middleName parametresini nullable yapıyoruz.

<?php
class Member {

private string $firstname;
private ?string $middleName;
private string $lastname;

/**
* Member constructor.
*
@param string $firstname
*
@param string|null $middleName
*
@param string $lastname
*/
public function __construct(string $firstname, ?string $middleName, string $lastname)
{
$this->firstname = $firstname;
$this->middleName = $middleName;
$this->lastname = $lastname;
}

/**
*
@return string
*/
public function getFullName(): string {
return sprintf("%s %s %s", $this->firstname, $this->middleName, $this->lastname);
}
}
$member = new Member("Baris", null, "Eser");
echo $member->getFullName();
// Baris Eser

Gelelim PHP 8 ile beraber hayatımıza giren union type’lara. Bu özelliğe göre artık birden fazla return type, parametre veya sınıf özelliği tanımlayabiliyoruz. Hemen örneğimizi inceleyelim…

Book adında bir sınıfımız ve price adında bir fonksiyonumuz var. Price fonksiyonu aldığı iki parametre ile kitabın toplam fiyatını hesaplıyor. Fonksiyonda price parametresi union türünde int veya float değer alabiliyor. Yine fonksiyonun dönüş tipi de integer veya float tipinde olabilir.

Union Type ile ilgili kurallar

  • Union type ile void beraber kullanılamaz. Void sadece tek başına kullanılmalıdır.
public function price(int|float $price, int $quantity): int|void// Fatal error: Void can only be used as a standalone type in
  • Aynı tür bir arada kullanılamaz.
public function price(int|float $price, int $quantity): int|int// Fatal error: Duplicate type ... is redundant in ... on line ...
  • Birden fazla return type belirtilmiş ise nullable operatörü kullanılamaz. Bu durumda null type kullanılmalıdır. Aksi halde Parse Error fırlatılır.
public function price(int|float $price, int $quantity): string|?int
//Parse error: syntax error,
public function price(int|float $price, int $quantity): string|int|nullpublic function price(int|float $price, int $quantity): string|int|null

Type Varyansları

Union type varyansları LSP kurallarını kullanmaktadır. LSP ile ilgili detaylı bilgi almak için buradan devam edin.

Türetilmiş sınıflar türetildikleri ana sınıf (base class) ile yer değiştirilebilir olmalıdır. Bir başka deyişle türetilen sınıflardan oluşturulan nesneler türetildikleri ana sınıfların (base class) nesneleriyle yer değiştirdiklerinde aynı davranışı göstermek zorundadırlar. (http://www.kazimcesur.com/liskovs-substitution-principle-lsp/)

Buna göre:

1 — Türetilmiş bir sınıfta fonksiyona eklenen parametreler sadece genişletilebilir (contra-variant). Aşağıdaki örnekte foo metodunda string ve integer türünde değer alabilen foo değişkenine float türü de eklenmiştir.

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

2 — Türetilmiş bir sınıfta return type daraltılabilir (covariant). Aşağıdaki örnekte foo metodunun return türünden int değeri kaldırılmıştır.

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

3 — Class property’leri değiştirilemez. Örnek vermek gerekirse:

class A{
protected string $name;

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

public function foo(string|int|float $foo): string|int|float {
return 1.0;
}
}
//Fatal error: Type of B::$name must be string (as in class A)

Union Type ile ilgili bu kadar açıklama yeterlidir diye düşünüyorum. Biraz uzun oldu umarım sıkılmazsınız.

Keyifli Okumalar.

Bağlantılar

1- PHP Union Types RFC: https://wiki.php.net/rfc/union_types_v2

2- https://en.wikipedia.org/wiki/Liskov_substitution_principle

--

--