PHP 8.1 — yeniliklere hızlı bakış

Bugünün tarihi ile yaklaşık 3 hafta önce (25 Kasım 2021) PHP 8.1 yayınlandı. Bu sürümde hayatımıza neler girdi, ne gibi değişiklikler yapıldı birlikte inceleyelim mi?

image source: php.net

Son yıllarda PHP camiasında oldukça hummalı bir çalışma vardı ve bu çalışmalar çok güzel meyveler verdi. PHP 8.1 ile de hayatımıza yeni bazı özellikler girdi ve bunlarla birlikte daha yalın, daha temiz ve daha hızlı geliştirme süreçleri yaşayacağız gibi duruyor.

Bu yazımda PHP 8.1 ile gelen yeniliklere göz atacağız, naçizane örnekler ile kullanım senaryolarını göreceğiz ve yeni oluşturulan PHP Foundation hakkında bir-iki kelam edeceğiz. Hazırsanız başlayalım…

Kurulum

PHP 8.1 kurulumu için, PHP.net sitesinde bulunan yönergeleri takip edebilir ve sisteminize göre uygun dosyayı indirebilirsiniz. Bunun dışında direkt olarak Terminal ekranından da paket yöneticiler ile kurulum yapmamız mümkün. Ben MacOS kullandığım için, buna göre diğer adımlara devam edeceğim.

MacOS üzerinde PHP 8.1'i yüklemek için homebrew kullanacağız. Öncelikle brew’in güncel olduğundan emin olalım:

$ brew update

Daha sonra PHP 8.1 için kullanacağımız güncel brew deposunu (repository) ekleyelim:

$ brew tap shivammathur/php

Artık PHP 8.1 için kuruluma başlayabiliriz.

$ brew install php@8.1
$ brew link --overwrite --force php@8.1

Vee tamamladık. Buraya kadar bir sorun yaşamadıysanız, PHP 8.1 bilgisayarınıza kuruldu. Artık PHP versiyonuna baktığınızda, aşağıdakine benzer bir çıktı alıyor olmasınız.

$ php -vPHP 8.1.0 (cli) (built: Nov 24 2021 20:21:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.0, Copyright (c) Zend Technologies
with Zend OPcache v8.1.0, Copyright (c), by Zend Technologies

Buna ek olarak; (bakın burası çokomelli) bilgisayarınızda, Docker üzerinde bir PHP geliştirme ortamına ihtiyacınız varsa, Demet ismiyle hazırlamış olduğum paketi inceleyebilirsiniz. Olabildiğince sade ve PHP ile uygulama geliştirirken ihtiyaç olabilecek çoğu aracı da içerisinde barındırıyor. Hem de PHP 8.1 ile birlikte geliyor… Docker üzerinde çalıştığı için hem Windows, hem MacOS hem de Linux ortamlarında kullanabilirsiniz.

Şimdi gelelim PHP 8.1 ile birlikte gelen ve öne çıkan bazı yeniliklere…

Enums

Bu sürüm için en dikkat çekici yeniliklerden. Yenilik diyorum, ama aslında diğer birçok dilde bu özellik zaten vardı. PHP’de bir sınıf ile alakalı bazı sabit değerlere ihtiyacımız olduğunda, genellikle sınıf sabitlerini (class constants) kullanıyorduk. Şimdi Enum’lar ile birlikte gelen özellikler sayesinde, çok daha temiz bir yapı oluşturabileceğiz. Örnekle inceleyelim.

Öncelikle eskiden nasıl bir yapı oluşturmamız gerektiğine bakalım: Diyelim ki Task isimli bir sınıfımız var ve bunun içerisinde TaskStatus’leri tanımlamak ve kullanmak istiyoruz. Bunun için hemen ayrı bir sınıf oluşturup, sabitlerimizi tanımlıyorduk:

<?phpclass TaskStatus
{
public const CREATED = 1;
public const IN_PROGRESS = 2;
public const COMPLETED = 3;
public const FAILED = 4;
}

Daha sonra tanımladığımız bu sınıfı, ilgili olan asıl sınıfımızda kullanmak için hazırlıkları yapıyoruz:

<?phpclass Task
{
// ...
// class properties and methods
private int $status; public function updateStatus(int $status)
{
$this->status = $status;
}
public function status(): int
{
return $this->status;
}
}

Şimdi de bu yapıyı kullanalım:

<?php$task = new Task;$task->updateStatus(TaskStatus::CREATED);echo $task->status(); // 1

Buradaki en büyük sorunlardan biri, updateStatus metodunun int tipinde bir değer kabul ediyor olması ve bunun doğrulamasının dil seviyesinde yapılamıyor oluşu. Aslında yukarda TaskStatus sınıfı ile gerekli değerleri oluşturduk ama, direkt olarak metoda 10, 20 vs gibi integer bir değer de gönderilse, yazdığımız kod yine çalışacak. Bunun için metot içerisinde ekstra kontroller yapmak ve istenmeyen değerleri engellemek gerekiyor. Aksi halde, istemediğimiz bir değeri almış alabilir ve uygulama akışını bozabiliriz.

Tam olarak bu tarz durumlar için Enum devreye giriyor ve bize daha sağlıklı bir yapı sunuyor. Yukarıdaki örneği bir de Enum ile yazmaya çalışalım:

<?phpenum TaskStatus
{
case CREATED;
case IN_PROGRESS;
case COMPLETED;
case FAILED;
}

Gördüğünüz gibi case keywordü ile birlikte Enum içerisindeki değerlerimizi oluşturduk. Bu yeni yapımız ile birlikte TaskStatus::CREATED değeri bizim için Enum tipinde bir nesne geriye dönecek. Diğer örneğimizde integer olarak tanımlamış ve o şekilde almıştık. Aslında şu an daha doğru ve istediğimiz tip ve ölçülerde kodumuzu yazmaya başladık bile. Şimdi ilgili sınıfımızı da Enum kullanımına göre düzenleyelim.

<?phpclass Task
{
// ...
// class properties and methods
private TaskStatus $status; public function updateStatus(TaskStatus $status)
{
$this->status = $status;
}
public function status(): TaskStatus
{
return $this->status;
}
}

Bizden TaskStatus tipinde bir değer isteyecek ve bize TaskStatus tipinde değer dönecek şekilde sınıfımızı güncelledik.

Şimdi de bu yeni yapımızı kullanmaya çalışalım:

<?php$task = new Task;$task->updateStatus(TaskStatus::CREATED);echo $task->status(); // Error

Bunu yaptığımız an, hata alacağız. Çünkü artık status metodumuzdan bir Enum nesnesi geriye dönüyor ve biz bunu direkt olarak ekrana yazamayız. Geriye dönen Enum nesnesinin ismine veya değerine şu şekilde erişmeniz gerekiyor:

echo $task->status()->name; // name of the Enum// ORecho $task->status()->value; // value of the Enum

Enum değerlerini oluştururken, eklediğimiz her case için bir value tanımlayabilir ve bunu yukarıda belirttiğim gibi ->value şekliyle alabiliriz.

enum TaskStatus: int
{
case CREATED = 10;
case IN_PROGRESS = 20;
case COMPLETED = 30;
case FAILED = 40;
}
echo TaskStatus::FAILED->value; // 40
echo TaskStatus::FAILED->name; // FAILED

Yukarıdaki örneğe dikkat ettiyseniz, Enum için bir tip belirlemesi de yaptık. Enumlar ile çalışırken, Enum’ın ne tipte bir değer döndüreceğini belirtebilirsiniz.

Enumların bir diğer güzel tarafı ise, ihtiyacımız olan herhangi bir metodu Enum sınıfı içerisine yazabiliyoruz. Örneğin, Enum değerimize göre başka bir ifade geriye dönen bir metot ekleyelim:

enum TaskStatus: int
{
case CREATED = 10;
case IN_PROGRESS = 20;
case COMPLETED = 30;
case FAILED = 40;
public function message(): string
{
return match ($this) {
case self::CREATED => 'Task Created',
case self::IN_PROGRESS => 'Task In Progress',
case self::COMPLETED => 'Task Completed',
case self::FAILED => 'Task Failed',
};
}
}

Burada tanımladığımız Enum içerisine message adında bir metot ekledik ve bunu dilediğimiz yerde kullanabiliriz:

<?php$task = new Task;$task->updateStatus(TaskStatus::CREATED);echo $task->status()->message(); // Task Created

Enum hakkında daha detaylı bilgi için ilgili RFC dokümanını inceleyebilirsiniz.

Read-Only Properties

Başka bir güzel özelliğe geldik. Artık sınıflarımız (class) içerisinde bulunan propertylere readonly keywordünü ekleyerek sadece okunabilir, değer yazılamaz/değiştirilemez bir şekilde ayarlayabiliyoruz. Bunun için daha önce private olarak property ekleyip, daha sonra o propertye erişebilmek için bir getter yazıyorduk. Şimdi ise şu şekilde kullanabileceğiz:

<?phpclass Task
{
// public readonly int $age;
public function __construct(
public readonly string $name,
public readonly string $email,
public readonly string $city
) {
}
}

Gördüğünüz gibi Contructor Property Promotion yöntemini kullanarak bir tanımlama yaptık ve bu tanımlama içerisine readonly keywordü ekleyerek, değerlere sadece erişim yapılabileceğini, herhangi bir şekilde değişim yapılamayacağını belirttik. Hemen kullanmayı deneyelim:

<?php$task = new Task('John Doe', 'john@doe.com', 'Ankara');echo $task->name; // John Doe
echo $task->email; // john@doe.com
$task->email = 'johndoe@doe.com'; // Error. Cannot change the value

Bu konu ile ilgili RFC dokümanına buradan ulaşıp, daha detaylı bilgi alabilirsiniz.

Intersection Types

Muhtemelen bildiğiniz gibi, PHP 8.0 ile hayatımıza Union Types kavramı girmişti. Oldukça faydalı bulup, severek kullandık ve kullanmaya devam ediyoruz. PHP 8.1 ile birlikte bu durum bir tık daha ileri götürülmüş ve Intersection Types özelliğini de bizlere sunmuşlar.

Union Types kavramı ile birden fazla veri tipini kullanabilir, kabul edebilir duruma gelmiştik. Bir örnek ile inceleyelim:

<?phpfunction square(int|float $number): int|float
{
return $number ** 2;
}

Burada verdiğimiz sayının karesini alan bir fonksiyon yazdık ve bu fonksiyon parametre olarak int veya float alabilir, ve geriye int veya float bir değer döndürebilir. Bunu Union Types özelliği ile yapabildik.

Şimdi de Intersection Types bize nasıl bir fayda sağlıyor, buna bakalım:

<?phpfunction example(Foo&Bar $param): void
{
// function block
}

Yukarıdaki örneğimizde $param parametresinin hem Foo hem de Bar sınıfından türüyor veya implement ediliyor olması gerekiyor:

<?phpclass ExampleClass implements Foo, Bar
{
// class block
}

Bu konu ile alakalı detaylı bilgi için RFC dokümanına göz atabilirsiniz.

Never Return Type

PHP 7.0 ile birlikte hayatımıza Return Types girmişti. Sonraki sürümlerde de hem parametreler için hem de dönüş tipleri için yeni veri tipleri eklendi. PHP 8.1 ile birlikte bunlara bir de never eklenmiş oldu. Peki ne işe yarıyor bu never ? Bir örnek ile inceleyelim:

<?phpfunction example(): never
{
// function block

die;
// or exit;
}

Bazı durumlarda yazdığımı fonksiyonların içerisinde die veya exit gibi keywordler ile, yazdığımız uygulamanın çalışmasını sonlandırıyoruz. Bu durumlarda fonksiyonlar hiçbir şey döndürmüyor. Bu fonksiyonlar için never keywordü ile return type belirtebilirsiniz.

array_is_list function

PHP 8.1 ile birlikte aramıza katılan yeni bir function. Basitçe, oluşturduğumuz bir array’in indisleri 0'dan başlayıp, count-1'e kadar sıralı bir şekilde gidiyorsa, bu bir list tir. Ama manuel olarak bir indis belirliyorsak, buraya list yapısı bozuluyor ve bu fonksiyon bize false dönüyor. Örnek ile incelersek, çok daha anlamlı olabilir:

<?php$list    = ['a', 'b', 'c'];
$notList = [1 => 'a', 'b', 'c'];
$list_2 = [0 => 'a', 'b', 'c'];
var_dump(array_is_list($list)); // true
var_dump(array_is_list($notList)); // false
var_dump(array_is_list($list_2)); // true

Detaylar için RFC’yi buradan buyurabilirsiniz.

First class callable syntax

PHP 8.1'den önce herhangi bir fonksiyonumuzu bir değişkene atayıp, bu değişken üzerinden kullanmak için şöyle bir yapı kullanıyorduk:

<?phpfunction sum(float ...$numbers): float
{
return array_sum($numbers);
}
$closure = Closure::fromCallable('sum');echo $closure(2, 3, 5); // 10

Bu syntaxı biraz daha basit bir şekilde kullanabiliyoruz artık:

<?phpfunction sum(float ...$numbers): float
{
return array_sum($numbers);
}
$closure = sum(...);echo $closure(2, 3, 5); // 10

Detaylar için ilgili RFC dokümanını okuyabilirsiniz.

‘new’ keyword in initializer

Başka bir güzel yeniliğe daha geldik. Bazen yazmış olduğumuz sınıfların kurucu (constructor) metotlarında, başka bir sınıfa opsiyonel olarak ihtiyaç duyabiliyoruz ve bunun için opsiyonel bir parametre ekliyoruz. Eğer uygulamanın herhangi bir yerinde bu parametre bize gönderilmişse, onu kullanıyoruz; aksi halde kendimiz varsayılan bir sınıf kullanabiliyoruz. Şöyle bir örnek yazalım:

<?phpclass ExampleClass
{
public function __construct(public ?Foo $foo = null)
{
$this->foo ??= new Foo();
}
}

PHP 8.1 ile birlikte, artık bu tarz kontrolleri metot içerisinde yapıp, tanımlama yapmamıza gerek yok. Şu şekilde hızlıca işimizi halledebiliyoruz:

<?phpclass ExampleClass
{
public function __construct(public ?Foo $foo = new Foo())
{}
}

Artık ilgili sınıfımızı kullanırken bir parametre gönderirsek o kullanılacak, göndermezsek varsayılan olarak new Foo() çalışacak ve Foo tipince bir instance oluşturulacak. Bu işlem bizim parametre gönderip/göndermeme durumumuza göre otomatik olarak çözümlendiği için, gereksiz bir instance oluşmuyor ve herhangi bir şekilde bellek tüketimini etkilemiyor:

<?php$example = new Example(new Foo()); // It's okay$example = new Example(); // It's okay

Detaylı bilgi için RFC dokümanını inceleyebilirsiniz.

‘final’ Class Constants

PHP 8.1'den önce, classlar içerisinde tanımladığımız sabitleri (constants), subclasslar içerisinde overwrite edebiliyor, yani değerini değiştirebiliyorduk. Bir örnek ile bakalım:

<?phpclass Foo
{
public const NUMBER = 10;
}
class Bar extends Foo
{
public const NUMBER = 20;
}
echo Bar::NUMBER; // 20

Artık bu durumu final keywordü ile engelleyebiliriz:

<?phpclass Foo
{
final public const NUMBER = 10;
}
class Bar extends Foo
{
public const NUMBER = 20; // This will not be valid
}

Detaylar için bu konu ile alakalı RFC dokümanını okuyabilirsiniz.

Array unpacking support for string-keyed array

Bir süredir dizilerle birleştirme işlemi yaparken, ... kullanımı ile hızlıca bu işlemi yapabiliyorduk. Öncelikle bu kullanımı bir hatırlayalım.

<?php$managers  = ['John', 'Jane'];
$employees = ['Michael', 'Sara', 'Alex', 'Lincoln'];
// old
// $team = array_merge($managers, $employees);
// with unpacking
$team = [...$managers, ...$employees];
// $managers = ['Burak', ...$managers];

Burada dikkat edilmesi gereken nokta, PHP 8.1'den önce, bir dizinin key => value şeklinde tanımlandığı ve string veri tipinde bir key kullandığımızda, bu işlemin ... ile (unpacking) yapılamaması.

PHP 8.1 ile bu sorun için de bir geliştirme yapılmış:

<?php$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = ['a' => 0, ...$arrayA, ...$arrayB];// ['a' => 1, 'b' => 2]

Gördüğünüz gibi, hem string keyleri unpack edebiliyoruz, hem de unpack edilen değerler var olan dizi içerisinde varsa, bu değerleri overwrite edebiliyoruz.

Bu konu ile detaylı bilgi için alakalı RFC dokümanını inceleyebilirsiniz.

Fiber — Async PHP, Concurrency

Fiber, önceki aylarda duyurulmuş ve PHP 8.0 ile dile eklenmişti. Kısaca PHP’de concurrency modu kullanabildiğimiz ve eş zamanlı işlemler yapabilmemize imkan sağlayan bir özellik. Anladığım kadarıyla henüz oluşumunu tam tamamlamış değil ama yine de PHP 8.1 ile de üzerinde geliştirmeler yapılmış ve bazı çalışmalar için uygun halde. Yine de henüz tam olarak kabul görmüş bir seviyede değil ve (benim görüşüm) bu biraz daha zaman alacak gibi duruyor.

Fiber hakkında detaylı bilgi için PHP resmi sitesinde bulunan şu sayfayı inceleyebilirsiniz. Ayrıca şurada bulunan kaynak da, Fiber’i incelemeniz için faydalı olabilir.

PHP’de concurrent işlemler için Amphp, ReactPHP, Swoole gibi paketler kullanılıyor ve şu an ciddi olarak kabul görmüş en iyi çözümler bunlar. Bu paketleri inceleyip, uygulamalarınıza daha da güç ve performans katabilirsiniz.

İncelemek isterseniz; ReactPHP’nin yapımcısı, Framework X adında bir Async PHP Frameworkü oluşturdu ve bunu open source olarak yayınladı. Detaylar için resmi sitesini inceleyebilirsiniz.

Ve dahası…

Bu konular haricinde yapılmış geliştirmeler, artık kullanılması önerilmeyen (deprecated) yöntemler ve projenizi PHP 8.1 için nasıl migrate edebilirsiniz gibi konular hakkında detaylı bilgiler için, PHP’nin resmi sayfasında yayınlamış olduğu doküman üzerinden inceleme yapabilirsiniz. Ayrıca PHP 8.1'e genel bir bakış daha atmak için, resmi sitesindeki şu sayfayı inceleyebilirsiniz.

PHP Foundation Kuruldu

Bu süreçteki önemli hususlardan birisi de PHP Foundation isimli, kar amacı gütmeyen bir vakfın kurulması ve önemli çekirdek geliştiricilerden Nikita Popov’un PHP core ekibinden ayrılması oldu. Oluşturulan vakıf için, Jetbrains, Laravel, Symfony, Zend vs gibi PHP’nin en önemli toplulukları desteklerde bulundu ve bulunmaya devam ediyor. Dilerseniz siz de destekte bulunabilir, PHP’nin gelişimi için katkı sağlayabilirsiniz.

Artık PHP bu vakıf tarafından desteklenecek, finanse edilecek ve geliştirmeler bu vakıf tarafından yapılacak. Konu ile alakalı Jetbrains üzerinde bir blog yazısı da yayınlandı. Dilerseniz buradan da oluşturulan vakıf hakkında bilgi alabilirsiniz.

Son Söz

Genel olarak; ben yapılan geliştirmeleri oldukça faydalı buldum. (Özellikle Enum ve readonly props). Günümüzde hala “PHP öldü. Ölmedi mi?” gibi geyikler devam etse de, PHP’nin bir yere gittiği yok; eğer bir yere gidiyorsa da, şu an gittiği yol oldukça güzel. Önümüzdeki sürümlerde çok daha iyi geliştirmeler göreceğiz ve PHP’yi daha güçlü bir şekilde kullanabileceğiz gibi hissediyorum. Özellikle PHP 7.0'dan önce, PHP’nin mazisi ve genel kullanımı her ne kadar iç açıcı olmasa da (kimi kesimlere göre), bugün ibrenin tamamen tersine döndüğünü ve çok daha doğru, modern ve kullanışlı bir dil haline geldiğini rahatlıkla söyleyebiliriz. PHP Foundation’ın kurulması ile PHP’nin akıbeti nasıl olacak şimdilik merak konusu ama, oluşturulan ekipler ve destek veren toplulukları görünce; çok iyi bir noktaya gidebileceği muhtemel. Bunu da zaman içerisinde izleyip, göreceğiz…

Belki değinmediğim veya eksik/yanlış bilgi vermiş olduğum yerler olmuştur ama; kabaca PHP 8.1 hakkında değinebileceğim ve sizlere anlatacağım başlıklar bunlardı. Umarım faydalı bulursunuz ve bir nebze bile olsa projelerinizin geleceği için ışık tutabilmişimdir.

Sizin de eklemek istediğiniz şeyler varsa veya herhangi bir hatamı/yanlışımı görürseniz; lütfen bana bildirmekten çekinmeyin.

Bu yazı içerisinde genel olarak php.net ve php.watch sitelerinden yararlandım ve RFC linkleri başta olmak üzere tüm linkleri bu siteler üzerinden paylaşmaya çalıştım.

Başka bir konuda tekrar görüşmek üzere,
Sağlıkla ve kodla kalın!

nam-ı diğer Buki. 👨🏻‍💻 . Software Engineer — #php #java #golang #javascript #react-native— Co-Founder at HaberSistemim & MemleketApp

nam-ı diğer Buki. 👨🏻‍💻 . Software Engineer — #php #java #golang #javascript #react-native— Co-Founder at HaberSistemim & MemleketApp