PHP 8, 26 Kasım 2020’de yayınlanacak. Bu yeni ana sürümde yer alacak bazı önemli yenilikleri ve değişiklikleri makalemizde bulabilirsiniz.
PHP 8 beta sürümü yayınlandıktan sonra gözler nihai sürümün 26 Kasım 2020 tarihinde yayınlanmasına yöneldi. Yeni PHP sürümü, bazı önemli değişiklikler ile birlikte birçok yeni özellik ve performans iyileştirmeleri barındırıyor. Şu anda PHP 8 dondurulmuş durumda, yani bu, yeni özellik eklenmeyecek anlamına gelmekte.
Yenilenen özellikler sebebi ile kodunuzu PHP 8’de çalıştırmak için bazı güncellemeler yapmanız gerekecek. Eğer kodunuzu PHP son sürümlerine göre güncel tuttuysanız uygulamanızı 8’e yükseltmeniz çok zor olmayacak. Çünkü kodları kullanılamaz hale getiren çoğu değişiklik 7.* sürümleriyle kullanımdan kaldırıldı. Bu yazıda tüm bu kullanımdan kaldırılanlar da listelenmiştir.
Yeni Özellikler
Birleşik Tür Tanımlamaları – Union Types rfc
PHP’nin dinamik tür tanımı doğası göz önüne alındığında, birleşik tür tanımlarının yararlı olabileceği bir çok durum vardır. Birleşik tür tanımı, tanımlanan türlerden yalnızca birinin kullanılabileceğini gösteren ve iki ya da daha fazla türden oluşan bir koleksiyondur.
public function foo(Elma|Armut $meyve): int|float; //$meyve değişkeni Elma ya da Armut olabilir.
void
türü asla bir birleşik tür tanımlaması olamaz. void
zaten “hiçbir değer döndürme” anlamına gelir. Ayrıca, nullable
kullanımı için |null
veya halihazırda kullanılan ?
işaretini kullanabiliyoruz.
public function foo(Elma|null $meyve): void;
public function bar(?Armut $bar): void;
Tam zamanında derleme – JIT rfc
JIT (just in time) compiler her zaman web istekleri bağlamında olmasa da, bize önemli performans iyileştirmeleri vaat ediyor. Bazı canlı web projelerinde yapılan testlerde çok fazla bir fark yaratmadığı da söylenmekte. Fakat bu performans farkını karmaşık ağır hesap işlemlerinde hissedebiliriz. Bunun için bir benchmark testini aşağıya ekliyoruz. Benchmark testlerine baktığımızda, JIT’in farkını çok iyi bir şekilde görebilmekteyiz.
Eğer JIT hakkında PHP için yaptıkları ile ilgili daha fazla detay istiyorsanız JIT in PHP 8 websitesine bakmanız yeterli olacaktır.
Nullsafe Operatörü rfc
Eğer null birleştirme(coalescing) operatörü ile PHP 7.0’da tanıştıysanız veya kullandıysanız, eksikliklerine zaten aşinasınız: metod çağırımlarında çalışmıyor. Yeni sürümde artık bu mümkün.
Önceki kullanımı:
$baslangicTarihi = $rezervasyon->baslangicTarihiniGetir();
$yaziBicimindeTarih = $baslangicTarihi ? $baslangicTarihi->yaziBicimineDonustur() : null;
Şimdiki kullanımı:
Nullsafe operatörün eklenmesi ile metodlarda null birleştirme özelliğine benzer bir davranışa sahip olabiliriz.
$yaziBicimindeTarih = $rezervasyon->baslangicTarihiniGetir()?->yaziBicimineDonustur();
Tanımlı fonksiyon parametreleri rfc
Tanımlı fonksiyon parametreleri ile bir fonksiyona parametre gönderirken bunu parametre sırasını dikkate almak zorunda kalmadan ve isteğe bağlı parametreleri atlayarak gerçekleştirebileceğiz.
Örnek:
function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{ /* … */ }
foo(
b: 'b değeri',
a: 'a değeri',
d: 'd değeri',
);
Öznitelikler – Attributes rfc
Öznitelikler, diğer dillerde ek açıklamalar olarak da bilinir, docblock işlemek zorunda kalmadan sınıflara meta data eklemenin bir yolunu sunuyor.
Özniteliklerin nasıl göründüğüne dair bir örnek:
use App\Attributes\ExampleAttribute;
#[ÖrnekNitelik]
class Foo
{
#[ÖrnekNitelik]
public const FOO = 'foo';
#[ÖrnekNitelik]
public $x;
#[ÖrnekNitelik]
public function foo(#[ÖrnekNitelik] $bar) { }
}
#[Nitelik]
class ExampleAttribute
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
Match ifadesi rfc
Buna switch
ifadesinin abisi diyebiliriz: match
değer döndürebilir, break
ifadesi gerektirmez, koşulları birleştirebilir, katı tür kıyaslaması yapar ama türler arasında dönüştürme yapmaz. Yani '1'
ile 1
‘i eşit saymaz.
Buna benzer:
$sonuc= match($deger) {
0 => "merhaba",
'1', '2', '3' => "dünya",
};
Yapıcı metodda sınıf özelliği tanımlama rfc
Sınıf özelliklerini tanımladıktan sonra yapıcı metod içinde bu özelliklere değer atamak yerine, özellikleri doğrudan yapıcı methodun parametre bölümünde tanımlayabilirsiniz.
Bunun yerine:
class Para
{
public Doviz $doviz;
public int $miktar;
public function __construct(
Doviz $doviz,
int $miktar,
) {
$this->doviz = $doviz;
$this->miktar = $miktar;
}
}
Bunu kullanabilirsiniz:
class Para
{
public function __construct(
public Doviz $doviz,
public int $miktar,
) {}
}
Yeni return tipi: static
rfc
Daha önce return tipi olarak self
kullanmak mümkün olsa da, PHP 8’e kadar static
geçerli bir return tipi değildi. PHP’nin dinamik yapısını göz önünde bulundurduğumuzda birçok geliştirici için faydalı bir özellik olacak.
class Foo
{
public function test(): static
{
return new static();
}
}
Yeni tür tanımı: mixed
rfc
PHP’de tür tanımı yapılmadığı zaman elde edilen sonuç birçok anlama gelebilir. Bunu duruma göre isteyebilir veya istemeyebiliriz. Bu tanımların sınırlarını daha iyi belirlemek isteyenler yeni eklenen mixed
türünü kullanabilir.
mixed
türü aşağıdaki türlerden biri anlamına gelir:
array
bool
callable
int
float
null
object
resource
string
mixed
sadece bir return tipi olarak değil, fonksiyon parametresi veya sınıf özelliği olarak da kullanılabilecek.
Ayrıca mixed
, null
türünü içerdiği için kendisini nullable
yapmak hata ile sonuçlanacaktır.
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
function bar(): ?mixed {}
Throw ifadesi rfc
throw
, artık bir “ifadeye(expression)” dönüştü ve kapsamı daha da genişledi. İfadeye dönüşmesi geriye değer döndürebilmesi anlamına geliyor. Bu sayede daha önce kullanamadığımız birçok yerde artık throw
kullanabileceğiz.
$hataTetikle = fn () => throw new Hata();
$foo = $bar['indeks'] ?? throw new IndeksBulunamadi('indeks');
private metodlarda katılım kuralları rfc
PHP, kalıtım kurallarını public, protected ve private metodlarının hepsinde aynı şekilde uyguluyordu. Başka bir deyişle private metod, protected ve public metodları ile aynı kalıtım kurallarını takip etmeliydi. Bu mantıklı bir davranış değildi çünkü private metod, yapısı gereği alt sınıfların erişimi dışında kalıyor.
Artık bu değişti, yani kalıtım kuralları private metodlarda uygulanmayacak. Örneğin final private function
kullanılması mantıklı değildi ve artık bunu yaptığımızda bir uyarı alacağız:
Warning: Private methods cannot be final as they are never overridden by other classes
Uyarı: Private metodlar, diğer sınıflar tarafından override edilemediği için final olamaz.
WeakMap rfc
PHP 8’e PHP 7.4 ile eklenen weak referansının üzerine kurulan bir WeakMap nesnesi eklendi. WeakMap, referansını tuttuğu nesnelerin otomatik olarak bellekten silinmesinin önüne geçiyor.
ORM sistemlerini ele alalım. Bu sistemlerde entity nesnelerinin referanslarını tutarak bellekten silinmemelerini sağlayan ön belllek sistemleri vardır. Çünkü bu nesneler PHP’nin Garbage Collector sistemiyle otomatik olarak bellekten silinmemeli. O yüzden bu nesnelere sürekli bir yerlerden referans göstermek gerekiyor. O yer de genellikle ön bellek sisteminin kendisi oluyor.
Fakat bir sorunu çözeyim derken ikincisi ile karşılaşıyorsunuz. Bu kadar nesneyi ön belleğe aldınız, tamam ama iş aynı anda on binlerce nesneyle uğraşmaya geldiğinde ne yapacaksınız? İşte o zaman ön bellek sisteminiz weak referansları ve map’leri kullanırsa, ön bellekte tutulan nesneler kendilerine referans eden başka bir şey kalmadığında bellekten silinecektir. Böylece ikinci problemi de bu sistem sayesinde daha kolay ve verimli bir şekilde çözmüş olacaksınız.
WeakMap kullanımına bir örnek:
class Foo
{
private WeakMap $onbellek;
public function onbellektenBirSeyGetir(object $nesne): object
{
return $this->onbellek[$nesne]
??= $this->cokMaliyetliBirİslemYap($nesne);
}
}
Nesneler üstünde ::class izni rfc
Küçük ama kullanışlı olan bu yeni özellik, objeler üzerinde get_class() kullanmak yerine ::class kullanmayı mümkün kılıyor. get_class() ile aynı sonucu elde ediyorsunuz.
$foo = new Foo();
var_dump($foo::class);
try-catch değişkeni artık şart değil rfc
PHP 8’den önce hata yakalamak istediğimizde, catch bloğunda kullansak da kullanmasak da hatayı bir değişkene atamak zorundaydık. PHP 8 ile birlikte artık buna gerek yok.
Önceki kullanımı:
try {
// Bir şeyler ters gider
} catch (Hata $hata) {
Log::error("Bir şeyler ters gitti");
}
Şimdiki kullanımı:
try {
// Bir şeyler ters gider
} catch (Hata) {
Log::error("Bir şeyler ters gitti");
}
Yine de her zaman hata türü belirtmek gerektiğini ve catch bloğunun boş olamayacağını unutmayın. Eğer tüm istisnaları ve hataları yakalamak istiyorsanız da Throwable türünü kullanabilirsiniz.
Parametre listelerinde sonda virgül rfc
Bir fonksiyonu çağırırken sonuncu parametreye virgül koyamıyorduk. Artık PHP 8’de buna izin verilmekte. Bu özelliğe alışık olmadığımız için alışmak biraz zaman alacak.
public function(
string $parameterA,
int $parameterB,
Foo $objectfoo,
) {
// …
}
Ayrıca sondaki virgüller anonim fonksiyonların dışarıdan değişken kullanımına izin veren use($a, $b,)
listesinde de destekleniyor.
interface altından DateTime
nesneleri oluşturma
DateTimeUmmutable
nesnesinden DateTime::createFromImmutable($immutableDateTime)
kullanarak DateTime
nesnesi oluşturabiliyorduk ama bunun tem tersini yapmak çetrefilliydi.
PHP 8 ile beraber DateTime::createFromInterface()
ve DatetimeImmutable::createFromInterface()
eklendi. Artık DateTime
ve DateTimeImmutable
nesnelerini birbirine dönüştürmenin genelleştirilmiş bir yolu var.
DateTime::createFromInterface(DateTimeInterface $diger);
DateTimeImmutable::createFromInterface(DateTimeInterface $diger);
Yeni interface: Stringable
rfc
__toString()
kullanılan sınıfları anlayabilmek için Stringable
arayüzü kullanılabilir. Ne zaman bir sınıf __toString()
metodunu kullanırsa o sınıf otomatik olarak arka planda Stringable
arayüzünü implement edecektir. Yani elle implement etmeye gerek yok.
class Foo
{
public function __toString(): string
{
return 'foo';
}
}
function bar(string|Stringable $stringable) { /* … */ }
bar(new Foo());
bar('abc');
Yeni fonksiyon: str_contains()
rfc
Aslında gecikmiş bir özellik fakat artık bir yazının başka bir yazıyı içerip içermediğini bilmek için çetrefilli bir kullanıma sahip olan strpos
‘a ihtiyacımız yok. Aşağıdaki örnekler bunu çok güzel bir şekilde açıklıyor.
strpos():
if (strpos('bir sürü sözcüğün olduğu bir yazı', 'sözcük') !== false) { /* … */ }
str_contains():
if (str_contains('bir sürü sözcüğün olduğu bir yazı', 'sözcük')) { /* … */ }
Yeni fonksiyonlar: str_starts_with()
ve str_ends_with()
rfc
Yine çok gecikmiş iki fonksiyon.
str_starts_with('samanlık', 'saman'); // true
str_ends_with('samanlık', 'saman'); // true
Yeni fonksiyon: fdiv()
pr
Yeni fdiv()
fonksiyonu fmod()
ve intdiv()
fonksiyonlarına benzer işlevi yapıyor. Yani sıfıra bölmeye izin veriyor. Sıfıra bölüm gibi hatalar almak yerine duruma bağlı olarak INF
, -INF
veya NAN
sonuçlarını alacaksınız.
Önceden alınan sonuç:
$num = 1 / 0;
// Warning: Division by zero in ... on line ...
$num = intdiv(1, 0);
// Fatal error: Uncaught DivisionByZeroError: Division by zero in ...:...
$mod = 1 % 0;
// Fatal error: Uncaught DivisionByZeroError: Modulo by zero in ...:...
Şu an alınan sonuç:
fdiv(1, 0); // float(INF)
fdiv(-1, 0); // float(-INF)
Yeni fonksiyon: get_debug_type()
rfc
get_debug_type()
fonksiyonu bize değişkenin türünü döndürüyor. Bu fonksiyonu gettype()
gibi düşünebiliriz. Ve get_debug_type()
; array, string, anonim sınıf ve nesneler için daha kullanışlı bilgiler döndürebiliyor.
get_type()
kullanımı:
(is_object($bar) ? get_class($bar) : gettype($bar)
get_debug_type()
kullanımı:
get_debug_type($bar)
Yeni fonksiyon: get_resource_id()
pr
Resources (kaynaklar) PHP’de harici kaynaklara (external resources) atıfta bulunan özel değişkenlerdir. MySQL bağlantısı ve dosya işleyicisi (file handle) gibi.
Bu kaynakların her birine bir ID atanır ancak daha önce bu ID’yi bilmenin tek yolu kaynağı int
‘e çevirmekti ve bu güvenilir bir yol değildi.
$resourceId = (int) $resource;
PHP 8 ile gelen get_resource_id()
fonksiyonu, bu işlemi daha açık ve güvenli hale getiriyor.
$resourceId = get_resource_id($resource);
Trait iyileştirmelerinde soyut metodlar rfc
Trait’lerde Trait’i implement eden sınıfın kullanması gereken soyut metodlar tanımlanabiliyor ama bir şey var: PHP 8 öncesinde bu metodların imzası doğrulanmıyordu.
Geçerli olan yöntem buydu:
trait Test {
abstract public function test(int $input): int;
}
class UsesTrait
{
use Test;
public function test($input)
{
return $input;
}
}
Artık PHP 8, soyut metodlar içeren Trait’lerde gerekli imza doğrulamalarını gerçekleştirecek.
Artık bunu yazmalısınız:
class UsesTrait
{
use Test;
public function test(int $input): int
{
return $input;
}
}
token_get_all()
nesne uyarlaması rfc
token_get_all()
fonksiyonu bir değer dizisi döndürür. Bu RFC ile PHP’ye PhpToken::getAll()
metodu içeren bir PhpToken
sınıfı eklendi. Bu, düz değerler yerine nesnelerle çalışıyor. Sebepleri ise daha az bellek tüketimi ve okunur bir kod elde etmek.
Değişkenlerin söz diziminde yapılan değişiklikler rfc
Bu ayarlamalar, PHP’nin değişken söz dizimindeki tutarsızlıkları gideren RFC’ye ek olarak gözden kaçan bazı ufak noktaları çözüyor.
Dahili fonksiyonlar için tür notları externals
Bir sürü geliştirici dahili fonksiyonlara düzgün bir tür açıklama sistemi eklenmesi için istekte bulundu. Bu çok eski bir mesele ve bugüne kadar PHP’ye yapılan değişiklikler ile artık çözülmesi mümkün. Bu da dahili fonksiyon ve metodların, eksiksiz bir tür bilgisine sahip olacağı anlamına geliyor.
ext-json
her zaman kullanılabilir rfc
Önceden PHP’yi JSON uzantısı etkinleştirilmeden derlemek mümkündü. Fakat artık bu mümkün değil. JSON çok yaygın kullanıldığından, önce uzantının var olduğundan emin olmak yerine, geliştiricilerin her zaman orada olduğuna güvenmesi en iyisidir diye düşünmüşler.
Geriye dönük uyumluluğu bozan değişiklikler
PHP 8 ile birçok önemli değişiklik ve düzenlemeler yapıldı. Bu sürüme güncelleme yapmadan önce geriye dönük uyumsuzlukların listelendiği dokümanı incelemek en iyisi olacaktır.
Yapılan önemli değişikliklerin çoğu önceki 7.* sürümlerinde kaldırıldı. Bu bağlamda eğer sürüm güncellemelerini takip edip yapıyorsanız, metin başında da belirttiğimiz gibi PHP 8’e geçme işlemi o kadar zor olmayacak.
Tür tanımı hataları artık daha tutarlı rfc
PHP’deki kullanıcı tanımlı fonksiyonlar gerektiğinde TypeError
hatasına neden oluyordu. Ama dahili fonksiyonlar bunu yapamıyor ve hataları yutup null
döndürüyordu. PHP 8 ile birlikte dahili fonksiyonlar tutarlı hale getirildi.
Yeniden tanımlanmış temel uyarılar rfc
Daha önce uyarı veya bildirim tetikleyen hatalar, daha uygun hatalara dönüştürüldü. Değişen uyarıları listeliyoruz…
Dip not: Hatalarda anlam bozukluğu yaşanmaması için çevirileri yapılmamıştır.
- Undefined variable: Notice yerine
Error
exception - Undefined array index: Notice yerine warning
- Division by zero: Warning yerine
DivisionByZeroError
exception - Attempt to increment/decrement property ‘%s’ of non-object: Warning yerine
Error
exception - Attempt to modify property ‘%s’ of non-object: Warning yerine
Error
exception - Attempt to assign property ‘%s’ of non-object: Warning yerine
Error
exception - Creating default object from empty value: Warning yerine
Error
exception - Trying to get property ‘%s’ of non-object: Notice yerine warning
- Undefined property: %s::$%s: Notice yerine warning
- Cannot add element to the array as the next element is already occupied: Warning yerine
Error
exception - Cannot unset offset in a non-array variable: Warning yerine
Error
exception - Cannot use a scalar value as an array: Warning yerine
Error
exception - Only arrays and
Traversables
can be unpacked: Warning yerineTypeError
exception - Invalid argument supplied for foreach(): Warning yerine
TypeError
exception - Illegal offset type: Warning yerine
TypeError
exception - Illegal offset type in isset or empty: Warning yerine
TypeError
exception - Illegal offset type in unset: Warning yerine
TypeError
exception - Array to string conversion: Notice yerine warning
- Resource ID#%d used as offset, casting to integer (%d): Notice yerine warning
- String offset cast occurred: Notice yerine warning
- Uninitialized string offset: %d: Notice yerine warning
- Cannot assign an empty string to a string offset: Warning yerine
Error
exception - Supplied resource is not a valid stream resource: Warning yerine
TypeError
exception
@ operatörü artık önemli hataları susturmuyor
@ operatörü artık önemli hataları susturmuyor. Bu değişikliğin PHP 8’den önce gizlenen hataları ortaya çıkarması olası gözüküyor. Bu yüzden sunucularınızda display_errors=Off
ayarını yaptığınızdan emin olun!
Varsayılan hata raporlama seviyesi
E_NOTICE
ve E_DEPRECATED
dışında her şey için artık E_ALL
var. Bu, PHP 8’den önce muhtemelen zaten mevcut olsa da, daha önce sessizce göz ardı edilen birçok hatanın ortaya çıkabileceği anlamına gelir.
Varsayılan PDO hata modu rfc
PDO’da mevcut varsayılan hata modu sessizdir. Bir SQL hatası oluştuğunda eğer geliştirici kendi hata işleyicisini yazmamışsa veya bir exception döndürmüyorsak, bunu bilemiyorduk.
Bu RFC, PHP 8’de varsayılan hatayı değiştirerekPDO::ERRMODE_EXCEPTION
yapıyor.
String bağlama önceliği rfc
PHP 7.4’te kullanımdan kaldırılan bu değişiklik şimdi tekrar karşımızda.
Eğer şöyle bir satır yazarsak:
echo "toplam: " . $a + $b;
PHP yukarıdaki satırı böyle yorumluyordu:
echo ("toplam: " . $a) + $b;
Fakat PHP 8 ile birlikte artık bu şekilde yorumlanmakta:
echo "toplam: " . ($a + $b);
Aritmetik ve bitsel operatörler için daha katı tür kuralları rfc
PHP 8’den önce, dizilere, kaynaklara veya nesnelere aritmetik veya bitsel operatörler uygulamak mümkündü. Ama bu artık mümkün değil ve TypeError
ortaya çıkaracak:
[] % [42];
$object + 4;
Namespace isimleri tek bir token olarak algılanacak rfc
PHP, bir namespace
‘in \
ile ayrılmış her bir parçasını bir token olarak yorumluyordu ama artık tümünü bir token olarak yorumlayacak. Bu da PHP 7.4 ile gelen fn
gibi ayrılmış isimlerin de artık namespace
içinde kullanılabileceği anlamına geliyor.
Daha makul sayısal dizeler rfc
PHP’nin tür sistemi, dizelerde sayılarla karşılaştığında akıllıca şeyler yapmaya çalışır. Bu RFC, bu davranışı daha tutarlı ve net hale getiriyor.
Daha makul dize ve sayı karşılaştırmaları rfc
Bu RFC, PHP’de 0 == "foo"
kodunun true
ile sonuçlandığı garip durumu düzeltiyor. Bunun gibi başka uç durumlar da var ve bu RFC bunları da düzeltiyor.
Reflection sınıflarının metod imzalarında değişiklik
Reflection sınıflarına ait üç metodunun imzası değiştirildi. Eskiden böyleydi:
ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);
Artık böyle:
ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);
Güncelleme kılavuzu, bu sınıflardan başka sınıflar türetiyorsanız ve hem PHP 7 hem de PHP 8’i desteklemek istiyorsanız, aşağıdaki imzalara izin verileceğini belirtiyor:
ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);
Kararlı sıralama rfc
PHP 8’den önce sıralama algoritmaları kararsızdı. Özellikle eşit değerlerin sırası garanti edilemiyordu. PHP 8, tüm sıralama fonksiyonlarının bu konuda daha kararlı bir sıralama yapmasını sağlıyor.
Uyumsuz metod imzaları için fatal error rfc
Uyumsuz metod imzalarından kaynaklanan kalıtım hataları, şu anda hatanın nedenine ve devralma hiyerarşisine bağlı olarak hata veya uyarı veriyor. Bu RFC, PHP 8’de her zaman fatal error vermeyi öneriyor.
Aşağıdaki kod fatal error verirken:
interface I {
public function method(array $a);
}
class C implements I {
public function method(int $a) {}
}
// Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a)
Bu kod ise sadece uyarı(warning) oluşturuyor:
class C1 {
public function method(array $a) {}
}
class C2 extends C1 {
public function method(int $a) {}
}
// Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a)
Kaynaklar: