PHP nie wymaga precyzowania typu podczas definicji zmiennej. Jest on określany na podstawie kontekstu, w jakim ta zmienna została użyta, z możliwością automatycznych konwersji. Możemy także samodzielnie zmieniać typ zmiennej poprzez rzutowanie lub odpowiednie funkcje. Rodzi to problemy szczególnie przy przekazywaniu parametrów do funkcji, jednak w tym przypadku można dla niektórych stosować podpowiadanie typów (ang. type hinting).
W PHP mamy do dyspozycji łącznie 9 typów zmiennych:
- 4 typy proste – logiczny, całkowity, zmiennoprzecinkowy i znakowy
- 2 złożone – tablicowy i obiektowy
- 3 specjalne – identyfikator zasobu (
resorce
), pusty (NULL
) oraz wywoływalny (callable
)
Podpowiadanie typów w PHP nie umożliwia niestety rozpoznawania wszystkich wymienionych powyżej. Od wersji 5 języka możemy to robić dla obiektów, interfejsów, tablic (od PHP 5.1), oraz parametrów wywoływalnych (PHP 5.4).
Podpowiadanie typów możemy stosować w zwykłych funkcjach, oraz metodach klas. Wystarczy w tym celu przed nazwą zmiennej podać oczekiwany typ (array
, callable
, nazwę konkretnej klasy lub interfejsu)
Podpowiadanie typów – obiekty
W przypadku podpowiadania typów obiektowych należy pamiętać o bardzo ważnej rzeczy: jeśli parametrem musi być konkretna klasa lub interfejs, dozwoleni są także ich potomkowie, uczestniczący w hierarchii dziedziczenia.
Gdy zostanie określony wymagany typu obiektu, przekazanie innego spowoduje powstanie błędu. Zostało to pokazane na poniższym przykładzie:
class Factory{ public function produce(Product $product){ print "Produkuję " . get_class($product); } } class Stock{ /* ... */ } class Product{ /* ... */ } class Car extends Product{ /* ... */ } class Motorbike extends Product{ /* ... */ } $factory = new Factory(); $stock = new Stock(); $car = new Car(); $motorbike = new Motorbike(); $factory->produce($car); $factory->produce($motorbike); $factory->produce($stock);
Zdefiniowane zostały klasy: Factory
, Stock
, Product
oraz dziedziczące po nim Car
oraz Motorbike
. Klasa fabryki posiada zdefiniowaną jedną metodę, odpowiadającą za produkcję. Wymaga ona przekazania obiektu typu Product
. Na powyższym listingu przekazano do niej natomiast dozwolone parametry wywodzące się z klasy Product
, ale na koniec omyłkowo także Stock
. Efekt będzie następujący:
Produkuję Car Produkuję Motorbike Fatal error: Argument 1 passed to Factory::produce() must be an instance of Product, called in typehinting.php on line 31 and defined in typehinting.php on line 3
Jak wspomniano już wcześniej, podpowiadanie typów w przypadku obiektów działa zarówno dla utworzonych bezpośrednio, jak i dziedziczących po określonym w definicji. Poprawne będzie więc też następujące wywołanie:
$factory = new Factory(); $product = new Product(); $factory->produce($product);
W powyższym przypadku może nie jest to do końca pożądane, jednak w praktyce częściej będziemy oczekiwali obiektów klas dziedziczących. Oczywiście, gdy tego chcemy, możemy zawęzić wymagany typ tylko do klasy pochodnej.
class Factory{ public function produce(Car $product){ print "Produkuję tylko samochody"; } }
W przypadku podpowiadania typów można stosować także wartości domyślne. Jeśli w naszej fabryce przewidujemy strajk, możemy posłużyć się następującą definicją:
class Factory{ public function produce(Product $product = null){ print "Produkuję" . get_class($product); } } $factory = new Factory(); $factory->produce();
Analogicznie do powyższych informacji, podpowiadanie typów może być używane także w przypadku interfejsów. Wystarczy określić, że parametr musi implementować jeden ze zdefiniowanych. Aby zobrazować tą możliwość, dokonamy pewnych zmian klas z pierwszego przykładu. Otóż w naszej fabryce w Indiach zamierzamy wyprodukować limitowaną wersję samochodu, o zwiększonej mocy silnika. Klasa fabryki otrzyma więc dodatkową możliwość produkcji, a specyfikacja samochodu zostanie odpowiednio zmodyfikowana poprzez implementację interfejsu (pominięto tu klasy zdefiniowane w pierwszym przykładzie):
interface limitedEdition{ public function engineImprovements(); } class carSpecial extends car implements limitedEdition{ public function engineImprovements(){ print "Zwiększona moc silnika"; } } class FactoryIndia extends Factory { public function produceSpecialCar(limitedEdition $car){ print "Produkuję limitowaną wersję samochodu."; } } $factoryIndia = new FactoryIndia(); $car = new car(); $carSpecial = new carSpecial(); $factoryIndia->produceSpecialCar($carSpecial); $factoryIndia->produce($car); $factoryIndia->produceSpecialCar($car);
Efekt wywołania kodu będzie następujący:
Produkuję limitowaną wersję samochodu. Produkuję Car Fatal error: Argument 1 passed to FactoryIndia::produceSpecialCar() must implement interface limitedEdition, called in typehinting.php5 on line 43 and defined in typehinting.php on line 32
Jak widać, najpierw wyprodukowano edycję limitowaną samochodu przy użyciu nowej linii produkcyjnej, a następnie powrócono do wersji zwykłej. W kolejnym kroku, w wyniku błędu pracownika, uruchomiono jednak ponownie zmodyfikowaną linię produkcyjna dla wersji podstawowej samochodu, co oczywiście zostało szybko wychwycone przez inspektorów kontroli jakości.
Podpowiadanie typów – tablice
Sytuacja jest analogiczna do opisywanych powyżej możliwości w przypadku obiektów. Poniższy przykład obrazuje w jaki sposób zastrzec, aby zdefiniowana funkcja akceptowała jako parametr tylko tablicę:
function needArray(array $param){ print_r($param); } $array = array(1, 2, 3); needArray($array);
Możemy także określić domyślą tablicę, dzięki czemu możliwe będzie wywołanie funkcji bez żadnego parametru:
function needArray(array $param = array(7, 8, 9)){ print_r($param); }
Podpowiadanie typów – callable
Jest to najnowsza możliwość podpowiadania typów, wprowadzona w PHP 5.4. Dzięki niej można określić, że parametr przekazywany do funkcji lub metody musi być wywoływalny. Ma to zastosowanie w przypadku stosowania callbacków oraz funkcji anonimowych.
function myFunction(callable $callback) { echo "Wywołuję callback: " . $callback(); } myFunction(function() { return 'Witaj!'; });
Podsumowanie
Jak widać na przedstawionych przykładach, wykorzystanie podpowiadania typów jest niezwykle przydatne i znacznie upraszcza tworzony kod. Nie musimy aż tak wnikliwie sprawdzać przekazywanych argumentów przy pomocy is_array()
lub instanceof
. Mam nadzieję, że w type hinting w kolejnych wersjach PHP zostanie rozszerzone o typy proste.