Konstruktor klasy jest specjalną funkcją uruchamianą automatycznie podczas tworzenia nowej instancji klasy, jednokrotnie w trakcie życia danego obiektu (oczywiście możemy potem w jawny sposób wywołać go w kodzie, jednakże nie jest to polecana praktyka programistyczna). W języku PHP to stwierdzenie także jest prawdziwe, aczkolwiek w wielu miejscach interpreter udowadnia nam, że z powodzeniem możemy potraktować konstruktor jak zwykłą funkcję.

Wstęp: Implementujemy interfejs w klasie.

Żeby nie być gołosłownym, weźmy pod uwagę przykładowy interfejs i implementującą go klasę:

<?php
interface IFoo
	{
	public function foo();
	}

class Foo implements IFoo
	{
	public function foo()
		{
		}
	}

$foo = new Foo();
$foo->foo();

Mamy tutaj dwie możliwości popełnienia błędu:

  • sygnatura funkcji w klasie niezgodna z deklaracją w interfejsie, np:
    • różna ilość parametrów,
    • różne typy parametrów, jeśli wykorzystujemy type hinting.
  • brak definicji (jednej lub większej ilości) funkcji implementowanego interfejsu w klasie.
W pierwszym przypadku otrzymamy błąd:
Fatal error: Declaration of Foo::foo() must be compatible with that of IFoo::foo() in [path]/index.php on line [line]
W drugim zostaniemy poinformowani, że:
Fatal error: Class Foo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (IFoo::foo) in [path]/index.php on line [line]
Ale cały czas mówiliśmy o funkcjach, a co z tytułowym konstruktorem?

Cel: Deklaracja konstruktora klasy w interfejsie.

Spróbujmy więc do pierwszego listingu dopisać konstruktor, deklarując go w interfejsie oraz “implementując” w klasie:

<?php
interface IFoo
	{
	public function __construct();
	}

class Foo implements IFoo
	{
	public function __construct()
		{
		}
	}

$foo = new Foo();

Da się? Oczywiście, że się da! Powyższy kod bezproblemowo zostanie wykonany przez interpreter, nie pozostawiając po sobie ani śladu błędu. Eclipse, z którego korzystam do pisania kodu, w tym przypadku nawet “podświetli” szarym trójkącikiem metodę construct() w klasie Foo, a po najechaniu wyświetli dymek: “implements IFoo.construct”.

Sprawdźmy jeszcze, czy jeśli wprowadzimy celowo błędy wymienione we wstępie, to interpreter zareaguje w podobny sposób - będzie to oznaczało, że faktycznie możemy potraktować konstruktor jak zwykłą funkcję.

Mieliśmy dwie możliwości popełnienia błędu, a więc:

  • sygnatura funkcji w klasie niezgodna z deklaracją w interfejsie:

<?php
interface IFoo
	{
	public function __construct($foo);
	}

class Foo implements IFoo
	{
	// Fatal error: Declaration of Foo::__construct() must be compatible with that of IFoo::__construct() in [path]/index.php on line [line]
	public function __construct()
		{
		}
	}

$foo = new Foo();

  • brak definicji (jednej lub większej ilości) funkcji implementowanego interfejsu w klasie:

<?php
interface IFoo
	{
	public function __construct();
	}

class Foo implements IFoo
	{
	// Fatal error: Class Foo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (IFoo::__construct) in [path]/index.php on line [line]
	}

$foo = new Foo();

Jak widać na wstawionych listingach (w komentarzach wstawiłem błędy, jakimi “rzuca” interpreter), postawione wyżej tezy są prawdziwe.

Po raz kolejny PHP zachowuje się w “ciekawy” sposób, którego musimy być świadomi, chcąc w pełni wykorzystać możliwości języka. Deklarowanie sygnatury konstruktora w implementowanym interfejsie może być np. pomocne w uściśleniu, jakie argumenty ma przyjmować konstruktor klasy konkretnej “strategii” we wzorcu Strategy.

Oczywiście możliwości jest wiele i zależą one jedynie od naszej wyobraźni. Jeśli przychodzi Wam na myśl jakiś ciekawy sposób wykorzystania informacji zebranych w niniejszym wpisie, podzielcie się nimi w komentarzach - z góry dziękuję!