Troszkę o funkcjach anonimowych

Trochę wstępu

Funkcje anonimowe w wielu językach zwane funkcjami / wyrażeniami lambda, nie są niczym nowym w świecie programowania a tym bardziej nie są jakimś wymysłem języka PHP, ponieważ można znaleźć je w wielu innych językach, w PHP są od 28 czerwca 2000 r. dokładnie od wersji 4.0.1, jak możemy wyczytać z change loga

Added create_function(), which gives the ability to create functions on-the-fly (Zeev, Zend Engine)

Natomiast kolejnym ważnym krokiem w funkcjonowaniu i postrzeganiu funkcji anonimowej w PHP jest wersja 5.3.
Na przestrzeni kolejnych wydań języka PHP, także i one (tzn. funkcje anonimowe dalej zwane FA) ewoluowały w wersji 5.47.0 oraz w najnowszym wydaniu 7.1 (na dzień pisania wpisu jest to najnowsza wersja). Kolejną ważna wersją dla FA będzie wersja 7.2.0, gdzie została ona oznaczona jako (na chwilę obecną wersja 7.2.0 ma status beta) DEPRECATED.

This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.

Pozwól, że w tym wpisie przyjrzymy się im troszkę bardziej, dla bardziej doświadczonych koderów temat może wydać się nudny.

A więc do dzieła

Zacznijmy od tego jak zadeklarować taką funkcję anonimową, dla przykładu przypiszmy ją do zmiennej $var

Super, mamy pierwszą funkcje anonimową przypisaną na zmiennej $var, ale jak ją teraz wywołać ?
…a no tak jak każdą inną funkcję a więc $var().
Co więcej jeśli nasza funkcja anonimowa przyjmuje parametry np.

To powyższą funkcję wywołujemy następująco: $var('wartość parametru 1', 'wartość parametru 2');
Prawda że żaden rocket science ?
zobaczmy co zwróci nam var_dump dla powyższego przykładu

A więc nasza funkcja anonimowa jest instancja obiektu Closure, zaraz zaraz funkcja jest obiektem i żeby było tego mało, obiekt ‚przedstawia’ nam się jako domknięcie ?

Closure

Definicja domknięcia brzmi następująco (źródło wikipedia):

Domknięcie – w metodach realizacji języków programowania jest to obiekt wiążący funkcję lub referencję do funkcji oraz środowisko mające wpływ na tę funkcję w momencie jej definiowania. Środowisko przechowuje wszystkie nielokalne obiekty wykorzystywane przez funkcję. Realizacja domknięcia jest zdeterminowana przez język, jak również przez kompilator.

I ważna informacja nie każda funkcja anonimowa jest domknięciem ! ! !

Powyższą informację radzę zapisać, wydrukować, zapamiętać !

Środowisko programistów w podejściu do domknięć jest bardzo podzielone, niektórzy uważają, że wystarczy wydzielić osobny zakres (scope) dla danego obiektu / zmiennych i to staje się już domknięciem, co by wskazywało na to, że każda funkcja anonimowa staje się domknięciem (a przecież tak nie jest, co przed chwilą wspominałem).
Dla zobrazowania o co chodzi tutaj przykład

Aby powyższy przykład stał się domknięciem, musi on wedle definicji mieć dostęp do nielokalnych zasobów (przez zasób mam na myśli obiekt lub zmienną), na poniższym przykładzie pokaże Ci które zasoby są nielokalne dla przyszłego domknięcia.

Jeżeli w funkcji $fa będziemy chcieli odwołać się do zasobu nielokalnego np. $zmienna1 czeka na nas niestety okrutny error Undefined variable: zmienna1.
Jeżeli chcemy użyć zasobu nielokalnego musimy dowiązać taki zasób z naszym domknięciem, w PHP robi się to za pomocą operatora use pamiętaj, że dowiązanie tworzy się przez wartość ale można wymusić referencje. Oto i przykład.

Closures w PHP

A więc jeśli już wiemy, że nasze funkcje anonimowe w PHP to domknięcia a bardziej szczegółowo, są obiektem klasy Closure, to przyjrzyjmy się troszkę bardziej owej klasie.
Posiada ona 5 metod z czego 2 to statyczne metody, jedna to konstruktor co więcej jest on prywatny, oraz dwie publiczne metody.

Skoro konstruktor jest zadeklarowany jako prywatny, to „jedynym” sposobem na utworzenie instancji tego obiektu jest zastosowanie wyżej wymienionego sposobu deklarowania funkcji anonimowej.
Kolejnym ważnym elementem jest to iż metoda call została dodana wraz z wersją 7.0 i jest ona małym ‚shorthandem’ dla metody bindTo.
Metody obiektu closure służą nam do wykonywania lub powiązania domknięcia z danym obiektem lub jego kontekstem, inaczej mówiąc, możemy wykonać domknięcie w obrębie danego obiektu używając jego kontekstu, co więcej można w dowolny sposób modyfikować ten obiekt.
Metoda call pozwala na natychmiastowe wykonanie domknięcia z wykorzystaniem jego kontekstu. natomiast metody bind oraz bindTo służą do powiązania danego domknięcia z reprezentantem klasy, co skutkuje tym, że do końca wykonania skryptu powiązanie jest aktywne co nie zawsze może nam się podobać.
Sprawdźmy teraz jak wykonać wybrane domknięcie w obrębie testowej klasy, najpierw utwórzmy przykładowe domknięcie

a teraz utwórzmy przykładową klasę i jej obiekt

następnie poskładajmy wszystko w całość i wykonajmy domknięcie w obrębie kontekstu klasy A

i na ekranie zobaczymy piękne hello world! ponieważ domknięcie zostało wykonane w kontekście obiektu klasy A, co więcej bez problemu możemy zmodyfikować taki obiekt, oto przykład

Teraz dostaniemy na ekranie piękny tekst hello closure.
Zwróć uwagę że zakres widoczności pola został ustawiony jako private i dalej mamy dostęp do danego pola co więcej nadal możemy je dowolnie zmieniać.
Dla czystej formalności przedstawię powyższy przykład z użyciem bindTo zamiast call.
Przy bindTo możemy wskazać dowolny obiekt lub klasę !

Zanim zakończymy, powyższe przykłady działają od wersji 5.3+ a przecież wspomniałem, że można utworzyć FA już od wersji 4.0.1, masz rację i poniżej przedstawię jak tworzy się takie wyrażenie lambda w alternatywny sposób którego nie jestem fanem i nie polecam, ale niestety muszę o nim wspomnieć skoro wpis ma być kompetentny.
A więc od wersji 4.0.1 do wersji 7.2.0 możemy FA zadeklarować w poniższy sposób (analogicznie do powyższych przykładów):

oraz przykład z argumentami funkcji

Wykonanie powyższych 2 funkcji jest takie same jak wszystkich innych pozostałych funkcji a więc $var().
Ciekawostką jest natomiast co zwraca var_dump dla tak utworzonej funkcji.

Zwróć uwagę na cyfrę na końcu zwróconej zwrotki, jest ona licznikiem wyrażeń lambda w naszym kodzie, analogicznie do licznika #<N> gdzie N jest cyfrą, w przypadku var_dump dla obiektów, przykład poniżej

Czas kończyć

Na zakończenie dodam, że wyżej w tekście wspomniałem iż jedynymi sposobami na utworzenie domknięcia jest użycie zapisu function… ale można by pokusić się o użycie np. ReflectionAPI lub jak każdy inny obiekt, także i Closure posiada magiczne __clone() oraz __wakeup() , co pozwala nam na utworzenie wielu instancji tego samego domknięcia.
Pamiętaj, że domknięcia są bardzo potężnym narzędziem w pracy każdego developera, nie każdy musi je rozumieć ale każdy musi przynajmniej wiedzieć co one potrafią i czy mogą nam uprzykrzyć życie, w następnych wpisach postaram się pokazać jak bardzo mogą nam pomóc lub zaszkodzić.
I tym miłym akcentem kończę mój pierwszy wpis na blogu (początki są zawsze trudne) mam nadzieję drogi czytelniku, że dotrwałeś do tego momentu a na Twojej twarzy nie pojawiło się zniesmaczenie, a jeżeli masz jakieś uwagi nie krępuj się zostaw informację w komentarzu.