System typów w PHP rozwijał się stopniowo od wersji 5.x. PHP 7.x znacznie go wzmocnił – mamy typowanie parametrów, typowanie zwracanych wartości, nullable types i union types na horyzoncie. PHP 7.4, które pojawi się pod koniec 2019, doda typed properties. To dobry moment, żeby podsumować stan typowania w PHP i pokazać dobre praktyki, które warto stosować już dziś.
Co mamy w PHP 7.x dziś
<?php
declare(strict_types=1); // wymuszaj strict mode na poziomie pliku
class OrderCalculator
{
// Typowanie parametrów i wartości zwracanej
public function calculateTotal(array $items, float $taxRate): float
{
$subtotal = array_sum(array_column($items, 'price'));
return $subtotal * (1 + $taxRate);
}
// Nullable type - parametr może być null
public function applyDiscount(float $price, ?float $discount): float
{
if ($discount === null) {
return $price;
}
return $price - $discount;
}
// Void - metoda nic nie zwraca
public function logOrder(int $orderId): void
{
// zapis do logu
}
}
declare(strict_types=1) – używaj zawsze
Bez strict mode PHP będzie po cichu rzutować typy – string „5” przekazany do parametru int zostanie skonwertowany do liczby 5 bez żadnego błędu. Z strict mode dostaniesz TypeError:
<?php
declare(strict_types=1);
function add(int $a, int $b): int
{
return $a + $b;
}
add(2, 3); // 5 - ok
add(2.5, 3); // TypeError: Argument #1 must be of type int, float given
add("2", 3); // TypeError: Argument #1 must be of type int, string given
declare(strict_types=1) musi być pierwszą instrukcją w pliku, przed namespace i use. Dotyczy tylko wywołań funkcji w danym pliku – nie wpływa na biblioteki zewnętrzne.
Typowanie właściwości klas już dziś – przez docblock i konstruktor
PHP 7.4 wprowadzi typed properties (public int $id), ale już teraz możesz wymuszać typy przez konstruktor i docblock:
<?php
declare(strict_types=1);
class Product
{
/** @var int */
private $id;
/** @var string */
private $name;
/** @var float */
private $price;
/** @var string|null */
private $description;
public function __construct(
int $id,
string $name,
float $price,
?string $description = null
) {
$this->id = $id;
$this->name = $name;
$this->price = $price;
$this->description = $description;
}
public function getId(): int { return $this->id; }
public function getName(): string { return $this->name; }
public function getPrice(): float { return $this->price; }
public function getDescription(): ?string { return $this->description; }
}
To wzorzec, który już dziś warto stosować w modułach Magento 2 – szczególnie w Data Models implementujących Data Interfaces. Typy w konstruktorze dają Ci wczesne wykrywanie błędów, a docblock wspiera autouzupełnianie w PHPStorm.
Zwracanie typów interfejsów zamiast klas konkretnych
Dobra praktyka przy typowaniu zwracanych wartości – zwracaj interfejs, nie konkretną klasę. Twój kod staje się bardziej elastyczny i łatwiejszy do mockowania w testach:
<?php
declare(strict_types=1);
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
class ProductService
{
public function __construct(
private ProductRepositoryInterface $productRepository
) {}
// Zwracamy interfejs, nie Model\Product
public function getById(int $id): ProductInterface
{
return $this->productRepository->getById($id);
}
// Zamiast: public function getById(int $id): \Magento\Catalog\Model\Product
}
Typowanie tablic – ograniczenia i obejścia
PHP nie pozwala na typowanie zawartości tablicy – int[] nie jest poprawnym type hintem. Możesz to obejść przez docblock lub przez własne kolekcje:
<?php
declare(strict_types=1);
class ProductCollection
{
/** @var ProductInterface[] */
private array $items = [];
public function add(ProductInterface $product): void
{
$this->items[] = $product;
}
/**
* @return ProductInterface[]
*/
public function all(): array
{
return $this->items;
}
public function count(): int
{
return count($this->items);
}
}
PHPStorm rozumie @var ProductInterface[] i podpowiada metody przy iteracji po all(). To nie wymusza typów w runtime, ale znacznie ułatwia pracę ze statyczną analizą kodu (PHPStan, Psalm).
PHPStan – statyczna analiza jako siatka bezpieczeństwa
Warto dodać PHPStan do projektu jako narzędzie CI:
composer require --dev phpstan/phpstan # Analiza na poziomie 5 (skala 0-8, im wyżej tym bardziej rygorystycznie) vendor/bin/phpstan analyse src --level=5
PHPStan wyłapuje błędy typów, wywołania nieistniejących metod i wiele innych problemów bez uruchamiania kodu. Dla Magento 2 dostępne są gotowe reguły przez pakiet bitExpert/phpstan-magento.
Podsumowanie
Typowanie w PHP to nie formalność – to narzędzie, które wyłapuje błędy na etapie pisania kodu zamiast na produkcji. declare(strict_types=1) kosztuje jedną linię, a eliminuje całą klasę cichych konwersji typów. W kontekście Magento 2, gdzie kod przechodzi przez warstwy DI, pluginów i repository – im więcej typów, tym łatwiejsza diagnostyka problemów.
