W poprzednim rozdziale opisałem podstawowe techniki definiowania funkcji w PHP, których znajomość jest wystarczająca w większości przypadków. Natomiast w tym rozdziale opisuję bardziej zaawansowane techniki definiowania funkcji PHP, które również warto znać, choć może nie spotyka się ich na każdym kroku.
Język PHP zapewnia całkiem szeroki wachlarz technik związanych z definiowaniem i używaniem funkcji. Jeśli będziesz je znać, to być może niektóre działania uda ci się wyrazić w kodzie prościej i zwięźlej. W szczególności funkcje strzałkowe dają taką możliwość. Warto się z nimi zaznajomić, choć nie tylko im jest poświęcony ten rozdział.
W tym rozdziale poznasz odpowiedzi między innymi na następujące pytania:
- Czym są funkcje wywołań zwrotnych?
- Czym są funkcje anonimowe?
- Czym są funkcje strzałkowe?
- Czym są zmienne funkcyjne?
- Co to jest rekurencja i czym są funkcje rekurencyjne?
- Jaki jest zakres dostępności zmiennych definiowanych w funkcjach?
Zaczniemy od funkcji wywołań zwrotnych, z którymi ściśle wiążą się także pojęcia funkcji anonimowych i strzałkowych. W jaki sposób? Wkrótce się dowiesz.
Funkcje wywołań zwrotnych PHP
Funkcje wywołań zwrotnych, w skrócie często nazywane funkcjami zwrotnymi (ang. callback function), to funkcje przekazywane jako argument do innych funkcji w celu realizacji na ich rzecz pewnych działań.
Przeczytaj powyższą definicję jeszcze raz bardzo uważnie. Pewnie już się orientujesz, że jeśli chodzi o definiowanie funkcji zwrotnych, nie muszę ci nic wyjaśniać, bo są to po prostu zwykłe funkcje, które już umiesz definiować.
Objaśnienia natomiast wymaga samo pojęcie wywołania zwrotnego. Wywołanie zwrotne (ang. callback) to prosta technika programistyczna, która polega na wywołaniu pewnej podprocedury (w PHP funkcji albo metody) przez inną podprocedurę (w PHP funkcję albo metodę), aby ta wykonała dla niej pewne działania.
Technika ta znajduje wiele zastosowań, a jednym z obszarów, w których jest często używana, są operacje tablicowe.
Powiedzmy na przykład, że mamy tablicę liczb i chcielibyśmy ją posortować od największej do najmniejszej. Do tego celu możemy użyć funkcji PHP usort()
, która jako drugi argument przyjmuje właśnie funkcję wywołania zwrotnego określającą sposób sortowania.
Funkcja PHP usort()
ma następujący prototyp:
usort(array &$array, callable $callback): true
To znaczy, że w pierwszym argumencie przyjmuje tablicę przez referencję, a w drugim — funkcję zwrotną. Funkcja ta powinna porównywać po dwie wartości i zwracać dla nich następujące wyniki typu int
:
- 0 — jeśli porównywane elementy zostają uznane za równe.
- Wartość mniejsza od 0 — jeśli pierwszy element zostaje uznany za mniejszy od drugiego.
- Wartość większa od 0 — jeśli pierwszy argument zostaje uznany za większy od drugiego.
Dlaczego używam dość dziwnej konstrukcji w rodzaju „zostaje uznany za mniejszy od drugiego” zamiast po prostu napisać „jest mniejszy od drugiego”? Robię tak, ponieważ to od programisty zależy, jaką definicję większości i mniejszości przyjmie.
Aby na przykład posortować tablicę malejąco, będziemy większa z porównywanych wartości uznawać za mniejszą i odwrotnie.
Inaczej mówiąc, w przypadku pary wartości 5
i 7
, które porównamy za pomocą wyrażenia 5 < 7
, nasza funkcja zwrotna będzie zwracać wartość 1
, oznaczającą że wartość 5
uznajemy za większą. Dzięki temu wartość 5
znajdzie się w tablicy przed wartością 7
.
Pora na konkretny przykład. Powiedzmy, że mamy taką tablicę liczb:
$values = array(17, 1, 48, 97, 82, 75, 14, 11, 19, 57);
Aby ją posortować malejąco za pomocą funkcji usort()
, musimy napisać funkcję zwrotną o cechach opisanych powyżej. Powinna ona przyjmować dwa argumenty i wykonywać na nich odpowiednie działania:
function cmp($val1, $val2) {
return $val2 <=> $val1;
}
W przykładzie tym użyłem operatora PHP statek kosmiczny, który zwraca liczbę całkowitą mniejszą od 0, jeśli pierwszy argument jest mniejszy od drugiego, 0, jeśli argumenty są równe, oraz 1, jeśli pierwszy argument jest większy od drugiego.
Mamy już wszystko, co jest nam potrzebne, czyli tablicę do posortowania i funkcję zwrotną, więc możemy wykonać zaplanowane działanie. Aby przekazać funkcję zwrotną w argumencie wywołania do innej funkcji, należy podać jej nazwę jako łańcuch, bez nawiasu, np.:
usort($values, "cmp");
To wszystko, teraz tablica $values jest posortowana malejąco. Poniżej przedstawiam skrypt w całości wraz z instrukcją pozwalającą zweryfikować wynik:
function cmp($val1, $val2)
{
return $val2 <=> $val1;
}
$values = array(17, 1, 48, 97, 82, 75, 14, 11, 19, 57);
usort($values, "cmp");
var_dump($values);
Wynik:
array(10) { [0]=> int(97) [1]=> int(82) [2]=> int(75) [3]=> int(57) [4]=> int(48) [5]=> int(19) [6]=> int(17) [7]=> int(14) [8]=> int(11) [9]=> int(1) }
A teraz jeszcze raz spójrz na definicję funkcji cmp()
. Jej ciało jest tak proste, że zawiera mniej znaków niż cała „standardowa otoczka”, czyli prototyp i klamra otaczająca blok kodu.
Czy nie prościej byłoby przekazać taką instrukcję wprost w miejscu wywołania zwrotnego zamiast tworzyć malutką funkcję, która zostanie użyta tylko raz w tym jednym miejscu? Oczywiście, że tak. W tym celu można użyć funkcji anonimowej.
Funkcje anonimowe PHP
Funkcja anonimowa PHP (ang. anonymous function) to, jak nazwa wskazuje, funkcja która nie ma nazwy. Funkcje takie mają wiele różnych zastosowań, a jednym z nich jest przekazywanie ich w roli wywołania zwrotnego do innych funkcji.
Definicja funkcji anonimowej wygląda prawie tak samo, jak zwykłej, tylko jest pozbawiona nazwy, np.:
function($val1, $val2) {
return $val2 <=> $val1;
};
Zwróć uwagę na średnik po znaku zamykającym klamrę, który w tym przypadku jest obowiązkowy.
Taką funkcję można bezpośrednio przekazać jako funkcję zwrotną (już bez średnika po znaku zamykającym klamrę). Dzięki temu nasz poprzedni przykład możemy trochę uprościć:
$values = array(17, 1, 48, 97, 82, 75, 14, 11, 19, 57);
usort($values, function($val1, $val2) {return $val2 <=> $val1;});
var_dump($values);
Wynik działania tego kodu będzie identyczny z poprzednim, ale skrypt ten jest zdecydowanie bardziej zwięzły i nie zawiera już niepotrzebnej pełnej definicji funkcji z nazwą.
Funkcje anonimowe mają pewną cechę, która sprawia, że nazywa się je też domknięciami.
Domknięcia
Inna nazwa funkcji anonimowych w PHP to domknięcia (ang. closure), choć poprawnie powinno się mówić zamknięcia, ponieważ odnosi się ona do tego, że funkcje te mogą dziedziczyć (obejmować) zmienne z zakresu nadrzędnego. Spójrz na poniższy przykład:
$x = 100;
$f = function($val1, $val2) use ($x) {
echo $val1 + $val2 + $x;
};
$k(1,2);
W tym przykładzie jest parę nowych rzeczy do wyjaśnienia.
- W wierszu 3. przypisałem funkcję anonimową do zmiennej o nazwie $f. W języku PHP domknięcia można przypisywać do zmiennych (jest to zwykłe przypisanie do zmiennej, stąd średnik za zamknięciem klamry), a następnie wywoływać tak, jak wywołuje się zwykłe funkcje, tylko przy użyciu nazwy zmiennej (wiersz 7.).
Choć może się to wydawać dziwnym pomysłem, to tego rodzaju funkcje, zwane zmiennymi funkcyjnymi (ang. variable function) mogą być przydatne w pewnych sytuacjach, najczęściej w dynamicznych technikach programowania.
- Pod koniec wiersza 3. użyłem konstrukcji językowej PHP
use
, która służy do importowania zmiennych do domknięcia z zakresu nadrzędnego. Gdybym jej nie zastosował, to nie mógłbym użyć zmiennej $x w ciele domknięcia.
$k(1,2);
? Sprawdź, czy miałeś rację, wykonując ten skrypt u siebie. a teraz pokażę ci, jak jeszcze bardziej skrócić i uprościć przykład sortowania tablicy. Do tego celu użyję funkcji strzałkowej.Funkcje strzałkowe PHP
Funkcje strzałkowe PHP (ang. arrow function) są uproszczoną wersją funkcji anonimowych. Ich ogólna postać wygląda tak:
fn(argumenty) => wyrażenie;
Początek funkcji strzałkowej wyznacza słowo kluczowe fn
. W nawiasie po nim znajdują się argumenty wywołania, a po strzałce, od której wzięła się ich nazwa, można umieścić jedno wyrażenie, którego wartość zostanie zwrócona.
Inaczej mówiąc, funkcje strzałkowe są odpowiednikiem funkcji anonimowych o następującej postaci:
function(argumenty) { return wyrażenie; }
W ramach przykładu użycia funkcji strzałkowych w PHP pokażę ci, jak jeszcze bardziej uprościć wywołanie funkcji usort()
:
$values = array(17, 1, 48, 97, 82, 75, 14, 11, 19, 57);
usort($values, fn ($val1, $val2) => $val2 <=> $val1);
var_dump($values);
Jeszcze jedną ważną różnicą między funkcjami anonimowymi a funkcjami strzałkowymi jest to, że funkcje strzałkowe PHP automatycznie dziedziczą zmienne z zakresu nadrzędnego, więc w ich przypadku konstrukcja use
jest niepotrzebna. Na przykład:
$x = 700;
$k = fn($val1, $val2) => $val1 + $val2 + $x;
echo $k(1,2); // Wynik: 703
Pamiętaj, że choć funkcja strzałkowa dziedziczy zmienne z zakresu nadrzędnego, to nie staje się ich „właścicielką”, a więc nie może ich modyfikować.
Inaczej mówiąc, jakiekolwiek operacje wykonasz na zmiennej odziedziczonej z zakresu nadrzędnego, to będą one odnosiły się tylko do jej wewnętrznej kopii w funkcji strzałkowej. Poza tą funkcją zmienna nadal będzie miała pierwotną wartość, na przykład:
$x = 700;
$k = fn($val1, $val2) => $val1 + $val2 + ++$x;
echo $k(1,2); // Wynik: 704
echo $x; // Wynik: 700
Jeśli po zapoznaniu się z funkcjami anonimowymi i strzałkowymi myślisz, że widziałeś już wszystko i nic cię nie zdziwi, to jesteś w błędzie. Teraz pokażę ci, jak tworzyć funkcje, które wywołują same siebie, czyli funkcje rekurencyjne.
Funkcje rekurencyjne PHP
Funkcja rekurencyjna (ang. recursive function) to taka funkcja, która wywołuje samą siebie w celu wykonania pewnego złożonego działania kawałek po kawałku. Można ją porównać do pętli, ponieważ tak samo jak ona wykonuje pewne działania, aż zostanie spełniony określony warunek (o którym nie można zapomnieć, bo powstanie rekurencja nieskończona).
Klasycznym przykładem przy omawianiu funkcji rekurencyjnych jest prezentacja sposobu obliczania silni. Przypomnę, że silnia to działanie matematyczne polegające na mnożeniu przez siebie kolejnych liczb ciągu liczbowego. Operację tę oznacza się wykrzyknikiem, np. 5! = 5 * 4 * 3 * 2 * 1 = 120.
Algorytm obliczania silni można zaimplementować na kilka różnych sposobów. Można do tego celu użyć zarówno pętli, np. while
albo for
, jak i funkcji rekurencyjnej. Najpierw przyjrzymy się implementacji w formie pętli for
, a potem przerobimy ją na funkcję rekurencyjną.
function factorial($num) {
if ($num < 1) return "Nieprawidłowy argument.";
for ($i = 1; $num > 0; $num--) {
$i *= $num;
}
return $i;
}
Ta funkcja najpierw sprawdza, czy przekazany argument nie jest mniejszy od 1
i jeśli tak, wyświetla komunikat o błędzie.
Następnie ustawia zmienną pętlową $i na 1
, definiuje warunek działania — dopóki zmienna $num ma wartość większą od zera — oraz zmniejsza wartość zmiennej $num o 1 w każdej kolejnej iteracji.
W bloku kodu pętli for w pierwszej iteracji bierzemy wartość zmiennej $i (początkowo 1
), mnożymy ją przez wartość zmiennej $num (początkowo 5
) i wynik działania zapisujemy z powrotem w zmiennej $i.
W drugiej iteracji zmienna $i ma już wartość 5
, a zmienna $num ma wartość 4
. W wyniku ich pomnożenia otrzymujemy 20
. Wartość tę zapisujemy w zmiennej $i, po czym zmniejszamy wartość zmiennej $num do 3
i przechodzimy do trzeciej iteracji, w której…
Ten prosty algorytm za pomocą funkcji rekurencyjnej można wyrazić jeszcze zwięźlej. Spójrz na poniższy przykład:
function factorial($num) {
if ($num <= 1) return 1;
return $num * factorial($num - 1);
}
echo factorial(5); // Wynik: 120
Wywołanie factorial(5);
powoduje, że do funkcji factorial()
jako argument zostaje przekazana wartość 5
. Następne kroki działania tej funkcji są następujące:
- Wiersz 2: funkcja sprawdza, czy wartość zmiennej $num wynosi
1
lub mniej. Jeśli tak, to funkcja zwraca1
i kończy działanie. - Wiersz 3: wartość zmiennej $num (początkowo
5
) zostaje pomnożona przez wynik kolejnego wywołania funkcjifactorial()
z argumentem zmniejszonym o1
($num - 1
), czyli mającym wartość4
.W efekcie w pamięci powstaje ciąg
5 * 4
. Jako że wartość argumentu $num jeszcze nie spełnia warunku$num <= 1
, następuje kolejne wywołanie funkcjifactorial()
, w wyniku którego ciąg przybiera postać5 * 4 * 3
, potem analogicznie5 * 4 * 3 * 2
.
Rekurencja daje bardzo duże możliwości i najczęściej jest wykorzystywana w różnego rodzaju zadaniach związanych z przeglądaniem i przetwarzaniem struktur danych, takich jak na przykład tablice, szczególnie wielowymiarowe.
Na razie jednak zostawiamy temat rekurencji i przechodzimy do ostatniego już tematu tego rozdziału, czyli do zakresu dostępności zmiennych w odniesieniu do funkcji.
Funkcje a dostępność zmiennych
W rozdziale Typy danych i zmienne w PHP w sekcji Zakres dostępności zmiennych w PHP opisałem pojęcie zakresu dostępności zmiennych w PHP.
Ponieważ jednak pojęcie to ściśle wiąże się z funkcjami, jeszcze raz odnoszę się do tego tematu z paroma dodatkowymi informacjami, które przed przestudiowaniem rozdziałów o funkcjach mogłyby być mało zrozumiałe. Ze względu na to, że jest to drugie podejście do tego tematu, znane już informacje staram się przedstawiać jak najzwięźlej.
Zakres dostępności zmiennej definiuje się jako kontekst, w którym ta zmienna może być używana. W PHP zmienna może być:
- globalna,
- lokalna,
- statyczna.
Zmienne globalne PHP
Zmienna globalna (ang. global variable) to zmienna, która nie jest zdefiniowana wewnątrz jakiejkolwiek funkcji. Zmienne takie są dostępne w każdym miejscu skryptu, tylko nie w funkcjach, np.:
$x = "z zewnątrz funkcji";
function f() {
echo $x; // Błąd: zmienna $x jest tutaj niedostępna
}
Ta zasada nie dotyczy jednak funkcji strzałkowych, które wiążą zmienne z zakresu nadrzędnego i pozwalają na korzystanie z ich kopii.
Aby użyć zmiennej z zakresu globalnego wewnątrz funkcji innej niż strzałkowa, można użyć słowa kluczowego global
.
Słowo kluczowe global
Jeśli chcesz użyć zmiennej globalnej wewnątrz funkcji, to zadeklaruj ją w tej funkcji przy użyciu słowa kluczowego global
, np.:
$x = "z zewnątrz funkcji";
function f() {
global $x;
echo $x; // Dobrze: zmienna $x jest tutaj dostępna
}
Innym sposobem na użycie zmiennej globalnej wewnątrz funkcji jest skorzystanie ze specjalnej tablicy o nazwie $GLOBALS.
Tablica $GLOBALS
Tablica $GLOBALS należy do zmiennych superglobalnych, czyli takich, które są dostępne naprawdę dosłownie wszędzie, zarówno wewnątrz funkcji, jak i w każdym innym miejscu.
Jest to tablica asocjacyjna, a więc taka, w której do wartości można odnosić się za pomocą kluczy zamiast indeksów. Zawiera ona wszystkie zmienne globalne dostępne w skrypcie — zarówno wbudowane, jak i zdefiniowane przez użytkownika. Kluczami są nazwy zmiennych globalnych, które należy przekazywać jako łańcuchy. Na przykład:
$x = "z zewnątrz funkcji";
function f() {
echo $GLOBALS['x']; // Dobrze: zostanie wyświetlona wartość globalnej zmiennej $x
}
W PHP dostępnych jest kilka wbudowanych zmiennych superglobalnych podobnych do $GLOBALS. Większość z nich jest przydatna w technikach sieciowych, na przykład służą do odbierania danych wprowadzonych w formularzu przez użytkownika itd. Jeszcze nie raz do nich wrócimy w dalszych częściach tego kursu.
Zmienne lokalne PHP
Zmienna lokalna (ang. local variable) to zmienna zdefiniowana wewnątrz funkcji i dostępna tylko w niej, np.:
function f() {
$x = "test";
}
echo $x; // Błąd: zmienna $x jest dostępna tylko wewnątrz funkcji f().
Zmienne lokalne istnieją tylko przez czas wykonywania funkcji, w której są zdefiniowane, po czym zostają usunięte z pamięci… No chyba że są zmiennymi statycznymi.
Zmienne statyczne PHP
Zmienne statyczne (ang. static variable) to zmienne lokalne, które są zachowywane między wywołaniami funkcji. To znaczy, zmienne te nie są usuwane w chwili zakończenia działania funkcji, tylko nadal istnieją i mogą być wykorzystywane ponownie w kolejnych wywołaniach. Aby utworzyć zmienną statyczną, należy użyć słowa kluczowego static
:
function counter() {
static $quantity = 0;
return ++$quantity;
}
echo counter();
echo counter();
echo counter();
echo counter();
echo counter();
Wynik działania tego kodu jest następujący:
12345
Pierwsze wywołanie funkcji counter()
ustawiło wartość zmiennej statycznej $quantity na 1
i ją zwróciło. Drugie wywołanie zmieniło ją na 2
i ją zwróciło. Trzecie kolejny raz dodało 1
do poprzedniej wartości itd.
Gdyby zmienna $quantity nie była statyczna, to powyższy kod zwróciłby pięć jedynek.
Podsumowanie

- Funkcje wywołań zwrotnych to funkcje, które przekazuje się jako argument do innych funkcji.
- Funkcje anonimowe to funkcje, które nie mają nazwy i mogą być przekazywane bezpośrednio jako argument do funkcji wywołań zwrotnych.
- Funkcje anonimowe można przypisywać do zmiennych i za pomocą nazw zmiennych wywoływać jak zwykłe funkcje.
- Funkcje anonimowe są też nazywane domknięciami, ponieważ mogą obejmować zmienne z zakresu nadrzędnego.
- Funkcje strzałkowe to uproszczona wersja funkcji anonimowych, która automatycznie obejmuje zmienne z zakresu nadrzędnego.
- Funkcja rekurencyjna to taka funkcja, która wywołuje sama siebie.
- Funkcja rekurencyjna powinna zawierać instrukcję warunkową definiującą warunek zakończenia jej działania.
- W PHP zmienne mogą być lokalne, globalne lub statyczne.
Pytania
- Czym są funkcje wywołań zwrotnych?
- Czym są funkcje anonimowe?
- Czym są funkcje strzałkowe?
- Czym są zmienne funkcyjne?
- Co to jest rekurencja i czym są funkcje rekurencyjne?
- Jaki jest zakres dostępności zmiennych w PHP?
Ćwiczenia
- Za pomocą funkcji PHP
usort()
posortuj poniższą tablicę malejąco:$data = array( "Kruszynka rdzawoczułka", "Odorek zieleniak", "Turkuć podjadek", "Wydłubka oczateczka", "Psotnik zakamarnik", "Kruszynka rdzawoczułka", "Dobroczynek", "Maworek zwisły", "Wydłubka oczateczka", "Psotnik zakamarnik", "Złodziejaszek rypidełko", "Szrotówek kasztanowcowiaczek", );
Napisz trzy wersje wywołania funkcji: z funkcją nazwaną, z funkcją anonimową i z funkcją strzałkową jako funkcją wywołania zwrotnego.
- Napisz funkcję rekurencyjną, która wyświetla n pierwszych liczb naturalnych.
- Napisz wersje, które wyświetlają ciąg od największej do najmniejszej i od najmniejszej do największej liczby.
- Napisz wersje, które uwzględniają w ciągu zero i nie uwzględniają w ciągu zera.
- Napisz funkcję rekurencyjną, która sumuje n pierwszych liczb naturalnych.
- Napisz implementację algorytmu, który generuje ciąg Fibonacciego przy użyciu funkcji rekurencyjnej.