Co nowego w „PHP 8.0”?

Nowa wersja interpretera PHP 8.0 zostanie wydana 26 listopada 2020 roku. – sprawdź jakie nowości ta zawiera ta przełomowa wersja PHP.

Logo PHP

Wersja PHP 8.0 to nowe główne wydanie interpretera, co oznacza, że wprowadza ona sporo istotnych zmian, a także wiele nowych funkcji i ulepszeń wydajności. Obecnie PHP 8.0 jest w fazie zamrożenia, czyli nie można już dodawać żadnych nowych funkcji. Z powodu istotnych zmian istnieje spora szansa, że będziesz musiał wprowadzić pewne zmiany w swoim dotychczasowym kodzie, aby zadziałał w PHP 8. Nowa, stabilna wersja PHP 8.0 zostanie wydana 26 listopada 2020 roku.

Przeczytaj również: Jak włączyć PHP 7.4 na serwerze hostowanym w home.pl?

Jeśli jednak jesteś na bieżąco z najnowszymi wydaniami, aktualizacja nie powinna przysporzyć kłopotów, ponieważ większość istotnych zmian została uznana za przestarzałą już w wersjach 7. *. Jeśli jednak coś będziesz musiał zmienić, to istnieje duże prawdopodobieństwo, że znajdziesz odpowiedź w tym wpisie.

Oprócz przełomowych zmian, PHP 8 oferuje również ładny zestaw nowych funkcji, takich jak długo wyczekiwany kompilator JIT (just in time), typy unii, atrybuty i wiele innych.


Nowości w PHP 8.0

Typy unii RFC

Biorąc pod uwagę dynamiczną naturę języka PHP, istnieje wiele przypadków, w których typy unii mogą być przydatne. Typy unii to zbiór dwóch lub więcej typów, które wskazują, że można użyć jednego z nich.

Zwróć uwagę, że void nigdy nie może być częścią typu unii, ponieważ oznacza „brak wartości zwracanej”. Ponadto związki dopuszczające wartość nullable można zapisać przy użyciu |null lub przy użyciu istniejącej ? notacji:

public function foo(Foo|Bar $input): int|float;

JIT (Just In Time) RFC

Kompilator JIT ma zapewnić znaczną poprawę wydajności stron internetowych, choć nie zawsze w kontekście żądań internetowych. Wygląda jednak na to, że w testach porównawczych dla rzeczywistych aplikacji internetowych JIT nie robi tak dużej różnicy. Zobaczymy jak to będzie wyglądało w praktyce.

Operator nullsafe RFC

Jeśli znasz operator koalescencji null, znasz już jego wady: nie działa on na wywołaniach metod. Zamiast tego potrzebujesz sprawdzeń pośrednich lub polegasz na optional – wsparciu dostarczanym przez niektóre frameworki:

$startDate = $booking->getStartDate();

$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

Po dodaniu operatora nullsafe, możemy teraz uzyskać w metodach zachowanie podobne do koalescencji null!

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

Nazwane argumenty RFC

Nazwane argumenty umożliwiają przekazywanie wartości do funkcji, określając nazwę wartości, dzięki czemu nie musisz brać pod uwagę ich kolejności, a także możesz pominąć parametry opcjonalne!

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

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

Atrybuty RFC

Atrybuty, powszechnie znane jako adnotacje w innych językach, oferują sposób dodawania metadanych do klas bez konieczności analizowania bloków dokumentów.

Oto przykład wyglądu atrybutów ze specyfikacji RFC:

use App\Attributes\ExampleAttribute;

#[ExampleAttribute]
class Foo
{
    #[ExampleAttribute]
    public const FOO = 'foo';
 
    #[ExampleAttribute]
    public $x;
 
    #[ExampleAttribute]
    public function foo(#[ExampleAttribute] $bar) { }
}
#[Attribute]
class ExampleAttribute
{
    public $value;
 
    public function __construct($value)
    {
        $this->value = $value;
    }
}

Zwróć uwagę, że ten podstawowy Attribute nosił nazwę PhpAttribute w oryginalnym dokumencie RFC, ale został później zmieniony w innym dokumencie RFC.

Wyrażenie dopasowania RFC

Można to nazwaćstarszym bratem wyrażenia switch:
match może zwracać wartości, nie wymaga instrukcji break, może łączyć warunki, używa ścisłych porównań typów i nie wykonuje żadnego wymuszania typu.

To wygląda tak:

$result = match($input) {
    0 => "hello",
    '1', '2', '3' => "world",
};

Constructor property promotion RFC

Ten dokument RFC dodaje trochę ulepszeń składniowych do tworzenia obiektów wartości lub obiektów transferu danych. Zamiast określać właściwości klasy i konstruktora dla nich, PHP może teraz połączyć je w jedną.

Zamiast robić tak:

class Money 
{
    public Currency $currency;
 
    public int $amount;
 
    public function __construct(
        Currency $currency,
        int $amount,
    ) {
        $this->currency = $currency;
        $this->amount = $amount;
    }
}

Możesz teraz to zrobić tak:

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

Nowy zwracany typ static RFC

Chociaż zwracanie self było już możliwe, static nie był poprawnym typem zwracanym aż do PHP 8. Biorąc pod uwagę dynamiczny charakter PHP, jest to funkcja przydatna dla wielu programistów.

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

Nowy typu mixed RFC

Niektórzy mogą nazwać to złem koniecznym: typ mixed powoduje, że wielu ma mieszane uczucia. Jest na to bardzo dobry argument: brakujący typ może oznaczać wiele rzeczy w PHP:

  • funkcja nie zwraca nic lub null,
  • spodziewamy się jednego z kilku typów,
  • spodziewamy się typu, którego nie można wskazać w PHP.

Z powyższych powodów dobrze jest dodać typ mixed. Sam mixed oznacza jeden z tych typów:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Zwróć uwagę, że mixed może być również używany jako parametr lub typ właściwości, a nie tylko jako typ zwracany.

Należy również pamiętać, że ponieważ mixed zawiera już wartość null, nie można nadawać jej tej wartości. Poniższe spowoduje błąd:

// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
function bar(): ?mixed {}

Wyrażenie throw RFC

Ta zmiana RFC zmienia throw z instrukcji w wyrażenie, co umożliwia zgłoszenie wyjątku w wielu nowych miejscach:

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

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

Dziedziczenie metod prywatnych RFC

Wcześniej PHP stosowało te same kontrole dziedziczenia dla metod publicznych, chronionych i prywatnych. Innymi słowy: metody prywatne powinny przestrzegać tych samych reguł sygnatur metod, co metody chronione i publiczne. To nie ma sensu, ponieważ metody prywatne nie będą dostępne dla klas podrzędnych.

Ten dokument RFC zmienił to zachowanie, tak że te sprawdzenia dziedziczenia nie są już wykonywane na metodach prywatnych. Co więcej, użycie ostatecznej funkcji prywatnej również nie miało sensu, więc teraz spowoduje to ostrzeżenia:

Warning: Private methods cannot be final as they are never overridden by other classes

WeakMap RFC

Zbudowana na podstawie weakrefs RFC, która została dodana w PHP 7.4, implementacja WeakMap została dodana w PHP 8. WeakMap przechowuje odniesienia do obiektów, które nie chronią tych obiektów przed zbieraniem śmieci.

Weźmy na przykład ORM-y, które często implementują pamięci podręczne, które zawierają odwołania do klas jednostek, aby poprawić wydajność relacji między jednostkami. Te obiekty encji nie mogą być usuwane jako elementy bezużyteczne, o ile ta pamięć podręczna ma do nich odniesienie, nawet jeśli pamięć podręczna jest jedyną rzeczą, do której się odwołuje.

Jeśli ta warstwa pamięci podręcznej używa zamiast tego słabych odniesień i map, PHP będzie zbierać te obiekty bezużyteczne, gdy nic więcej już do nich się nie odwołuje. Zwłaszcza w przypadku ORM-ów, które mogą obsługiwać kilkaset, jeśli nie tysiące podmiotów w ramach zapytania; Weak Maps mogą oferować lepszy, bardziej przyjazny dla zasobów sposób radzenia sobie z tymi obiektami.

Oto jak wyglądają Weak Maps, przykład z RFC:

class Foo 
{
    private WeakMap $cache;
 
    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

Zezwalanie ::class na obiektach RFC

Mała, ale użyteczna nowa funkcja: można teraz używać ::class na obiektach, zamiast używać na nich get_class(). Działa tak samo, jak get_class().

$foo = new Foo();

var_dump($foo::class);

Niezabezpieczone przechwytywanie RFC

Ilekroć chciałeś złapać wyjątek przed PHP 8, musiałeś przechowywać go w zmiennej, niezależnie od tego, czy użyłeś tej zmiennej, czy nie. W przypadku przechwytywania można pominąć zmienną, więc zamiast tego:

try {
    // Something goes wrong
} catch (MySpecialException $exception) {
    Log::error("Something went wrong");
}

Możesz teraz zrobić to:

try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Pamiętaj, że wymagane jest zawsze określenie typu, nie możesz mieć pustego catch. Jeśli chcesz wychwycić wszystkie wyjątki i błędy, możesz użyć Throwable jako typu łapania.

Końcowy przecinek na listach parametrów RFC

Możliwe jest to już podczas wywoływania funkcji, na listach parametrów nadal brakowało obsługi końcowych przecinków. Jest to teraz dozwolone w PHP 8, co oznacza, że możesz wykonać następujące czynności:

public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

Na marginesie: końcowe przecinki są również obsługiwane na liście use, było to przeoczenie i teraz zostało dodane za pośrednictwem oddzielnego RFC.

Tworzenie obiektów DateTime z poziomu interfejsu

Możesz już utworzyć obiekt DateTime z obiektu DateTimeImmutable przy użyciu DateTime::createFromImmutable($immutableDateTime), ale odwrotna sytuacja była trudna. Dodając DateTime::createFromInterface() i DatetimeImmutable::createFromInterface() istnieje teraz uogólniony sposób konwersji obiektów DateTime i DateTimeImmutable na siebie.

DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

Nowy interfejs Stringable RFC

Interfejs Stringable może służyć do wpisywania podpowiedzi, które implementują __toString(). Za każdym razem, gdy klasa implementuje __toString (), automatycznie implementuje interfejs w tle i nie ma potrzeby ręcznej implementacji.

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

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

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

Nowa funkcja str_contains() RFC

Niektórzy mogą powiedzieć, że jest to już dawno spóźniona opcja, ale w końcu nie musimy już polegać na strpos(), aby wiedzieć, czy ciąg zawiera inny ciąg.

Zamiast to robić w ten sposób:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

Możesz teraz to zrobić tak:

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

Nowe funkcje str_starts_with() i str_ends_with() RFC

Dwie różne funkcje i od dawna spóźnione, są teraz dodane w corze.

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

Nowa funkcja fdiv() PR

Nowa funkcja fdiv() robi coś podobnego do funkcji fmod() i intdiv(), które pozwalają na dzielenie przez 0. Zamiast błędów, w zależności od przypadku, otrzymasz INF, -INF lub NAN.

Nowa funkcja get_debug_type() RFC

get_debug_type() zwraca typ zmiennej. Wygląda na to, że zrobi coś w stylu gettype()? Funkcja get_debug_type() zwraca bardziej przydatne dane wyjściowe dla tablic, łańcuchów znaków, anonimowych klas i obiektów.

Na przykład wywołanie metody gettype() w klasie \Foo\Bar zwróci object. Użycie get_debug_type() zwróci nazwę klasy.

Pełną listę różnic między get_debug_type() i gettype() można znaleźć w tym RFC.

Nowa funkcja get_resource_id() PR

Zasoby to specjalne zmienne w PHP, odnoszące się do zasobów zewnętrznych. Jednym z przykładów jest połączenie MySQL, innym uchwyt pliku.

Każdemu z tych zasobów przypisywany jest identyfikator, chociaż wcześniej jedynym sposobem na poznanie tego identyfikatora było rzutowanie zasobu na int:

$resourceId = (int) $resource;

PHP 8 dodaje funkcje get_resource_id(), dzięki czemu ta operacja jest bardziej oczywista i bezpieczniejsza dla typów:

$resourceId = get_resource_id($resource);

Abstrakcyjne metody doskonalenia cech RFC

Cechy mogą określać abstrakcyjne metody, które muszą być implementowane przez wykorzystujące je klasy. Jest jednak pewne zastrzeżenie: przed PHP 8 podpis tych implementacji metod nie był sprawdzany. Obowiązuje:

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

PHP 8 przeprowadzi poprawną walidację podpisu metody podczas używania cechy i implementacji jej abstrakcyjnych metod. Oznacza to, że zamiast tego musisz napisać to:

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

Implementacja obiektu token_get_all() RFC

Funkcja token_get_all() zwraca tablicę wartości. Ten dokument RFC dodaje klasę PhpToken z metodą PhpToken::getAll(). Ta implementacja działa z obiektami zamiast zwykłymi wartościami. Zużywa mniej pamięci i jest łatwiejszy do odczytania.

Poprawki składni zmiennej RFC

Z dokumentu RFC: Uniform Variable Syntax RFC rozwiązał szereg niespójności w składni zmiennej PHP. Ten dokument RFC ma na celu zajęcie się niewielką garstką przypadków, które zostały przeoczone.

Adnotacje typu dla funkcji wewnętrznych EXTERNALS

Wiele osób oczekiwało dodawania odpowiednich adnotacji typu do wszystkich funkcji wewnętrznych. Był to długotrwały problem i ostatecznie rozwiązany po wszystkich zmianach wprowadzonych w PHP w poprzednich wersjach. Oznacza to, że wewnętrzne funkcje i metody będą miały pełną informację o typie.

ext-json zawsze dostępny RFC

Wcześniej można było skompilować PHP bez włączonego rozszerzenia JSON, teraz już nie będzie to możliwe. Ponieważ JSON jest tak szeroko stosowany, najlepsi programiści zawsze mogą polegać na tym, że istnieje, zamiast najpierw upewnić się, że rozszerzenie istnieje.


Przełomowe zmiany

Wersja ósma PHP to poważna aktualizacja, dlatego zawiera także istotne zmiany. Najlepiej przejrzeć pełną listy ważnych zmian w dokumencie UPGRADING.

Wiele z tych przełomowych zmian zostało jednak wycofanych w poprzednich wersjach 7. *, więc jeśli przez lata byłeś na bieżąco, aktualizacja do PHP 8 nie powinna być taka trudna.

Spójne błędy typu RFC

Funkcje zdefiniowane przez użytkownika w PHP będą już zgłaszać TypeError, ale funkcje wewnętrzne nie, raczej dadzą ostrzeżenia i będą zwracały wartość null. Od PHP 8 zachowanie funkcji wewnętrznych zostało ujednolicone.

Przeklasyfikowane ostrzeżenia dotyczące silnika RFC

Wiele błędów, które wcześniej powodowały tylko ostrzeżenia lub powiadomienia, zostało przekształconych w prawidłowe błędy. Zmienione zostały poniższe ostrzeżenia:

  • Niezdefiniowana zmienna: wyjątek Error zamiast powiadomienia
  • Niezdefiniowany indeks tablicy: ostrzeżenie zamiast powiadomienia
  • Dzielenie przez zero: wyjątek DivisionByZeroError zamiast ostrzeżenia
  • Próba zwiększenia/zmniejszenia właściwości „%s” obiektu niebędącego obiektem: wyjątek Error zamiast powiadomienia
  • Próba zmodyfikowania właściwości „%s” obiektu niebędącego obiektem: wyjątek Error zamiast powiadomienia
  • Próba przypisania właściwości „%s” obiektu niebędącego obiektem: wyjątek Error zamiast powiadomienia
  • Tworzenie obiektu domyślnego z pustej wartości: wyjątek Error zamiast powiadomienia
  • Próba uzyskania właściwości „%s” obiektu niebędącego przedmiotem: ostrzeżenie zamiast powiadomienia
  • Niezdefiniowana właściwość %s::$%s: ostrzeżenie zamiast powiadomienia
  • Nie można dodać elementu do tablicy, ponieważ następny element jest już zajęty: wyjątek Error zamiast powiadomienia
  • Nie można usunąć przesunięcia w zmiennej innej niż tablica: wyjątek Error zamiast powiadomienia
  • Nie można użyć wartości skalarnej jako tablicy: wyjątek Error zamiast powiadomienia
  • Rozpakować można tylko tablice i Trawersable: wyjątek TypeError zamiast ostrzeżenia
  • Podano nieprawidłowy argument dla funkcji foreach(): wyjątek TypeError zamiast ostrzeżenia
  • Niedozwolony typ przesunięcia: wyjątek TypeError zamiast ostrzeżenia
  • Niedozwolony typ przesunięcia w isset lub empty: wyjątek TypeError zamiast ostrzeżenia
  • Nieprawidłowy typ przesunięcia w unset: wyjątek TypeError zamiast ostrzeżenia
  • Konwersja tablicy na ciąg: ostrzeżenie zamiast powiadomienia
  • Identyfikator zasobu ID#%d  używany jako przesunięcie, rzutowanie na liczbę całkowitą (%d): ostrzeżenie zamiast powiadomienia
  • Wystąpiło rzutowanie przesunięcia ciągu: ostrzeżenie zamiast powiadomienia
  • Przesunięcie niezainicjowanego ciągu %d: ostrzeżenie zamiast powiadomienia
  • Nie można przypisać pustego ciągu do przesunięcia ciągu: wyjątek Error zamiast ostrzeżenia
  • Dostarczony zasób nie jest prawidłowym zasobem strumienia: wyjątek TypeError zamiast ostrzeżenia

Operator @ nie wycisza już błędów krytycznych

Możliwe, że ta zmiana może ujawnić błędy, które ponownie były ukryte przed PHP 8. Upewnij się, że ustawiłeś display_errors=Off na serwerach produkcyjnych!

Domyślny poziom raportowania błędów

Teraz jest E_ALL zamiast wszystkiego, ale E_NOTICE i E_DEPRECATED. Oznacza to, że może pojawić się wiele błędów, które wcześniej były dyskretnie ignorowane, chociaż prawdopodobnie istniały już przed PHP 8.

Domyślny tryb błędu PDO RFC

Z dokumentu RFC: Bieżący domyślny tryb błędu dla PDO jest wyciszony. Oznacza to, że w przypadku wystąpienia błędu SQL nie mogą być emitowane żadne błędy ani ostrzeżenia ani wyjątki, chyba że programista zaimplementuje własną, jawną obsługę błędów.

Ten dokument RFC zmienia domyślny błąd zmieni się na PDO::ERRMODE_EXCEPTION w PHP 8.

Pierwszeństwo konkatenacji RFC

Chociaż jest już przestarzałe w PHP 7.4, ta zmiana została wprowadzona w życie. Jeśli napisałbyś coś takiego:

echo "sum: " . $a + $b;

PHP wcześniej zinterpretowałby to następująco:

echo ("sum: " . $a) + $b;

PHP 8 sprawi, że będzie to interpretowane następująco:

echo "sum: " . ($a + $b);

Bardziej rygorystyczne sprawdzanie typów dla operatorów arytmetycznych i bitowych RFC

Przed PHP 8 można było stosować operatory arytmetyczne lub bitowe na tablicach, zasobach lub obiektach. Nie jest to już możliwe i spowoduje zgłoszenie TypeError:

[] % [42];
$object + 4;

Nazwy w przestrzeni nazw będące pojedynczym tokenem RFC

PHP używane do interpretacji każdej części przestrzeni nazw (oddzielonej ukośnikiem odwrotnym – backslashem \ ) jako sekwencji tokenów. Ten dokument RFC zmienił to zachowanie, co oznacza, że zarezerwowane nazwy mogą być teraz używane w przestrzeniach nazw.

Lepsze ciągi liczbowe RFC

System typów PHP próbuje zrobić wiele inteligentnych rzeczy, gdy napotyka liczby w łańcuchach znaków. Ten dokument RFC sprawia, że to zachowanie jest bardziej spójne i jasne.

Bardziej rozsądny ciąg do porównań liczbowych RFC

To RFC naprawia bardzo dziwny przypadek w PHP, w którym 0 == "foo" daje w wyniku true. Istnieją inne skrajne przypadki, takie jak ten, a niniejszy dokument RFC je rozwiązuje.

Zmiana sygnatury metody Reflection

Zmieniono trzy sygnatury metod klas Reflection:

ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);

Stały się:

ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);

Przewodnik aktualizacji określa, że jeśli rozszerzysz te klasy i nadal chcesz obsługiwać zarówno PHP 7, jak i PHP 8, dozwolone są następujące podpisy:

ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);

Stabilne sortowanie RFC

Przed PHP 8 algorytmy sortowania były niestabilne. Oznacza to, że kolejność równych elementów nie była gwarantowana. PHP 8 zmienia zachowanie wszystkich funkcji sortowania na stabilne sortowanie.

Błąd krytyczny dla niekompatybilnych podpisów metod RFC

Z dokumentu RFC: Inheritance errors z powodu niezgodnych sygnatur metod obecnie generują błąd krytyczny lub ostrzeżenie, w zależności od przyczyny błędu i hierarchii dziedziczenia.


Zobacz także:

źródło: PHP.net | Stitcher.io (1)