PHPDevs

Projekt obiektowy z Composerem i Twigiem? Czytaj!

Funkcje > Lekcja 3

Argumenty funkcji

Argumenty to dane, które chcemy przesłać do wnętrza naszej funkcji. Definiujemy je w momencie deklaracji funkcji, w nawiasach, oddzielając przecinkami. Przypisujemy im zmienne, których następnie możemy użyć wewnątrz funkcji. Z kolei przy wywołaniu funkcji musimy podać ich wartości.

<?php
function showNumbers($number1, $number2)
{
    echo $number1 . ' ' . $number2;
}
showNumbers(4, 3); // result: 4 3
showNumbers(23, 7); // result: 23 7

Teraz może zacząłeś zastanawiać się po co nam te argumenty i dobrze, to właściwe pytanie. Rzecz jest w tym, że zmienne zadeklarowane poza funkcją nie są w niej widoczne. Podobnie zmienne stworzone w funkcji - nie są widoczne poza nią. Jeśli więc chcemy przekazać coś do funkcji, musimy użyć argumentów.

Jeśli jeszcze coś jest niejasne to z pewnością zrozumiesz po obejrzeniu przykładu:

<?php
$number1 = 5;
function showNumbers()
{
    var_dump($number1); // result: Notice: Undefined variable: number1
    $number2 = 8;
}

showNumbers();
var_dump($number2); // result: Notice: Undefined variable: number2

Uwaga

Zmienne globalne, nazywane też globalami, umożliwiają inne obejście tego problemu. Jest to jednak bardzo zły sposób. Zagrożeń w związku ze zmiennymi globalnymi jest wiele - tracimy wtedy pełną kontrolę i przejrzystość tego, co się dzieje.

<?php
$number1 = 5;
function showNumbers()
{
    global $number1, $number2;
    var_dump($number1); // result: int(5)
    $number2 = 8;
}

showNumbers();
var_dump($number2); // result: int(8)

Przykład pokazuję, abyś wiedział co to jest, gdy spotkasz taki zapis. Podkreślam jednak raz jeszcze, że stanowczo odradzam używanie globali, chyba że naprawdę musisz i jesteś świadomy tego, co robisz i jakie może to przynieść konsekwencje.

Wracamy do naszego celu i kodu z poprzedniej lekcji. Poprawmy go tak, aby zmienne do działań można było przekazać z zewnątrz, przez argumenty.

<?php

$operation = '+';
$number1 = 8;
$number2 = 4;

function operationAddition($number1, $number2)
{
    echo $number1 + $number2;
}

function operationSubtraction($number1, $number2)
{
    echo $number1 - $number2;
}

function operationMultiplication($number1, $number2)
{
    echo $number1 * $number2;
}

function operationDivision($number1, $number2)
{
    echo $number1 / $number2;
}

switch ($operation) {
    case '+':
        operationAddition($number1, $number2);
        break;
    case '-':
        operationSubtraction($number1, $number2);
        break;
    case '*':
        operationMultiplication($number1, $number2);
        break;
    case '/':
        operationDivision($number1, $number2);
        break;
}

Dla jasności: zmienne, których wartości podstawiamy oraz zmienne tworzone wewnątrz funkcji jako argumenty, nie muszą nazywać się tak samo - w powyższym przykładzie to czysty przypadek.

Wartość domyślna

Przy wywołaniu funkcji musimy podać wartości dla wszystkich zadeklarowanych argumentów. Jeżeli tego nie zrobimy, zostanie zwrócony błąd. Istnieje jednak możliwość zdefiniowania wartości domyślnych dla każdego (lub tylko części) z nich - wtedy rzecz jasna błędów w przypadku nieprzekazania wartości nie będzie.

Domyślną wartość możesz skojarzyć z przypisywaniem wartości zmiennych, gdyż definiujemy ją tak jak przypisanie, przy użyciu =. Istnieje tylko jedna istotna uwaga: robimy to od końca (czyli od ostatniego, od prawej strony). Dlaczego? Ustawiamy od końca argumenty jako opcjonalne do wywołania funkcji, dzięki czemu w momencie ich podania interpreter wie, do których zmiennych je przekazać. Gdybyśmy mogli sobie ustawić taką wartość na przykład dla pierwszego argumentu, skąd interpreter miałby wiedzieć, który argument chcemy pominąć i użyć domyślnej wartości?

<?php
$number = 3;
function showNumbers($number1, $number2 = 1)
{
    echo $number1 . ' ' . $number2;
}
showNumbers($number); // result: 3 1
<?php
$number = 3;
function showNumbers($number1 = 1, $number2) // próbujemy powiedzieć, aby pierwszy argument miał wartość domyślną
{
    echo $number1 . ' ' . $number2;
}
showNumbers($number); // result: Fatal error: Too few arguments to function showNumbers()
// podaliśmy tylko pierwszy argument, a drugiego nie, pomimo tego, że jest obowiązkowy

Definiuj więc argumenty tak, aby te opcjonalne znajdowały się możliwie na końcu.

Typy argumentów

Od php >=7 istnieje możliwość określenia typów argumentów, jakie mają zostać przesłane (dla ścisłości: wcześniej można było określać tylko klasę gdy argumentem był obiekt, ale o tym gdzieś dalej). Robimy to poprzez dodanie typu przed nazwą zmiennej w deklaracji funkcji. Dla przykładu tak:

<?php
function showNumbers(int $number1, int $number2)
{
    echo $number1 . ' ' . $number2;
}
showNumbers(4, 3); // result: 4 3
showNumbers(23, 7); // result: 23 7

Co się stanie gdy podamy wartość innego typu, niż zadeklarowany? Pewnie spodziewasz się, że wyrzucony zostanie błąd, ale nie do końca tak jest. Zależy to od sytuacji. Gdy zadeklarujesz, że zmienna $number ma być typu int, a prześlesz tekst, to owszem, działanie kodu zakończy się błędem. Jednak gdy do zadeklarowanego inta spróbujesz przesłać na przykład wartość typu float to kod zadziała bez zarzutu - interpreter sam przekonwertuje liczbę zmiennoprzecinkową na całkowitą ("obetnie" końcówkę).

<?php
function showNumber(int $number)
{
    echo $number;
}
showNumber(5); // result: 5
showNumber(5.7); // result: 5
showNumber('PHPDevs'); // result: Uncaught TypeError: Argument 1 passed to showNumber() must be of the type integer, string given

Gdybyśmy jednak chcieli precyzyjnie określić, że zmienna ma być takiego typu jaki zadeklarowaliśmy, możemy to wymusić. Wystarczy na samym początku pliku użyć declare i ustawić strict_types na 1. Kontrola typowania może pomóc w uniknięciu różnego rodzaju błędów, podobnie jak pokazywałem to na przykład przy operatorach i porównywaniu z typem, więc generalnie polecam jej używać.

<?php
declare(strict_types=1);
function showNumber(int $number)
{
    echo $number;
}
showNumber(5); // result: 5
showNumber(5.7); // result: Uncaught TypeError: Argument 1 passed to showNumber() must be of the type integer, float given
showNumber('PHPDevs'); // result: Uncaught TypeError: Argument 1 passed to showNumber() must be of the type integer, string given

W php >=7.1 istnieje także możliwość dopuszczenia wartości null nawet wtedy, gdy mamy określony typ. Jest to możliwe poprzez dopisanie znaku zapytania ? przed danym typem.

<?php
function showNumber(?int $number)
{
    echo $number;
}
showNumber(8); // result: 8
showNumber(null); // result: nothing

Warto zauważyć, że ?int $number i int $number = null to coś innego. Pierwszy przypadek nie posiada wartości domyślnej, więc null będzie dopuszczalny, ale i tak musi zostać przekazany jako argument, nie można go pominąć tak jak w zapisie drugim.

Wiele typów dla jednego argumentu?

Określenie kilku dopuszczalnych typów dla jednego argumentu funkcji jest niemożliwe. W takiej sytuacji przede wszystkim powinieneś zastanowić się czy na pewno dany argument powinien być raz jedną wartością, a raz całkowicie inną - może problem jest w samym pomyśle. Jeśli nadal tak, to musisz pozostawić argument bez określonego typu. Możesz jedynie wspomóc się specjalnym komentarzem phpDoc (wspomniałem o nim słowo na początku) z elementem @param. Dzięki temu chociażby inny programista będzie wiedział co jest tam oczekiwane bez dalszej analizy kodu.

<?php
/**
 * @param int|string $value
 */
function show($value)
{
    // ...
}

Z nową wiedzą przeróbmy znów nasz kalkulator. Użycie wartości domyślnej raczej się nie sprawdzi, więc to tutaj odpuścimy (przećwiczysz w zadaniu domowym), ale już deklarację typu jak najbardziej możemy sobie dodać. Jako iż może zaistnieć potrzeba operowania na liczbach z przecinkiem, pozwólmy na wartości typu float.

<?php

function operationAddition(float $number1, float $number2) { /* ... */ }

function operationSubtraction(float $number1, float $number2) { /* ... */ }

function operationMultiplication(float $number1, float $number2) { /* ... */ }

function operationDivision(float $number1, float $number2) { /* ... */ }

// ...

Ćwiczenia

  1. Przygotuj funkcję showName wyświetlającą imię przekazane do niej jako argument.

    Przykładowe rozwiązanie

  2. Napisz funkcję prepareCart sprawdzającą, czy klient może złożyć zamówienie o cenie $price, gdy stan jego konta wynosi $money. Zadeklaruj odpowiednie typy dla argumentów. Trzeci argument $promotion powinien być typu logicznego (prawda lub fałsz). Wartością domyślną w przypadku jego nieprzekazania powinno być false. W przypadku gdy promocja ma obowiązywać (true) od łącznej ceny zamówienia ($price) należy odjąć 10. Jeśli klient może dokonać zamówienia wyświetl komunikat "OK".
    <?php
    $price = 22.90;
    $money = 50;
    // your code here
    

    Przykładowe rozwiązanie

Poprzednia lekcja Następna lekcja

Udostępnij

  • Facebook
  • Twitter

Komentarze