Funkcje PHP — podstawy

W języku PHP wiele standardowych czynności wykonuje się przez wywoływanie funkcji. To ułatwia pracę i pozwala utrzymać spójność kodu źródłowego skryptów. Choć podstawowa biblioteka języka PHP zawiera ponad 1000 funkcji, to dodatkowo każdy może tworzyć własne funkcje. i w tym rozdziale właśnie nauczysz się tworzyć własne funkcje PHP.

Czym jest funkcja PHP? Jest to blok kodu, który można porównać do maszyny przetwórczej. Wprowadza się do niego surowce, czyli argumenty wywołania, a on odpowiednio je przetwarza (realizuje pewien algorytm) i zwraca gotowy produkt (często w postaci wartości zwrotnej).

W tym rozdziale poznasz odpowiedzi między innymi na następujące pytania:

  • Czym są funkcje PHP?
  • Jak zdefiniować funkcję w PHP?
  • Po co definiować funkcje?
  • Czym różni się parametr funkcji od argumentu funkcji?
  • Czym jest prototyp funkcji?
  • Jakie są sposoby przekazywania argumentów do funkcji PHP?

Funkcje PHP – wprowadzenie

Zanim pokażę ci, jak definiować funkcje PHP, spróbuję wyjaśnić, po co w ogóle to robić. Zresztą nie jestem w stanie lepiej wyrazić sensu tworzenia funkcji niż zrobił to twórca języka C++ Bjarne Stroustrup w swojej klasycznej powieści kryminalnej z elementami horroru pt. Język C++. Kompendium wiedzy:

Dlaczego funkcje

Istnieje długa i okryta złą sławą tradycja pisania bardzo długich funkcji składających się z setek wierszy kodu. Raz nawet natknąłem się na ręcznie napisaną funkcję o długości ponad 32 768 wierszy kodu. Programiści piszący takie funkcje chyba nie rozumieją, do czego te konstrukcje służą — do dzielenia skomplikowanych obliczeń na łatwiejsze do ogarnięcia moduły o określonych nazwach. Kod źródłowy powinien być zrozumiały, bo inaczej program będzie trudny w utrzymaniu.

Bjarne Stroustrup, Język C++. Kompendium wiedzy. Tłum. Łukasz Piwko. Wyd. Helion 2013

Warto zapamiętać radę tego jednego z najbardziej cenionych programistów na świecie: funkcje służą do dzielenia skomplikowanych zadań na mniejsze moduły, nad którymi łatwiej jest zapanować.

To pierwszy z powodów, ale są też inne. Po drugie funkcje ułatwiają wielokrotne wykorzystanie fragmentów kodu (ang. code reuse). Jeśli w budowanym systemie często wykonujemy jakąś czynność, na przykład łączymy się z bazą danych, to warto zdefiniować ją w postaci funkcji, którą można wywoływać w dowolnym miejscu.

I po trzecie dzięki funkcjom kod staje się bardziej przejrzysty i łatwiejszy do zrozumienia, co można docenić zwłaszcza po pewnym czasie, kiedy już się zapomni, co miało się w danym miejscu na myśli. Jeśli określoną czynność zdefiniowaliśmy w postaci funkcji z dobrze dobraną nazwą, to po prostu wywołamy tę funkcję i nie będziemy musieli rozgryzać szczegółów jej działania.

A czy zdarzają się sytuacje, w których lepiej odstąpić od zamiaru tworzenia funkcji? Tak. Przede wszystkim nie należy wyważać już otwartych drzwi. To znaczy, jeśli istnieje standardowa funkcja, która wykonuje określone zadanie, np. funkcja zwracająca pierwiastek kwadratowy z podanej liczby, to nie należy pisać własnej, tylko powinno się używać tej gotowej.

Wyjątek od tej reguły stanowią ćwiczenia, w ramach których wszystko jest dozwolone. W internecie można znaleźć przykładowe implementacje chyba wszystkich możliwych algorytmów w najróżniejszych językach programowania. Jeśli jednak chcesz się nauczyć programować, to możesz, a wręcz powinieneś, próbować pisać własne implementacje. W tym nie ma nic złego.

Definiowanie funkcji PHP

Funkcja PHP, lub bardziej precyzyjnie definicja funkcji PHP, składa się z dwóch głównych części: deklaracji funkcji (ang. function declaration) i bloku kodu zawierającego jej implementację, zwanego potocznie ciałem funkcji (ang. function body). Na przykład:


function larger(int $value1, int $value2): int | string {
  if ($value1 > $value2) return $value1;
  elseif ($value1 < $value2) return $value2;
  else return "Wartości są równe";
}

Ta funkcja nazywa się larger. Ma dwa parametry typu int: $value1$value2. Zwraca wartość typu int lub string, która jest większą z wartości parametrów, lub napis "Wartości są równe", jeśli parametry mają takie same wartości.

Nie każda funkcja musi zwracać wartość. W języku PHP równie często jak funkcje zwracające wartości, spotyka się funkcje, które ich nie zwracają, tylko wykonują inną czynność, na przykład:


function larger2(int $value1, int $value2) {
  if ($value1 > $value2) echo $value1;
  elseif ($value1 < $value2) echo $value2;
  else echo "Wartości są równe";
}

Ta funkcja nie ma określonego typu zwrotnego, ponieważ nie zwraca żadnej wartości. W zamian od razu wyświetla ona wynik na wyjściu.

W ciele funkcji może znajdować się dowolny prawidłowy kod PHP, ale nie tylko. Ciało funkcji może też w ogóle nie zawierać kodu PHP, a jedynie kod HTML, np.:


<?php function welcome() { ?>
<p>Witaj!</p>
<?php } ?>

Wywołanie tej funkcji spowoduje wyświetlenie na stronie napisu Witaj! w elemencie HTML p.

Teraz przyjrzymy się dokładnie deklaracji funkcji.

Deklaracja funkcji

Deklaracja funkcji PHP (ang. function declaration), nazywana też prototypem funkcji (ang. function prototype), sygnaturą funkcji (ang. function signature) lub nagłówkiem funkcji (ang. function header) zawiera po kolei następujące elementy:

  1. Słowo kluczowe function
  2. Nazwa — powinna jak najlepiej odzwierciedlać, co dana funkcja robi
  3. Lista rozdzielanych przecinkami parametrów w nawiasie, opcjonalnie z określonymi typami — może być pusta lub może zawierać więcej elementów niż w przykładzie
  4. Opcjonalny typ zwrotny po dwukropku za nawiasem i przed znakiem otwarcia klamry.

Na przykład, to jest deklaracja (prototyp) funkcji PHP o nazwie larger():


function larger(int $value1, int $value2): int | string

Poniższy schemat przedstawia jej poszczególne elementy.

Funkcje PHP - prototyp funkcji PHP
Prototyp funkcji PHP

W niektórych językach programowania, np. C++, można tylko zadeklarować funkcję, ale w PHP funkcje należy od razu definiować, czyli deklaracji zawsze musi towarzyszyć implementacja w klamrze (chociaż może być pusta).

Nazwą funkcji może być dowolny prawidłowy identyfikator PHP: pierwszym znakiem musi być litera lub znak podkreślenia, po których może znajdować się dowolna liczba liter, cyfr i znaków podkreślenia. Należy pamiętać, że w nazwach funkcji wielkość liter nie jest rozróżniana.

Ważną kwestię stanowią też typy parametrów, które nie są obowiązkowe, ale pomagają w unikaniu różnych błędów związanych ze stosowaniem typów danych. W języku PHP określanie typów danych nazywa się podpowiadaniem typów danych.

W PHP można podpowiadać interpreterowi typy parametrów i wartości zwrotnych funkcji

Podpowiedzi typów danych

PHP jest językiem o luźnej kontroli typów danych, a więc nie ma w nim obowiązku bezpośredniego określania typów parametrów funkcji. Na przykład definicję funkcji larger moglibyśmy napisać tak:


function larger($value1, $value2) {
  if ($value1 > $value2) return $value1;
  elseif ($value1 < $value2) return $value2;
  else return "Wartości są równe";
}

Wyniki zwracane przez taką funkcję mogą być jednak zaskakujące i nie do końca zgodne z zamiarem programisty, np.:


larger("a", 10); // Wynik: a

Jeśli zależy ci na ściślejszej kontroli typów, np. jeśli chcesz, aby funkcja larger() przyjmowała tylko argumenty typu int, a w przypadku innych zgłaszała błąd, to możesz zastosować tzw. podpowiedzi typów (ang. type hints).

Podpowiedzi te to po prostu deklaracje typów, które umieszcza się przed nazwami parametrów w nagłówku funkcji. Dzięki nim PHP będzie pilnować jak policjant, czy nie doszło do złamania przepisów dotyczących typów danych, np.:


function larger(int $value1, int $value2)

Przy takim prototypie wywołanie funkcji larger("a", 10); zakończy się zgłoszeniem przez interpreter PHP następującego błędu:

Fatal error: Uncaught TypeError: larger(): Argument #1 ($value1) must be of type int, string given

Komunikat ten informuje, że wystąpił błąd krytyczny — pierwszy argument powinien być typu int, a jest typu string.

Określony typ mogą mieć nie tylko parametry, ale także wartość zwrotna funkcji PHP. Więcej na ten temat dowiesz się w sekcji Zwracanie wartości przez funkcję.

A teraz chciałbym zwrócić twoją uwagę na jeszcze jedną kwestię związaną z typami parametrów i wartości zwrotnych funkcji, czyli na tak zwane typy dopuszczające null.

Typy dopuszczające null

Jeśli istnieje możliwość, że do funkcji, którą piszesz, zostanie przekazana wartość null, albo że funkcja ta zwróci wartość null, to możesz to zaznaczyć za pomocą znaku ?, który należy umieścić przed typem danego parametru lub przed typem zwrotnym, np.:


function toWords(?int $val): ?string {
  if ($val == 1) return "jeden";
  if ($val == 2) return "dwa";
  else return null;
}

Jeśli jako argument wywołania do tej funkcji przekażesz null albo jakąkolwiek inną wartość niż reprezentacja liczby 1 lub 2, to funkcja ta zwróci null.

Stosowanie składni ze znakiem ? nie jest obowiązkowe w takich przypadkach, jak opisany powyżej. Zamiast niej można też używać unii typów albo typu mixed, np.:


function toWords(null | int $val): mixed

Podpowiedzi typów, choć przydatne, nie włączają ścisłej kontroli typów, tylko podpowiadają, jak należy traktować opatrzone nimi wartości.

To znaczy, że PHP będzie starać się każdą otrzymaną wartość dostosować do wymagań i zgłosi błąd tylko wtedy, gdy nie znajdzie sposobu, aby dokonać konwersji. Jeśli zależy ci na ścisłej kontroli typów, to możesz ją włączyć za pomocą specjalnej dyrektywy.

Ścisła kontrola typów w PHP

Napisałem, że jeśli skorzystasz z opisanych powyżej podpowiedzi typów PHP, to interpreter będzie starał się w miarę możliwości dopasować wartości do określonych warunków. Weźmy na przykład taką funkcję:


function multiplication(int $val1, int $val2) {
  echo $val1 * $val2;
}

multiplication(2.5, 4); // Wynik: 8

Poprawny wynik mnożenia 2,5 × 4 wynosi 10, ale funkcja zwróciła 8, ponieważ przyjmuje ona tylko wartości typu int, a więc odrzuciła część ułamkową z pierwszego argumentu wywołania.

Jeśli wolisz, aby w takiej sytuacji PHP zgłaszał błąd krytyczny zamiast wykonywać niejawną konwersję, to możesz włączyć tryb ścisłej kontroli typów. Służy do tego dyrektywa declare(strict_types=1);, którą należy umieścić na początku pliku, np.:


declare(strict_types=1);

function multiplication(int $val1, int $val2) {
  echo $val1 * $val2;
}

multiplication(2.5, 4);

Wynik:

Fatal error: Uncaught TypeError: multiplication(): Argument #1 ($val1) must be of type int, float given

Skoro wiesz już, jak zdefiniować funkcję oraz umiesz zapanować nad typami jej parametrów i wartości zwrotnej, pora przyjrzeć się metodom wywoływania funkcji.

Kiedy już zdefiniujesz funkcję PHP, możesz ją wywołać

Wywoływanie funkcji PHP

Aby wywołać funkcję PHP, należy wpisać jej nazwę i ewentualnie w nawiasie podać listę argumentów wywołania rozdzielonych przecinkami, np.:


larger(5, 4);

To wywołanie funkcji zwróci wartość 5, ale się o tym nie dowiemy, ponieważ nigdzie nie przypisujemy wartości zwrotnej ani jej nie wyświetlamy. Możemy więc zmodyfikować nasze wywołanie w następujący sposób:


echo larger(5, 4);

albo


$result = larger(5, 4);
// Tu mogą być jakieś działania na wartości zmiennej $result

W pierwszym przypadku wynik zostanie wyświetlony na stronie, a w drugim — zostanie zapisany w zmiennej o nazwie $result.

Zakres dostępności funkcji

Funkcje w PHP mają globalny zakres dostępności. To znaczy, że są dostępne wszędzie w skrypcie. Nawet funkcja zdefiniowana w innej funkcji jest dostępna także poza funkcją nadrzędną, ale dopiero po wywołaniu tej funkcji nadrzędnej, na przykład:


function testA()
{
  function testB()
  {
    echo "Jestem funkcją zdefiniowaną wewnątrz funkcji testA.";
  }
}

Aby móc wywoływać funkcję testB, najpierw musimy wywołać funkcję testA:


testA();
// Różne instrukcje...
testB();

W takim przypadku funkcja testB zostanie normalnie wywołana i wyświetli swój napis. Gdyby natomiast przed wywołaniem funkcji testB nie było wywołania funkcji testA, wystąpiłby błąd:

Fatal error: Uncaught Error: Call to undefined function testB()

To były podstawowe wiadomości na temat tego, jak definiuje się funkcje w PHP. Wiesz już jak wygląda definicja i deklaracja funkcji, wiesz że funkcja może mieć parametry, ale nie są one obowiązkowe oraz wiesz, że funkcja może zwracać wartość lub wykonywać określone działania, nie zwracając wyniku operacji do wywołującego.

Teraz przejdziemy do szczegółów. Zaczniemy od dokładniejszego przyjrzenia się parametrom i argumentom funkcji.

Parametry i argumenty funkcji PHP

Listy parametrów funkcji PHP mogą być dość różnorodne. Mogą zawierać parametry domyślne i obowiązkowe, mogą przyjmować nieokreśloną z góry liczbę argumentów wywołania oraz mogą pozwalać na przekazywanie argumentów przez wartość lub referencję.

Zaraz ci o tym wszystkim opowiem, ale najpierw, na rozgrzewkę, pokażę ci, jak można zwiększyć przejrzystość deklaracji i wywołań funkcji, które mają dużą liczbę parametrów.

Powiedzmy na przykład, że chcemy napisać funkcję obliczającą średnią arytmetyczną 7 wartości:


function meanValue($firstValue, $secondValue, $thirdValue, $fourthValue, $fifthValue, $sixthValue, $seventhValue) {
  // Obliczanie średniej
}

Taka lista parametrów jest raczej mało przejrzysta. Dlatego wielu programistów mogłoby zdecydować się na zapisanie jej w taki sposób:


function meanValue(
    $firstValue,
    $secondValue,
    $thirdValue,
    $fourthValue,
    $fifthValue,
    $sixthValue,
    $seventhValue,
) {
  // Obliczanie średniej
}

Zwróć uwagę na przecinek po ostatnim parametrze — w poprzedniej wersji go nie było. Przecinek ten jest opcjonalny i ma znaczenie wyłącznie estetyczne.

W wersji jednowierszowej przecinek na końcu nie poprawia wyglądu deklaracji, a nawet ją pogarsza. Natomiast w wersji „pionowej” jego obecność wydaje się wręcz naturalna. To czy będziesz go używać w tym miejscu, to kwestia twojego gustu (choć pamiętaj, że możliwość ta jest dostępna od PHP 8.0.0).

To samo dotyczy listy argumentów wywołania funkcji. Ją też można zakończyć opcjonalnym przecinkiem, który ma tylko znaczenie estetyczne, na przykład:


meanValue(
    20,
    10,
    2011,
    17,
    345,
    1996,
    2006,
);

Kiedy już nacieszysz oko pięknem tych przykładów z dodatkowym przecinkiem bez znaczenia dla działania funkcji, to przejdź do następnej sekcji, w której pokażę ci, jak definiować obowiązkowe i opcjonalne parametry funkcji PHP.

Parametry obowiązkowe

Jeśli chodzi o definiowanie parametrów obowiązkowych funkcji PHP, to tak naprawdę nie trzeba już nic wyjaśniać, ponieważ już umiesz to robić. Wszystkie pokazane do tej pory przykłady zawierały parametry obowiązkowe.

Krótko mówiąc, aby zdefiniować obowiązkowy parametr funkcji PHP, wystarczy go zadeklarować. Weźmy na przykład zdefiniowaną wcześniej funkcję larger(), która w deklaracji ma dwa argumenty:


function larger($value1, $value2)…

Jeśli wywołamy tę funkcję z jednym argumentem, to spowodujemy błąd krytyczny PHP:


larger(4);

Wynik:

Fatal error: Uncaught ArgumentCountError: Too few arguments to function larger(), 1 passed in C:\xampp\htdocs\index.php on line 11 and exactly 2 expected

Zostałem poinformowany, że spowodowałem błąd krytyczny, ponieważ przekazałem za mało argumentów. Przekazałem jeden, podczas gdy funkcja spodziewała się dokładnie dwóch.

Aby parametr przestał być obowiązkowy, należy nadać mu wartość domyślną.

Parametry opcjonalne

Aby nadać wartość domyślną parametrowi funkcji PHP, należy mu ją przypisać dokładnie w taki sam sposób, jak przypisuje się wartość zwykłej zmiennej:


function larger($value1, $value2 = 7)

Domyślna wartość parametru musi być wyrażeniem stałym, czyli takim, którego wartość nie zmienia się w czasie wykonywania skryptu. To znaczy, że jako wartości domyślnej nie można zastosować na przykład zmiennej ani wywołania funkcji.

Wartością parametru domyślnego może być wartość skalarna, tablica, wartość null oraz (od PHP 8.1.0) obiekt.

Liczba parametrów opcjonalnych jest nieograniczona, ale wszystkie powinny znajdować się za parametrami obowiązkowymi.

Gdy lista argumentów wywołania funkcji jest krótsza niż jej lista parametrów w deklaracji, to wartości parametrów domyślnych są stosowane w miejsce brakujących argumentów w kolejności ich występowania na liście.

Weźmy na przykład poniższą funkcję. Przyjmuje ona jako argumenty trzy łańcuchy, które łączy w jeden łańcuch i wyświetla na stronie:


function concat($str1, $str2 = "ma", $str3 = "kota") {
  echo "$str1 $str2 $str3";
}

Tę funkcję możemy wywołać z jednym argumentem, z dwoma argumentami lub z trzema argumentami, np.:


concat("Ala"); // Wynik: Ala ma kota
concat("Ala", "nie ma"); // Wynik: Ala nie ma kota
concat("Ala", "ma", "kapibarę"); // Wynik: Ala ma kapibarę

Taki pozycyjny system przypisywania wartości argumentom ma jednak pewne ograniczenie. Gdybyśmy na przykład chcieli przekazać tylko trzeci argument, a drugi chcielibyśmy pozostawić z wartością domyślną, to nie mielibyśmy takiej możliwości.

W takiej sytuacji pozostawałoby nam tylko przekazać tej funkcji wszystkie trzy argumenty wywołania. Było tak do PHP 8.0.0, ponieważ w tej wersji języka wprowadzono argumenty nazwane.

Argumenty nazwane

Argumenty nazwane (ang. named arguments) to rozszerzenie składni parametrów pozycyjnych PHP. Umożliwiają przekazywanie do funkcji argumentów przez wskazanie nazw parametrów, do których się odnoszą, zamiast polegać na ich przypisywaniu do odpowiednich parametrów na podstawie pozycji na liście argumentów.

Aby przekazać argument nazwany, należy wpisać nazwę parametru, do którego się on odnosi, dwukropek i wartość, na przykład:


concat(str3: "kapibary", str2: "nie ma", str1: "Bogdan");

Wynik:

Bogdan nie ma kapibary

Kolejność argumentów nazwanych nie ma znaczenia, ale wszystkie powinny znajdować się za ewentualnymi argumentami pozycyjnymi, razem z którymi mogą występować w jednym wywołaniu funkcji. Na przykład:


concat("Bogdan", str3: "kapibary", str2: "nie ma");

W tym wywołaniu pierwszy argument jest pozycyjny, a dwa ostatnie są nazwane. Przeniesienie argumentu "Bogdan" gdziekolwiek indziej byłoby błędem, na przykład:


concat(str3: "kapibary", "Bogdan", str2: "nie ma"); // To jest nieprawidłowe wywołanie

Wynik:

Fatal error: Cannot use positional argument after named argument…

Jeśli wszystkie argumenty są nazwane, to można je dowolnie przestawiać.

Wiesz już, jak tworzyć parametry obowiązkowe i opcjonalne, wiesz jak używać argumentów nazwanych, czyli wiesz już całkiem sporo, ale jednak jeszcze nie wszystko.

Przypomnij sobie wcześniejszą przykładową funkcję, która przyjmowała aż siedem argumentów wywołania i obliczała ich średnią wartość arytmetyczną. Dlaczego akurat siedem, a nie na przykład osiem? No właśnie. Wybrałem tę liczbę arbitralnie, bez jakiegokolwiek logicznego uzasadnienia.

Żeby nasza funkcja obliczająca średnią arytmetyczną była naprawdę przydatna, powinniśmy umożliwić przekazywanie do niej dowolnej liczby argumentów wywołania. Teraz pokażę ci, jak to zrobić.

Zmienna liczba argumentów

Funkcje w PHP nie muszą przyjmować ściśle ustalonej liczby argumentów wywołania. W języku tym, tak jak w wielu innych językach programowania, można tworzyć funkcje wariadyczne (ang. variadic function), czyli takie, które przyjmują zmienną liczbę argumentów.

Aby funkcja przyjmowała zmienną liczbę argumentów, należy przed nazwą parametru w jej deklaracji umieścić trzy kropki (…), np.:


function meanValue(...$numbers) {
  // Obliczanie średniej
}

Ewentualnie można też zadeklarować typ parametru wariadycznego, np.:


function meanValue(int ...$numbers) {
  // Obliczanie średniej
}

Do funkcji wariadycznej, takiej jak powyższa funkcja meanValue(), można przekazać dowolną liczbę argumentów wywołania, które zostaną zapisane w tablicy o podanej nazwie (tu: $numbers).

Zobaczmy, jak to wygląda w praktyce. Poniżej znajduje się „kompletna” definicja funkcji meanValue():


function meanValue(...$numbers) {
  $sum = 0;
  $length = 0;
  foreach ($numbers as $n) {
    $sum += $n;
    $length++;
  }
  $mean = $sum / $length;
  return $mean;
}

W funkcji tej najpierw za pomocą pętli foreach sumuję wszystkie wartości przekazane jako argumenty wywołania i zapisane w tablicy $numbers (przy okazji obliczam długość tej tablicy za pomocą instrukcji $length++;), a następnie sumę tę dzielę przez liczbę elementów, aby otrzymać średnią arytmetyczną. Poniżej znajduje się przykładowe wywołanie tej funkcji:


echo meanValue(1, 12, 34, 17.5); // Wynik: 16.125

Funkcja może mieć tylko jeden parametr wariadyczny, a jeśli oprócz niego ma też „zwykłe” parametry, to parametr wariadyczny musi znajdować się na ostatnim miejscu listy parametrów, np.:


function test($x, $y, $z, ...$numbers)…

W takim przypadku w tablicy argumentów wariadycznych zostaną umieszczone tylko te wartości, które nie zostaną przyporządkowane wcześniejszym parametrom pozycyjnym.

Rozwijanie list argumentów

Warto też wiedzieć, że operator ... może być używany także w wywołaniach funkcji, w których działa odwrotnie niż w definicjach funkcji. Zamiast tworzyć tablicę z wartości argumentów, rozwija on tablicę wartości do postaci listy argumentów, na przykład:


$values = [1, 2, 3, 4];

echo meanValue(...$values); // Wynik: 2.5

To bardzo wygodny sposób na przekazywanie tablic wartości jako argumentów do funkcji. Można go stosować zarówno w wywołaniach funkcji wariadycznych, jak i „zwykłych”. W drugim przypadku należy tylko dopilnować, aby tablica zawierała tyle elementów, ile argumentów przyjmuje funkcja.

Argumenty do funkcji w PHP można przekazywać przez wartość lub przez referencję

Sposoby przekazywania argumentów do funkcji

Argumenty wywołania funkcji w PHP mogą być przekazywane na dwa sposoby: przez wartość i przez referencję.

Przekazywanie przez wartość jest domyślnym sposobem, z którego korzystaliśmy przez cały czas do tej pory. To znaczy, że już wiesz, na czym to polega i jak się to robi, choć może nie masz jeszcze pełnej świadomości, jakie są tego konsekwencje. Teraz się dowiesz.

Przekazywanie argumentów przez wartość

Przekazywanie argumentów przez wartość równie dobrze można by było nazwać przekazywaniem przez kopię, ponieważ każdy argument przekazywany do funkcji w ten sposób jest przez nią kopiowany i funkcja pracuje na tej kopii. Spójrz na poniższy przykład:


function power2($liczba) {
  $liczba *= $liczba;
  return $liczba;
}

$x = 5;

echo 'power2($x) == ' . power2($x);

echo '
$x == ' . $x;

Wynik:

power2($x) == 25
$x == 5

Ten wynik jest poprawny i powinien być dla ciebie oczywisty. Funkcja pobrała argument wywołania, czyli wartość zmiennej $x, wykonała jego kopię, zrobiła z nią co trzeba i zwróciła wynik swoich obliczeń.

W efekcie wartość zmiennej $x po wywołaniu funkcji power2 pozostała niezmieniona.

Natomiast gdyby argument do funkcji power2 był przekazywany przez referencję, to zmienna $x zostałaby potraktowana odrobinę inaczej. Zobaczmy.

Przekazywanie argumentów przez referencję

Przekazywanie argumentów przez referencję można nazwać przekazywaniem przez oryginał, ponieważ w tym przypadku funkcja nie tworzy kopii argumentu, tylko pracuje bezpośrednio na nim. To znaczy, że zmiana wartości tego argumentu będzie trwała i widoczna także poza funkcją.

Aby argumenty do funkcji były przekazywane przez referencję, w definicji funkcji należy ich nazwy poprzedzić znakiem & (może być ze spacją lub bez), na przykład:


function power2(&$liczba) {
  $liczba *= $liczba;
  return $liczba;
}

$x = 5;

echo 'power2($x) == ' . power2($x);

echo '
$x == ' . $x;

Ta drobna zmiana w pierwszym wierszu spowoduje, że otrzymamy taki wynik:


power2($x) == 25
$x == 25

W tym przypadku zmiana wartości zmiennej $x „przetrwała” wywołanie funkcji i jest widoczna także poza nią.

W przypadku funkcji wariadycznych znak referencji & należy umieścić przed operatorem ..., np.: function x(&...$args)….

Bardzo dużo opowiedziałem już o przyjmowaniu wartości przez funkcje na różne sposoby. Pora więc dokładniej przyjrzeć się odbieraniu wyników ich działania.

Zwracanie wartości przez funkcję

Zwracanie wartości przez funkcję jest opcjonalne, ale jeśli się na to zdecydujesz, to służy do tego konstrukcja językowa return. Jej wykonanie powoduje natychmiastowe przerwanie wykonywania funkcji i zwrócenie sterowania do konstrukcji, która ją wywołała.

Funkcja może zwracać wartość dowolnego typu, np.:


function power2($liczba) {
  $liczba *= $liczba;
  return $liczba;
}

var_dump(power2(5)); // Wynik: int(25)
var_dump(power2(5.5)); // Wynik: float(30.25)

Ta funkcja zwraca wartość typu, który odpowiada typowi wartości jej argumentu. W razie potrzeby można też określić typ zwrotny funkcji i wówczas będzie ona zawsze zwracać wartość tego typu.

W PHP typ zwrotny funkcji określa się w jej definicji bezpośrednio za nawiasem zawierającym listę parametrów, po dwukropku, np.:


function power2($liczba): int {
  $liczba *= $liczba;
  return $liczba;
}

var_dump(power2(5.5)); // Wynik: int(30)

Mimo że argument i „rzeczywisty” wynik tej operacji są liczbami zmiennoprzecinkowymi, funkcja zwróciła wartość typu int, po prostu odrzucając część ułamkową.

Funkcja może mieć więcej niż jeden typ zwrotny. Jeśli tak, to typy zwrotne funkcji należy rozdzielić znakiem pionowej kreski, np.:


function power2($liczba): int | float

Możliwość ta została wprowadzona w PHP 8.0.0 i taki typ zwrotny nazywa się unią typów (ang. union type).

Unia typów to połączenie przynajmniej dwóch typów. Dodatkowo w tej samej wersji PHP zdefiniowano jeszcze typ mieszany (ang. mixed type), który jest równoznaczny z następującą unią typów:


object|resource|array|string|int|float|bool|null

Typu mixed można używać w definicjach funkcji w taki sam sposób, jak innych typów.

Jeśli funkcja nie zwraca żadnej wartości, to powinno się zadeklarować jej typ zwrotny void, a w ciele należy umieścić pustą instrukcję return lub w ogóle jej nie umieszczać, np.:


function power2($liczba): void {
  echo $liczba *= $liczba;
  return; // Ta instrukcja jest opcjonalna, równie dobrze można ją usunąć
}

Jednak w PHP nawet jeśli funkcja „nie zwraca” żadnej wartości, to tak naprawdę zwraca null.

Zwrot wartości przez referencję

W PHP istnieje także możliwość utworzenia funkcji pozwalającej na zwracanie wartości przez referencję. W tym celu wystarczy dodać operator referencji (&) przed nazwą funkcji w jej definicji, np.:


function &getBigData() {
 static $bigDataSet = array(1,2,3);

 $bigDataSet[2]++;

 return $bigDataSet;
}

W funkcji tej utworzyłem statyczną tablicę o nazwie $bigDataSet. Tablica statyczna to taka, która „przeżywa” między wywołaniami funkcji. Czyli jeśli wywołam daną funkcję raz a potem znowu, to w tym drugim wywołaniu zostanie użyta ta sama tablica, co w pierwszym (o zmiennych statycznych dowiesz się więcej w dziale poświęconym programowaniu obiektowemu w PHP), a nie jej nowa kopia.

Następnie zwiększam wartość ostatniego elementu tablicy $bigDataSet o jeden. Gdybym nie korzystał ze zwrotu przez referencję, to w każdym wywołaniu funkcji getBigData(); otrzymywałbym nową kopię tworzonej w niej tablicy.

Jeśli natomiast skorzystam ze zwrotu wartości funkcji przez referencję, to cały czas będę używał tej samej zmiennej tablicowej, w efekcie czego każde kolejne wywołanie będzie powodować, że ostatni element tablicy będzie zwiększany o jeden.

I teraz jeszcze jedna ważna uwaga: aby odebrać wartość zwrotną funkcji przez referencję, dodatkowo należy użyć operatora & w instrukcji przypisania zwracanej wartości do zmiennej, np.:


$results = &getBigData();

Zmienna $results zawiera referencję do statycznej tablicy $bigDataSet funkcji getBigData(). Poniżej znajduje się dowód:


$results = &getBigData();
var_dump($results); // Wynik: array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(4) }
getBigData();
getBigData();
getBigData();
var_dump($results); // array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(7) }

Gdyby nie operator referencji w instrukcji przypisania wartości do zmiennej, to zarówno w pierwszym, jak i drugim wywołaniu funkcji var_dump() otrzymalibyśmy taki sam wynik — z wartością 4 w ostatnim elemencie tablicy.

Na koniec jeszcze jedna uwaga: dodanie operatora referencji w definicji funkcji nie oznacza, że automatycznie będziesz otrzymywać referencję jako wartość zwrotną. Operator ten daje tylko taką możliwość, z której można skorzystać przez zastosowanie dodatkowo tego samego operatora w instrukcji przypisania wartości zwrotnej do zmiennej.

Jeśli go nie dodasz w tym drugim miejscu, to otrzymasz zwykły zwrot z funkcji przez wartość.

Na zakończenie tego rozdziału opowiem ci jeszcze, jak czytać prototypy funkcji w dokumentacji PHP. Wprawdzie doskonale już wiesz, czym jest prototyp funkcji i co oznaczają jego poszczególne elementy, ale mimo tego myślę, że nie zaszkodzi uporządkować tej wiedzy, aby wszystko było całkowicie jasne.

Jak czytać prototypy funkcji w dokumentacji PHP

W dokumentacji PHP można znaleźć zwięzły opis każdej funkcji tego języka. Jego najważniejszą częścią jest prototyp funkcji, z którego można wyczytać wszystkie najistotniejsze informacje.

Prototypy prezentowane w dokumentacji powinny być doskonale zrozumiałe dla każdego, kto umie definiować własne funkcje, ponieważ wyglądają one dokładnie tak, jak prototypy funkcji definiowanych przez użytkownika. Na przykład:


array_change_key_case(array $array, int $case = CASE_LOWER): array

To jest prototyp funkcji PHP o nazwie array_change_key_case (zobacz opis funkcji array_change_key_case w dokumentacji PHP).

Funkcja ta przyjmuje dwa argumenty wywołania (czyli ma dwa parametry). Pierwszy (o nazwie $array) jest typu array i jest obowiązkowy. Drugi (o nazwie $case) jest typu int i jest opcjonalny, ponieważ ma przypisaną wartość domyślną CASE_LOWER. Funkcja ta zwraca tablicę (array) w wyniku.

Jeśli informacje zawarte w prototypie są niewystarczające, to na stronie dokumentacji znajdziesz także dodatkowe objaśnienia i przykłady, a często także cenne komentarze innych programistów.

W tym rozdziale dowiedziałeś się bardzo dużo na temat definiowania własnych funkcji w PHP, dzięki czemu masz już solidne podstawy do posługiwania się tymi konstrukcjami programistycznymi.

W następnym rozdziale opisuję bardziej zaawansowane techniki definiowania funkcji. Na razie możesz przeczytać go tylko pobieżnie, aby zdobyć ogólne rozeznanie, a potem możesz do niego wracać, gdy poczujesz taką potrzebę.

Podsumowanie

  1. W języku PHP jest dostępnych wiele funkcji służących do wykonywania różnych standardowych działań.
  2. Programista PHP może definiować także własne funkcje.
  3. Funkcje definiuje się po to, aby dzielić kod programu na logiczne, łatwe do wielokrotnego wykorzystania, moduły.
  4. Nie powinno się definiować własnych funkcji do wykonywania działań, które wykonują funkcje wbudowane.
  5. Definicja funkcji składa się z deklaracji (prototypu) i bloku kodu (ciała).
  6. Funkcje są dostępne w zakresie globalnym.
  7. Parametry funkcji to zmienne zadeklarowane lub zdefiniowane w jej definicji.
  8. Argumenty funkcji to wartości przekazywane jej w wywołaniu.
  9. Funkcja może mieć parametry obowiązkowe i opcjonalne.
  10. Argumenty do funkcji można przekazywać pozycyjnie lub przez podanie nazw parametrów, do których się odnoszą.
  11. Funkcja może przyjmować zmienną liczbę argumentów i wtedy nazywa się funkcją wariadyczną.
  12. Argumenty do funkcji można przekazywać przez wartość lub przez referencję.
  13. Funkcja może zwracać wartość.


Pytania

  1. Do czego służą funkcje PHP?
  2. W jakim celu definiuje się własne funkcje PHP?
  3. Kiedy nie powinno się definiować własnych funkcji?
  4. Z czego składa się definicja funkcji PHP?
  5. Jaki jest zakres dostępności funkcji PHP?
  6. Czym są parametry funkcji?
  7. Czym są argumenty funkcji?
  8. Jakie parametry może mieć funkcja PHP?
  9. Jaki znasz sposoby przekazywania argumentów do funkcji PHP?
  10. Ile argumentów może przyjmować funkcja PHP?
  11. W jaki sposób funkcja PHP może zwracać wartość?


Ćwiczenia

  1. Co robi ta funkcja:
    
    function funkcja(int $value) {
      return $value << 1;
    }
  2. Zdefiniuj funkcję, która przyjmuje dwa argumenty typu int i zwraca wartość typu float, która jest ilorazem tych dwóch argumentów. W ciele funkcji sprawdzaj, czy nie powstaje dzielenie przez 0 i, jeśli tak, zamiast wykonać działanie, wyświetl odpowiedni komunikat.
  3. Napisz funkcję, która sprawdza, czy podana liczba jest parzysta, czy nieparzysta.
  4. Napisz funkcję, która oblicza pole powierzchni koła o podanej długości promienia.
  5. Napisz funkcję, która oblicza pole powierzchni trapezu o podanej długości podstawy i wysokości.
  6. Napisz funkcję, która przyjmuje 1, 2 lub 3 argumenty liczbowe.
    1. Jeśli podany zostanie 1 argument, to oblicza pole powierzchni kwadratu o podanej długości boku.
    2. Jeśli podane zostaną 2 argumenty, to oblicza pole powierzchni prostokąta o podanych długościach boków.
    3. Jeśli podane zostaną trzy argumenty, to oblicza pole powierzchni trójkąta. (Podpowiedź: skorzystaj ze wzoru Herona).
    4. Jeśli podane zostaną 4 argumenty, to wyświetla napis: "Za dużo argumentów".
  7. Napisz funkcję, która przyjmuje liczbę całkowitą nie większą niż 20 i drukuje wszystkie liczby całkowite do podanej wartości, np. dla 5 funkcja powinna drukować 1, 2, 3, 4, 5.
  8. Napisz funkcję, która przyjmuje wartość określającą, ile procent z klasówki zdobył uczeń, i zwracającą jego ocenę na tej podstawie. Zastosuj dowolne progi procentowe, np. do 50% -> 1, od 51 do 60% -> 2 itd.
  9. Napisz funkcję, która jako argument przyjmuje tablicę liczb i zwraca największą z nich.

Podobał Ci się ten artykuł?

Oceń go!

Średnia 5 / 5. Liczba głosów: 1

Jeszcze nikt nie głosował. Wyprzedź innych i zagłosuj.

Skoro spodobał Ci się ten artykuł...

Poleć go znajomym!

Ojej :( Powiedz nam, co powinniśmy poprawić!

Podoba Ci się ta strona?

Pomóż nam się rozwijać, wykupując płatne konto. Dzięki temu będziemy mogli tworzyć dla Ciebie jeszcze więcej ciekawych treści, a Ty pozbędziesz się reklam.

Subskrybuj
Powiadom o
guest

0 Komentarze
Najstarsze
Najnowsze Najwięcej głosów
Opinie w linii
Zobacz wszystkie komentarze
0
Chętnie poznam Twoje przemyślenia, skomentuj.x