PHP 8 new features

Union types

Union types are a collection of two or more types which indicate that either one of those can be used.

Note that void can never be part of a union type, since it indicates „no return value at all“.

Furthermore, nullable unions can be written using |null, or by using the existing ? notation.

public function foo(Foo|Bar $input): int|float;
public function foo(Foo|null $foo): void;
public function bar(?Bar $bar): void;

The nullsafe operator

Вече може да се взема първият not null резултат от повече функции.

$dateAsString = $booking->getStartDate()?->asDateTimeString();

Тук $dateAsString ще приеме стойността на втората функция, ако първата е null. Toест, все едно null coalescing operator но за функции.

Named arguments

Удобството е, че не само няма нужда да се спазва реда на аргументите, но и не е задължително всички да бъдат подавани (за такива трябва разбира се, да имаме по подразбиране null)

function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{
    /* ... */
}

foo( b: 'value b', a: 'value a', d: 'value d', );

Attributes

https://lindevs.com/using-attributes-in-php-8-0

https://dev.to/icolomina/using-php-attributes-easily-2fpo

https://www.amitmerchant.com/how-to-use-php-80-attributes/

Match expression

Подобрение на добрият, стар switch оператор. Реално е syntactic sugar, но има и някои предимства:
1) може да връща резултат, който да се присвои на променлива;
2) използва стриктна проверка на типовете, т.е. 200 не е ‘200’;
3) ако няма зададен default, и не влезе в никоя от възможностите, хвърля UnhandledMatchError exception;

Общо взето, основното му предимство е, че е по-стриктно откъм типове.

Constructor property promotion

Syntactic sugar. Цялата му философия се вижда от долният пример. Да не трябва да декларираш отделно клас пропъртитата, които сетваш в конструктора, а да става наведнъж – в конструктора.

In short: property promotion allows you to combine class fields, constructor definition and variable assignments all into one syntax, in the construct parameter list.

class Money
{
    public function __construct(
         public Currency $currency,
         public int $amount,
    ) {}
}

Важно е, че constructor property promotion НЕ е разрешено за абстрактни класове и интерфейси, но са разрешени за traits.

Също, НЕ са разрeшени т.н. „Variadic Parameters Are Not Allowed“ например:

class TestClass
{
    // Error: Variadic parameter
    public function __construct(public string ...$strings)
    {
        //...
    }
}

New static return type

New mixed type for both parameter and return type

Throw expression

Изключенията вече могат да се присвояват на променливи.

$triggerError = fn () => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

Inheritance with private methods

Досега PHP правеше проверка за signature както на public и protected методите, но така и на private, което e безсмислено и има RFC това да бъде премахнато.

Private методите няма смисъл да се декларират като final, защото те така или иначе не могат да се наследяват. Вече ще дава:
Warning: Private methods cannot be final as they are never overridden by other classes

Weak maps

SplObjectStorage е механизъм, с чиято помощ може да се създават колекции от обекти, като масиви, в които ключът е самият обект и евентуално може да има стойност, може и да няма. Системен клас, който си идва с набор от методи за управление на всичко това.

И като добавиш готов обект с метода attach(object $obj, mixed $value = null): void вече имаш рефърънс към този обект. И ако унищожиш този обект, например с unset($obj1), той ще е NULL но в SplObjectStorage ще съществува, което си е memory leak.

$obj1 = new stdClass();
$obj1->name = 'Foo'; $obj1->age = 28;

$map = new SplObjectStorage();

$map[$obj1] = 'Additional data for bla bla bla...';
var_dump($map->current());    // имаме си го

unset($obj1);       // Only clears reference

var_dump($obj1);    // NULL
echo count($map);   // 1
var_dump($map->current());   // имаме си го пак, нищо че оригиналът е ънсетнат

При WeakMap – не, изтрива се и от там, което е предимство откъм памет.
Демек:

$obj1 = new stdClass();
$obj1->name = 'Foo'; $obj1->age = 28;

$map = new WeakMap();

$map[$obj1] = 'Additional data for бла бла бла...';
var_dump($map->offsetGet($obj1));    // имаме си го

unset($obj1); // Removes $obj and the key and value from $map as well.

var_dump($obj1);    // NULL
echo count($map);   // 0
var_dump($map->offsetGet($obj1));   // нямаме си го, garbage collector го е забърсал

Иначе трябва изрично да изчистваш реферънса в SplObjectStorage с SplObjectStorage::detach(object $object): void

Allowing ::class on objects

За да вземеш класа на обект, вече не ти трябва get_class(). Може и с $foo::class

Non-capturing catches

Вече няма нужда в catch() да се задава променлива, ако не ти трябва.

try {
    // Something goes wrong
} catch (MySpecialException) {
    // Do something here
}

Trailing comma in parameter lists

Вече може да имаш запетая след последният параметър.

function patapan(string $parameterA, int $parameterB, Foo $objectfoo, )
{
    //...
}

Create DateTime objects from interface

New Stringable interface

Вграден интерфейс, който може да се използва като typehint за да зададе, че очакваната променлива например, трябва да може да се използва като string. Пък била тя string или обект с __toString() метод, който може да се използва като стринг.

function bar(string|Stringable $stringable)
{
    /* ... */
}

bar(new Foo());
bar('abc');

Вижда се, че на bar() можем да подадем както string скаларна променлива, така и обект, съдържащ __toString() магичн метод.

От примера се вижда, че е напълно нормално двата typehints да се използват заедно като uniontype.

New str_contains() function

Whether a string contains another string…

if (str_contains('string with lots of words', 'words')) { 
    /* ... */
}

New str_starts_with() and str_ends_with() functions

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

New fdiv() function

var_dump(fdiv(5.7, 1.3));    // float(4.384615384615385)
var_dump(fdiv(4, 2));        // float(2)
var_dump(fdiv(1.0, 0.0));    // float(INF)
var_dump(fdiv(-1.0, 0.0));   // float(-INF)
var_dump(fdiv(0.0, 0.0));    // float(NAN)

New get_debug_type() function

По-удобна версия на gettype(), която например дава името на класа и т.н…

TypeExample valuegettype()get_debug_type()
Class objectnew stdClass()objectstdClass
Class objectnew DateTime()objectDateTime
Class objectnew Foo\Bar()objectFoo\Bar
Closurefunction() {}objectClosure
Anonymous classnew class {}objectclass@anonymous
Anonymous subclassnew class extends Foo{}objectFoo@anonymous

New get_resource_id() function

Всяка ресурс променлива (разни хендлъри към бази данни…) имат ID, което до сега се вземаше като се тайпкастне ресурса към int.
Сега има тази специална функция.

Abstract methods in traits improvements

Трейтовете могат да декларират абстрактни методи, като е важно да се спомене, че спазванео на сигнъчъра е задължителен.

Object implementation of token_get_all()

Variable syntax tweaks

Type annotations for internal functions

Литература:

https://stitcher.io/blog/new-in-php-8#new-features

https://php.watch/articles/practical-weakmap

https://www.php.net/manual/en/class.splobjectstorage.php

https://www.php.net/manual/en/class.weakmap.php