PHP 8.0 ma wyjść w listopadzie 2020 i będzie największą zmianą w języku od czasu PHP 7.0. JIT compiler, union types, named arguments, match expression, constructor property promotion – to nie są drobne poprawki. Przeglądam najważniejsze nowości na przykładach i oceniam co realne znaczenie dla codziennego kodu z Magento 2.
Union Types – wiele typów w jednej deklaracji
Do tej pory jedyną opcją połączenia typów był nullable type (?string). PHP 8.0 pozwala deklarować pełne uniony:
<?php
declare(strict_types=1);
// PHP 7.x - brak możliwości wyrażenia "int lub float"
// trzeba było używać docblocka i liczyć na IDE
// PHP 8.0 - union type w sygnaturze
function calculatePrice(int|float $basePrice, int|float $taxRate): float
{
return (float) ($basePrice * (1 + $taxRate));
}
calculatePrice(100, 0.23); // ok
calculatePrice(99.99, 0.23); // ok
calculatePrice('100', 0.23); // TypeError w strict_types
// Przydatne przy metodach akceptujących int lub string ID
function findProduct(int|string $identifier): ?ProductInterface
{
if (is_int($identifier)) {
return $this->productRepository->getById($identifier);
}
return $this->productRepository->get($identifier); // pobierz po SKU
}
Named Arguments – koniec z tajemniczymi true/false
Named arguments pozwalają przekazywać argumenty po nazwie zamiast po pozycji. Eliminuje jeden z najbardziej irytujących anty-wzorców w PHP:
<?php
// PHP 7.x - co znaczy to "true, false, null"?
$product = $productRepository->getById(42, true, null, false);
// PHP 8.0 - czytelne nazwane argumenty
$product = $productRepository->getById(
id: 42,
editMode: true,
storeId: null,
forceReload: false
);
// Kolejność nie ma znaczenia przy named arguments
array_slice(array: $items, offset: 2, length: 5, preserve_keys: true);
// Szczególnie przydatne przy funkcjach z wieloma opcjonalnymi parametrami
implode(separator: ', ', array: ['PHP', 'Magento', 'MySQL']);
Match Expression – switch bez pułapek
match to ulepszona wersja switch: używa strict comparison, zwraca wartość, nie wymaga break i rzuca UnhandledMatchError gdy żaden przypadek nie pasuje:
<?php
// switch - luźne porównanie, łatwo o bug
$status = '1'; // string
switch ($status) {
case 1: // true! switch używa ==
echo 'active';
break;
}
// match - strict comparison, zwraca wartość
$statusLabel = match((int) $status) {
1 => 'Aktywny',
2 => 'Nieaktywny',
3, 4 => 'Oczekuje', // kilka wartości naraz
default => throw new \InvalidArgumentException("Unknown status: {$status}"),
};
// W Magento 2 - przykład w resolverze
$visibility = match($product->getVisibility()) {
1 => 'Not Visible Individually',
2 => 'Catalog',
3 => 'Search',
4 => 'Catalog, Search',
default => 'Unknown',
};
Constructor Property Promotion
Eliminuje powtarzanie właściwości w trzech miejscach – deklaracja, parametr konstruktora, przypisanie:
<?php
declare(strict_types=1);
// PHP 7.4 - trzy miejsca dla każdej właściwości
class ApiClient
{
private string $baseUrl;
private int $timeout;
private \Psr\Log\LoggerInterface $logger;
public function __construct(
string $baseUrl,
int $timeout,
\Psr\Log\LoggerInterface $logger
) {
$this->baseUrl = $baseUrl;
$this->timeout = $timeout;
$this->logger = $logger;
}
}
// PHP 8.0 - jedno miejsce
class ApiClient
{
public function __construct(
private string $baseUrl,
private int $timeout = 30,
private \Psr\Log\LoggerInterface $logger
) {}
}
W Magento 2 constructor promotion jest wygodny, ale ma jedno ograniczenie – Magento’s Object Manager działa przez refleksję i analizuje konstruktory. W praktyce promotion działa poprawnie z DI od Magento 2.4.x wzwyż, które oficjalnie wspiera PHP 8.0.
Nullsafe Operator
Operator ?-> przerywa łańcuch wywołań gdy napotka null, zamiast rzucać błąd:
<?php
// PHP 7.x - piramida sprawdzeń null
$city = null;
if ($order !== null) {
$address = $order->getShippingAddress();
if ($address !== null) {
$region = $address->getRegion();
if ($region !== null) {
$city = $region->getCity();
}
}
}
// PHP 8.0 - nullsafe operator
$city = $order?->getShippingAddress()?->getRegion()?->getCity();
// Jeśli cokolwiek po drodze zwróci null - $city = null, bez błędu
JIT Compiler – dla kogo realna różnica?
JIT (Just-In-Time compilation) kompiluje kod PHP do kodu maszynowego w runtime. Dla typowych aplikacji webowych – Magento, Symfony, Laravel – różnica jest minimalna, bo wąskim gardłem jest I/O (baza, sieć), nie obliczenia CPU. JIT błyszczy przy zadaniach numerycznych, przetwarzaniu obrazów, algorytmach – czyli wszędzie tam gdzie PHP wykonuje dużo obliczeń bez czekania na zewnętrzne zasoby.
// php.ini - włączenie JIT opcache.enable=1 opcache.jit_buffer_size=100M opcache.jit=tracing // tryb tracing - najlepszy dla większości zastosowań
Dla Magento 2 nie spodziewaj się dramatycznej poprawy wydajności po włączeniu JIT. Optymalizacja zapytań do bazy i konfiguracja Varnish/Redis dadzą znacznie więcej.
Podsumowanie
PHP 8.0 to solidny krok naprzód. Named arguments i match expression wchodzą do codziennego kodu niemal natychmiast – są wstecznie kompatybilne z istniejącą logiką. Constructor property promotion czyści konstruktory w nowym kodzie. Union types wymuszają myślenie o typach tam gdzie wcześniej uciekało się do docblocka. Magento 2.4.x oficjalnie wspiera PHP 8.0 – migracja jest możliwa, choć wymaga przeglądu zewnętrznych modułów pod kątem kompatybilności.
