PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP 8.4 ostatnie RC – Lazy Objects, BcMath\Number, Dom\HTMLDocument

by Henryk Tews / wtorek, 03 września 2024 / Opublikowano w PHP

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.

About Henryk Tews

Co możesz przeczytać następne

PHP 8.4 preview – property hooks, asymmetric visibility, chaining new
WooCommerce vs Magento 2 – architektura, hooks vs DI, kiedy który
PHPUnit – testy jednostkowe, mocki, data providers, testy w Magento 2
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}