PHP 7.4 wyszło w listopadzie 2019. Miałem kilka tygodni na przejście kilku modułów Magento 2 na nową wersję i wyrobienie sobie zdania. Typed properties to zmiana, która realnie wpływa na sposób pisania klas – ale ma też kilka pułapek, na które warto uważać. Pokazuję co działa świetnie, co zaskakuje i jak wygląda migracja istniejącego kodu.
Typed properties – pułapki przy migracji
Najpopularniejszy błąd przy migracji na typed properties to niezainicjalizowane właściwości. Przed PHP 7.4 niezainicjalizowana właściwość zwracała null z ostrzeżeniem. Teraz rzuca Error:
<?php
declare(strict_types=1);
class CustomerData
{
// Stary kod - działało, bo PHP zwracało null
/** @var string */
private $firstName;
// Nowy kod - Error jeśli odczytasz przed przypisaniem
private string $firstName;
public function getFirstName(): string
{
// Jeśli konstruktor nie przypisał $firstName - Error tutaj
return $this->firstName;
}
}
Rozwiązanie – zawsze inicjalizuj w konstruktorze lub użyj wartości domyślnej:
<?php
declare(strict_types=1);
class CustomerData
{
private string $firstName;
private string $lastName;
private ?string $company = null; // nullable z domyślnym null
private float $creditLimit = 0.0; // wartość domyślna
public function __construct(string $firstName, string $lastName)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
// $company i $creditLimit mają wartości domyślne - ok
}
}
Typed properties a serializacja i hydratacja
Typowy wzorzec w Magento 2 to hydratacja modelu z tablicy danych z bazy. Z typed properties musisz uważać na typy podczas hydratacji:
<?php
declare(strict_types=1);
class ProductData
{
private int $id;
private string $sku;
private float $price;
private bool $isActive;
public static function fromArray(array $data): self
{
$obj = new self();
// Baza danych zwraca stringi - rzutowanie konieczne
$obj->id = (int) $data['entity_id'];
$obj->sku = (string) $data['sku'];
$obj->price = (float) $data['price'];
$obj->isActive = (bool) $data['status'];
return $obj;
}
public function getId(): int { return $this->id; }
public function getSku(): string { return $this->sku; }
public function getPrice(): float { return $this->price; }
public function isActive(): bool { return $this->isActive; }
}
Bez jawnego rzutowania PHP rzuci TypeError przy próbie przypisania stringa „1” do właściwości int – nawet jeśli wartość wygląda jak liczba.
Constructor promotion – zapowiedź z PHP 8.0
PHP 8.0 zapowiada Constructor Property Promotion – skrócony zapis, który eliminuje powtarzanie właściwości w deklaracji i konstruktorze. To naturalne rozwinięcie typed properties:
<?php
// PHP 7.4 - powtarzanie w trzech miejscach
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 - jeden zapis w konstruktorze
class ApiClient
{
public function __construct(
private string $baseUrl,
private int $timeout,
private \Psr\Log\LoggerInterface $logger
) {}
}
PHP 8.0 przyniesie też union types, match expression i named arguments. Więcej o tym gdy wyjdzie.
Arrow functions w codziennym użyciu – po kilku tygodniach
Arrow functions (PHP 7.4) bardzo naturalnie weszły do kodu przy operacjach na kolekcjach. Kilka wzorców, których używam najczęściej:
<?php
declare(strict_types=1);
// Transformacja kolekcji produktów
$skus = array_map(
fn(\Magento\Catalog\Api\Data\ProductInterface $p): string => $p->getSku(),
$products
);
// Filtrowanie z dostępem do zewnętrznej zmiennej - bez use
$minPrice = 10.0;
$affordable = array_filter(
$products,
fn(\Magento\Catalog\Api\Data\ProductInterface $p): bool => $p->getPrice() >= $minPrice
);
// Sortowanie wielopoziomowe
usort(
$products,
fn($a, $b) => [$a->getPrice(), $a->getSku()] <=> [$b->getPrice(), $b->getSku()]
);
// Zagnieżdżone arrow functions - $factor przechwycony z zewnątrz
$factor = 1.23;
$withTax = array_map(
fn(float $price): float => round($price * $factor, 2),
$prices
);
Null coalescing assignment w praktyce
Operator ??= z PHP 7.4 pojawia się naturalnie przy lazy initialization i przy pracy z konfiguracją:
<?php
declare(strict_types=1);
class ConfigProvider
{
private array $cache = [];
public function get(string $path, mixed $default = null): mixed
{
// Zamiast: $this->cache[$path] = $this->cache[$path] ?? $this->load($path);
$this->cache[$path] ??= $this->load($path) ?? $default;
return $this->cache[$path];
}
private function load(string $path): mixed
{
// ładowanie z bazy lub pliku
return null;
}
}
// Przy budowaniu tablic z opcjonalnymi kluczami
$options = [];
$options['timeout'] ??= 30;
$options['retries'] ??= 3;
$options['debug'] ??= false;
Podsumowanie
PHP 7.4 po kilku tygodniach w produkcyjnych projektach sprawdza się bardzo dobrze. Typed properties wymagają dyscypliny przy hydratacji danych z zewnętrznych źródeł, ale w zamian dają natychmiastowe wykrywanie błędów typów. Arrow functions naturalnie zastępują closures z use wszędzie tam gdzie transformujesz kolekcje. Warto zaktualizować projekty zanim pojawi się PHP 8.0 – zmiany są ewolucyjne i migracja jest przewidywalna.
