Do premiery PHP 8.4 zostały dwa miesiące. Przez ostatnie tygodnie śledziłem zamknięte RFC i testowałem na RC2. Lazy Objects to feature który przemknął trochę w cieniu property hooks, a jest równie ciekawy – obiekty tworzone dopiero przy pierwszym dostępie do właściwości. Pokazuję co ostatecznie trafiło do 8.4 i jak to wpłynie na codzienny kod.
Lazy Objects – leniwa inicjalizacja bez Proxy
Do PHP 8.4 leniwa inicjalizacja obiektów wymagała ręcznego Proxy pattern lub zewnętrznych bibliotek jak Doctrine Proxy. PHP 8.4 dodaje wsparcie na poziomie Reflection API:
<?php
declare(strict_types=1);
class HeavyService
{
private array $config = [];
public function __construct()
{
// Ciężka inicjalizacja - ładuje konfigurację z bazy, robi HTTP request itd.
echo "HeavyService: inicjalizacja...\n";
sleep(0); // symulacja
$this->config = ['timeout' => 30, 'retries' => 3];
}
public function doWork(): string
{
return "Praca wykonana z config: " . json_encode($this->config);
}
}
// PHP 8.4 - Lazy Ghost
$reflector = new ReflectionClass(HeavyService::class);
// Obiekt stworzony BEZ wywołania konstruktora
$lazyService = $reflector->newLazyGhost(function(HeavyService $object): void {
// Ten callback jest wołany TYLKO przy pierwszym dostępie do właściwości
echo "Lazy: inicjalizuję teraz...\n";
// Musisz ręcznie wywołać konstruktor lub ustawić właściwości
$object->__construct();
});
echo "Obiekt stworzony - konstruktor NIE wywołany\n";
// Dopiero tutaj konstruktor zostaje wywołany
$result = $lazyService->doWork();
echo $result . "\n";
// Output:
// Obiekt stworzony - konstruktor NIE wywołany
// Lazy: inicjalizuję teraz...
// HeavyService: inicjalizacja...
// Praca wykonana z config: {"timeout":30,"retries":3}
<?php
declare(strict_types=1);
// Lazy Proxy - alternatywna forma
// Proxy to osobny obiekt który deleguje do inicjalizowanego "real" obiektu
$reflector = new ReflectionClass(HeavyService::class);
$lazyProxy = $reflector->newLazyProxy(function(HeavyService $proxy): HeavyService {
// Zwróć prawdziwy obiekt - Proxy do niego deleguje
return new HeavyService();
});
// Różnica Ghost vs Proxy:
// Ghost = ten sam obiekt, inicjalizowany lazy
// Proxy = osobny obiekt który deleguje do innej instancji (jak Decorator)
// Zastosowanie w DI Container - jak Magento robi to przez generated/Proxy
class MyContainer
{
private array $resolved = [];
public function get(string $class): object
{
if (!isset($this->resolved[$class])) {
$rf = new ReflectionClass($class);
$this->resolved[$class] = $rf->newLazyGhost(
function(object $obj) use ($class): void {
// Zbuduj zależności i wywołaj konstruktor dopiero przy pierwszym użyciu
$this->initialize($obj);
}
);
}
return $this->resolved[$class];
}
private function initialize(object $obj): void
{
// Wstrzyknij zależności...
$rf = new ReflectionClass($obj);
$constructor = $rf->getConstructor();
if ($constructor) {
$args = $this->resolveArgs($constructor->getParameters());
$constructor->invoke($obj, ...$args);
}
}
private function resolveArgs(array $params): array
{
return []; // uproszczenie
}
}
ReflectionProperty::isLazy() – nowe API
<?php // Nowe metody Reflection dla Lazy Objects $reflector = new ReflectionClass(HeavyService::class); $lazyObj = $reflector->newLazyGhost(fn($o) => $o->__construct()); // Sprawdź czy obiekt jest nadal "lazy" (nie był jeszcze inicjalizowany) var_dump($reflector->isUninitializedLazyObject($lazyObj)); // true // Wymuś inicjalizację (bez wywoływania metod) $reflector->initializeLazyObject($lazyObj); var_dump($reflector->isUninitializedLazyObject($lazyObj)); // false // Zresetuj obiekt do stanu lazy (do testowania) $reflector->resetAsLazyGhost($lazyObj, fn($o) => $o->__construct()); var_dump($reflector->isUninitializedLazyObject($lazyObj)); // true ponownie
Nowe funkcje DOM HTML5 – mniej znana perełka
<?php
// PHP 8.4 dodaje HTML5-compliant DOM parser
// Klasy: Dom\HTMLDocument i Dom\XMLDocument
// Stary sposób - DOMDocument z quirks
$dom = new \DOMDocument();
@$dom->loadHTML('<article><p>Tekst</p></article>'); // @ bo generuje ostrzeżenia
// PHP 8.4 - czysty, HTML5-compliant parser
$dom = \Dom\HTMLDocument::createFromString('
<article>
<h1>Tytuł artykułu</h1>
<p class="lead">Pierwszy akapit</p>
<p>Drugi akapit</p>
</article>
');
// querySelector i querySelectorAll jak w JavaScript!
$title = $dom->querySelector('h1');
echo $title->textContent; // Tytuł artykułu
$paragraphs = $dom->querySelectorAll('p');
foreach ($paragraphs as $p) {
echo $p->textContent . "\n";
}
// Przydatne przy scrapingu lub przetwarzaniu HTML emaili, webhooki z HTML payload
// Dom\HTMLDocument poprawnie obsługuje HTML5 semantykę bez quirks DOMDocument
BCMath z operatorami – operator overloading w PHP
<?php
// PHP 8.4 - BcMath\Number z przeciążonymi operatorami
use BcMath\Number;
$price = new Number('29.99');
$taxRate = new Number('0.23');
$quantity = new Number('3');
// Teraz możesz używać normalnych operatorów zamiast bcadd/bcmul
$subtotal = $price * $quantity; // zamiast: bcmul('29.99', '3', 2)
$tax = $subtotal * $taxRate; // zamiast: bcmul($subtotal, '0.23', 2)
$total = $subtotal + $tax; // zamiast: bcadd($subtotal, $tax, 2)
echo $total; // 110.6631 (precyzja zachowana)
// Porównania też działają
if ($total > new Number('100')) {
echo "Zamówienie powyżej 100 PLN\n";
}
// Szczególnie przydatne przy obliczeniach finansowych gdzie float jest niedokładny
$a = 0.1 + 0.2;
echo $a; // 0.30000000000000004 (błąd float)
$b = new Number('0.1') + new Number('0.2');
echo $b; // 0.3 (dokładne)
Co jeszcze w PHP 8.4
| Feature | Opis | Przydatność |
|---|---|---|
| Property hooks | get/set przy właściwości | Bardzo wysoka |
| Asymmetric visibility | public private(set) | Wysoka |
| Lazy Objects | Leniwa inicjalizacja przez Reflection | Średnia (DI, ORM) |
| new without parens | new Foo()->bar() | Niska (ergonomia) |
| array_find/all/any | Funkcje tablicowe | Wysoka |
| Dom\HTMLDocument | HTML5-compliant DOM | Średnia (parsing) |
| BcMath\Number | OOP bcmath z operatorami | Wysoka (finanse) |
Podsumowanie
PHP 8.4 to najbardziej naładowany release od czasu PHP 8.0. Property hooks i asymmetric visibility zmieniają sposób pisania klas domenowych. Lazy Objects uzupełniają DI ecosystem bez potrzeby generowania klas Proxy. BcMath\Number eliminuje bolączkę obliczeń finansowych. Premiera w listopadzie 2024 – warto przygotować projekty już teraz przez uruchomienie PHPStan z phpVersion=8.4.
