Najnowsza wersja PHP oznaczona numerem 5.3 wprowadziła do języka szereg usprawnień, o których programiści jeszcze niedawno mogli jedynie marzyć. Jednym z takich usprawnień są funkcje anonimowe, nazywane również domknięciami (closures). Mimo swoich wad znacząco wpływają na sposób programowania, wprowadzając do słownika programisty PHP konstrukcje określane mianem funkcji zwrotnych.

Najprostsza funkcja anonimowa ma postać

$funkcja = function()
{
	echo 'Jestem funkcja anonimowa';
};

$funkcja();

Wprawdzie zastosowanie takiej funkcji jest żadne, niemniej powyższy kod daje pogląd na to jak funkcja anonimowa jest definiowana.

Co tak naprawdę powyższy kod robi? Korzysta z wbudowanej w PHP klasy Closure (oznaczonej jako finalna), która zawiera magiczną metodę __invoke. Innymi słowy funkcja anonimowa jest obiektem klasy Closure, co doskonale widać na poniższym przykładzie.

// zmienna $funkcja została zdefinowana w poprzednim przykładzie
var_dump($funkcja);
/*
wynik działa funkcji var_dump:

object(Closure)#1 (0) {
}
*/

Metoda __invoke znajdująca się w klasie Closure, wywoływana jest w momencie, gdy do obiektu odwołujemy się tak, jakby był funkcją. I to jest cały sekret funkcji anonimowych. Tak naprawdę w tle dzieje się coś takiego

$funkcja->__invoke();

Do anonimowych funkcji można przekazywać argumenty w taki sam sposób, jak do standardowych funkcji.

$funkcja = function($arg1, $arg2)
{
	echo $arg1;
	echo $arg2;
};

$funkcja(123, 'abc');

Innym sposobem przekazania danych do funkcji anonimowej jest skorzystanie z zmiennych dostępnych w tym samym zasięgu, w którym znajduje się funkcja.

$zmienna = 123;
$funkcja = function()
{
	echo $zmienna;
};

$funkcja();

Niestety (a może i stety) funkcja anonimowa nie ma do nich bezpośredniego dostępu i to my musimy wskazać jakich zmiennych chcemy użyć. Z tego właśnie względu powyższy kod nic nie wyświetli oraz zgłosi notice.

$zmienna = 123;
$funkcja = function() use ($zmienna)
{
	echo $zmienna;
};

$funkcja();

Dopiero użycie use spowodowało, że zmienna jest widoczna wewnątrz funkcji anonimowej. W celu przekazania większej ilości zmiennych wystarczy oddzielić je od siebie przecinkiem.

W tym momencie ujawniają się dwie poważne wady funkcji anonimowych. Po pierwsze, do funkcji przekazywana jest kopia zmiennej, wykonana w momencie definiowania funkcji, a nie w momencie jest użycia.

$funkcja = function() use ($zmienna)
{
	echo $zmienna;
};

$zmienna = 123;

$funkcja();

Efekt działania powyższego kodu będzie identyczny z tym, w którym nie skorzystaliśmy z use. Jest to o tyle niewygodne, że funkcje muszą być definiowane na samym końcu naszego kodu, co nie zawsze jest możliwe. Na szczęście można zasymulować efekt późnego wiązania (late binding) poprzez skorzystanie z referencji.

$funkcja = function() use (&$zmienna)
{
	echo $zmienna;
};

$zmienna = 123;

$funkcja();

Jeśli do funkcji anonimowej przekazywany jest obiekt, ampersand nie jest potrzebny, ponieważ obiekty domyślnie przekazywane są przez referencję (pod warunkiem, że utworzenie nowego obiektu nastąpi przez funkcją anonimową).

// BLĄD
$funkcja = function() use ($obiekt)
{
	var_dump($obiekt);
};

$obiekt = new stdClass;
$obiekt->x = 123;

$funkcja();

/******************************************/

// OK
$funkcja = function() use (&$obiekt)
{
	var_dump($obiekt);
};

$obiekt = new stdClass;
$obiekt->x = 123;

$funkcja();

/******************************************/

// OK
$obiekt = new stdClass;

$funkcja = function() use ($obiekt)
{
	var_dump($obiekt);
};

$obiekt->x = 123;

$funkcja();

/******************************************/

// OK
$obiekt = new stdClass;
$obiekt->x = 123;

$funkcja = function() use ($obiekt)
{
	var_dump($obiekt);
};

$funkcja();

W każdym z przedstawionych przypadków (poza pierwszym), wynikiem działania będzie

object(stdClass)#1 (1) {
  ["x"]=>
  int(123)
}

Drugą poważna wadą funkcji anonimowych jest brak możliwości użycia (use) zmiennej $this w kontekście obiektu. W PHP 5.4 ten problem ma być rozwiązany, niestety do tego czasu musimy sobie radzić sami.

class Foo
{
	public $foo = 123;
	protected $_bar = 'abc';

	public function Bar()
	{
		$funkcja = function() use ($this)
		{
			var_dump($this);
		};
		
		$funkcja();
	}
}

$obj = new Foo();
$obj->Bar();

Powyższy kod spektakularnie się wyłoży zgłaszając błąd:

PHP Fatal error:  Cannot use $this as lexical variable

Jak temu zaradzić? Rozwiązania są dwa. Pierwszym jest przekazanie zmiennej $this jako argumentu funkcji, drugim przypisanie $this do zmiennej i dodanie jej do zasięgu widoczności funkcji anonimowej.

public function Bar()
{
	$funkcja = function($that)
	{
		var_dump($that);
	};
	
	$funkcja($this);
}

public function Bar()
{
	$that = $this;
	$funkcja = function() use ($that)
	{
		var_dump($that);
	};
	
	$funkcja();
}

Niestety żadne z rozwiązań nie jest w pełni satysfakcjonujące, ponieważ nie daje dostępu do właściwości i metod innych niż publiczne.

Po co w ogóle są funkcje anonimowe? Przede wszystkim można z nich korzystać we wbudowanych w PHP funkcjach, korzystających z callbacków, np. array_filter. Jednak prawdziwą siłę pokazują w momencie tworzenia klas, które są świadome istnienia tej konstrukcji. Funkcje anonimowe dają możliwość wstrzykiwania dowolnego kodu do obiektu i wykorzystania go w późniejszym momencie. Dzięki temu możemy tworzyć o wiele bardziej elastyczne aplikacje, z luźno powiązanymi komponentami, które można dowolnie wymieniać. O praktycznym zastosowaniu funkcji anonimowych przeczytacie we wpisie Automatyczna obsługa formularzy Zend_Form, w którym zostały użyte jako callbacki wywoływane w momencie przejścia walidacji.