Używanie domyślnych argumentów w funkcji

177

Nie mam pojęcia co do wartości domyślnych funkcji PHP. Powiedzmy, że mam taką funkcję:

function foo($blah, $x = "some value", $y = "some other value") {
    // code here!
}

A jeśli chcę użyć domyślnego argumentu dla $ x i ustawić inny argument dla $ y?

Eksperymentowałem na różne sposoby i po prostu coraz bardziej się pogubiłem. Na przykład wypróbowałem te dwa:

foo("blah", null, "test");
foo("blah", "", "test");

Ale oba nie dają prawidłowego domyślnego argumentu dla $ x. Próbowałem również ustawić to według nazwy zmiennej.

foo("blah", $x, $y = "test");   

W pełni spodziewałem się, że coś takiego zadziała. Ale to wcale nie działa tak, jak się spodziewałem. Wygląda na to, że bez względu na to, co zrobię, i tak będę musiał wpisywać domyślne argumenty, za każdym razem, gdy wywołam funkcję. I chyba brakuje mi czegoś oczywistego.

renoza
źródło
68
Prawdopodobnie niczego nie brakuje, programiści PHP prawdopodobnie to przegapili.
Matti Virkkunen,
To interesujące pytanie. Nigdy nie miałem takiej potrzeby, jak powiedziałeś, ponieważ używam domyślnych argumentów tylko do rozszerzania funkcji odziedziczonych przez innych programistów, gdy nie jestem pewien, co zależy od oryginału. Ciekawi mnie odpowiedź.
Kai Qing
3
Jedno z najlepszych pytań, jakie widziałem od jakiegoś czasu! Czy próbowałeś niczego nie przekazać? foo("blah", , "test");?
Madara's Ghost
2
Zasada jest taka, że ​​domyślne argumenty mogą występować tylko po argumentach. Oznacza to, że możesz mieć foo („bla”) i x, y są domyślne lub foo („Blah”, „xyz”), a y jest wartością domyślną.
Mouse Food
1
Spodziewane zachowanie. Zobacz sekcję „Domyślne wartości argumentów” w: php.net/manual/en/functions.arguments.php - wartość pusta i pusty ciąg są akceptowane jako rzeczywiste argumenty funkcji
jmdavalos

Odpowiedzi:

164

Proponuję zmienić deklarację funkcji w następujący sposób, abyś mógł robić, co chcesz:

function foo($blah, $x = null, $y = null) {
    if (null === $x) {
        $x = "some value";
    }

    if (null === $y) {
        $y = "some other value";
    }

    code here!

}

W ten sposób możesz wykonać wywołanie takie jak foo('blah', null, 'non-default y value');i sprawić, by działało tak, jak chcesz, gdzie drugi parametr $xnadal ma swoją wartość domyślną.

W przypadku tej metody przekazanie wartości null oznacza, że ​​chcesz mieć wartość domyślną dla jednego parametru, gdy chcesz zastąpić wartość domyślną parametru, który występuje po niej.

Jak stwierdzono w innych odpowiedziach,

parametry domyślne działają tylko jako ostatnie argumenty funkcji. Jeśli chcesz zadeklarować wartości domyślne w definicji funkcji, nie ma możliwości pominięcia jednego parametru i przesłonięcia następnego po nim.

Jeśli mam metodę, która akceptuje różną liczbę parametrów i parametry różnych typów, często deklaruję funkcję podobną do odpowiedzi, którą pokazał Ryan P.

Oto kolejny przykład (to nie odpowiada na twoje pytanie, ale miejmy nadzieję, że jest pouczające:

public function __construct($params = null)
{
    if ($params instanceof SOMETHING) {
        // single parameter, of object type SOMETHING
    } else if (is_string($params)) {
        // single argument given as string
    } else if (is_array($params)) {
        // params could be an array of properties like array('x' => 'x1', 'y' => 'y1')
    } else if (func_num_args() == 3) {
        $args = func_get_args();

        // 3 parameters passed
    } else if (func_num_args() == 5) {
        $args = func_get_args();
        // 5 parameters passed
    } else {
        throw new InvalidArgumentException("Could not figure out parameters!");
    }
}
drew010
źródło
Dlaczego nie $ x = isset ($ x)? $ x: 'wartość domyślna'; ?
zloctb
@zloctb to jest czysty i czytelny znak zerowy. Wygląda na to, że jest to preferencja programisty. Albo $ x = isset ($ x)? $ x: 'wartość domyślna'; lub $ x = is_null ($ x)? 'default value': $ x; działałoby równie dobrze, jak to rozwiązanie. Jest to preferowany wybór dotyczący czytelności i konserwacji.
deweloperów
9
Tylko po to, aby wyjaśnić przyszłym czytelnikom. Pierwsze rozwiązanie oczywiście uniemożliwia przypisanie rzeczywistej nullwartości do parametru, ponieważ za każdym razem przypisywałby on wartość domyślną. Nawet jeśli miałeś inną specjalną wartość, kiedy faktycznie chcesz mieć wartość null (np. Gdy $ x == "use_null" sprawi, że $ x = null), nie będziesz w stanie przypisać takiej specjalnej wartości jak dosłowna wartość parametr (w tym przypadku ciąg „use_null”). Więc po prostu upewnij się, że używasz unikalnego, nigdy nie pożądanego „klucza”, gdy chcesz użyć wartości domyślnej (i chcesz nullbyć poprawną opcją)
DiegoDD
37

Opcjonalne argumenty działają tylko na końcu wywołania funkcji. Nie ma sposobu, aby określić wartość $ y w twojej funkcji bez określenia $ x. Niektóre języki obsługują to poprzez nazwane parametry (na przykład VB / C #), ale nie PHP.

Możesz to emulować, jeśli używasz tablicy asocjacyjnej dla parametrów zamiast argumentów - tj

function foo(array $args = array()) {
    $x = !isset($args['x']) ? 'default x value' : $args['x'];
    $y = !isset($args['y']) ? 'default y value' : $args['y'];

    ...
}

Następnie wywołaj funkcję w ten sposób:

foo(array('y' => 'my value'));
Ryan P
źródło
1
Twój warunek naprawdę powinien być isset($args['x']), ponieważ obecnie zastąpiłby pusty ciąg, pustą tablicę lub falsewartość domyślną.
Tim Cooper
@TimCooper lol Poszedłem zmienić na podstawie twojego pierwszego komentarza, że ​​powinno to być '=== null', poszedłem i zmieniłem moją odpowiedź na użycie isset, a potem wróciłem, aby zobaczyć, jak zmieniłeś również swój komentarz. : D
Ryan P
Ach, cholera! Wcale mi się to nie podoba ... No cóż, nie możesz mieć wszystkiego. Przypuszczam, że będę musiał wtedy dokładniej przemyśleć kolejność moich domyślnych argumentów. Dzięki!
renoza
Niestety nie będzie można przypisać wartości null za pomocą tej odpowiedzi. To jest lepsza opcja: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Frank Forte
20

Jest to rzeczywiście możliwe:

foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test');

Czy chcesz to zrobić, to inna historia :)


AKTUALIZACJA:

Powody, dla których warto unikać tego rozwiązania to:

  • jest (prawdopodobnie) brzydki
  • ma oczywisty narzut.
  • jak inne odpowiedzi dowodzą, istnieją alternatywy

Ale w rzeczywistości może się przydać w sytuacjach, gdy:

  • nie chcesz / nie możesz zmienić oryginalnej funkcji.

  • możesz zmienić funkcję, ale:

    • używanie null(lub odpowiednik) nie jest opcją (zobacz komentarz DiegoDD )
    • nie chcesz iść ani ze skojarzeniem, ani z func_num_args()
    • Twoje życie zależy od uratowania kilku LOC

Jeśli chodzi o wydajność, bardzo prosty test pokazuje, że użycie interfejsu API Reflection do uzyskania parametrów domyślnych powoduje, że wywołanie funkcji jest 25 razy wolniejsze , podczas gdy nadal zajmuje to mniej niż jedną mikrosekundę . Powinieneś wiedzieć, czy możesz z tym żyć.

Oczywiście, jeśli zamierzasz używać go w pętli, powinieneś wcześniej uzyskać wartość domyślną.

David
źródło
1
Jaki jest powód, dla którego nie chcesz tego robić? Wydaje się całkiem przydatne.
Bryan,
10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}
zloctb
źródło
2
Czy coś mi brakuje? To wydaje się w ogóle nie odpowiadać na pytanie, ale ma 7 głosów za. Może trochę wyjaśnienia pomogłoby?
Sean the Bean
3
@SeantheBean: prawda, wyjaśnienie byłoby lepsze, ale chodzi o to: ponieważ PHP nie ma nazwanych argumentów, użyj tablicy asocjacyjnej jako jej pojedynczego parametru.
MestreLion,
1
Bardziej pomocne jest skorzystanie z przykładu podanego w pytaniu, np.$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte
4

Jedyny sposób, w jaki mogę to zrobić, to pominięcie parametru. Jedynym sposobem na pominięcie parametru jest zmiana kolejności listy parametrów tak, aby ta, którą chcesz pominąć, znajdowała się po parametrach, które MUSISZ ustawić. Na przykład:

function foo($blah, $y = "some other value", $x = "some value")

Następnie możesz zadzwonić do foo na przykład:

foo("blah", "test");

Spowoduje to:

$blah = "blah";
$y = "test";
$x = "some value";
Wes Crow
źródło
3

Niedawno miałem ten problem i znalazłem to pytanie i odpowiedzi. Chociaż powyższe pytania działają, problem polega na tym, że nie pokazują one domyślnych wartości dla IDE, które je obsługują (np. PHPStorm).

wprowadź opis obrazu tutaj

jeśli użyjesz null, nie będziesz wiedział, jaka byłaby wartość, jeśli pozostawisz to pole puste.

Preferowanym przeze mnie rozwiązaniem jest również umieszczenie wartości domyślnej w definicji funkcji:

protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
{
    if ($limit===null) $limit =1000;
    if ($sort===null) $sort = [];
    if ($offset===null) $offset = 0;
    ...

Jedyną różnicą jest to, że muszę się upewnić, że są takie same - ale myślę, że to niewielka cena za dodatkową przejrzystość.

Yehosef
źródło
Wewnętrzne użycie samego ReflectionFunction oszczędziłoby cenę!
TematDelta
2

Nie możesz tego zrobić bezpośrednio, ale małe manipulowanie kodem umożliwia emulację.

function foo($blah, $x = false, $y = false) {
  if (!$x) $x = "some value";
  if (!$y) $y = "some other value";

  // code
}
kba
źródło
6
To by zadziałało, ale uważam, że użycie nullw tym przypadku jest koncepcyjnie lepsze niż false.
TRiG
Dodanie do komentarza TriG falsejest bardziej ryzykownym wyborem niż null, ponieważ php nie jest surowym językiem. !$xbędzie prawdziwe dla kilku różnych wejść, co może zaskoczyć programistę: 0, ''. Podczas gdy z null, możesz użyć !issetjako ściślejszego testu.
ToolmakerSteve
1
<?php
function info($name="George",$age=18) {
echo "$name is $age years old.<br>";
}
info();     // prints default values(number of values = 2)
info("Nick");   // changes first default argument from George to Nick
info("Mark",17);    // changes both default arguments' values

?>
Odyn
źródło
1

Moje 2 centy z zerowym operatorem koalescencji ?? (od PHP 7)

function foo($blah, $x = null, $y = null) {
    $varX = $x ?? 'Default value X';
    $varY = $y ?? 'Default value Y';
    // ...
}

Możesz sprawdzić więcej przykładów na mojej repl.it

SandroMarques
źródło
0

Dzieje się tak, gdy obiekt jest lepszy - ponieważ możesz ustawić obiekt tak, aby trzymał x i y, ustawić wartości domyślne itp.

Podejście z tablicą jest bliskie utworzenia obiektu (w rzeczywistości obiekt to zestaw parametrów i funkcji, które będą działać na obiekcie, a funkcja pobierająca tablicę będzie działać na niektórych parametrach)

Z pewnością zawsze możesz zrobić kilka sztuczek, aby ustawić wartość null lub coś podobnego jako domyślnego

SergeS
źródło
0

Możesz również sprawdzić, czy masz pusty ciąg jako argument, aby móc wywołać:

foo('blah', "", 'non-default y value', null);

Poniżej funkcji:

function foo($blah, $x = null, $y = null, $z = null) {
    if (null === $x || "" === $x) {
        $x = "some value";
    }

    if (null === $y || "" === $y) {
        $y = "some other value";
    }

    if (null === $z || "" === $z) {
        $z = "some other value";
    }

    code here!

}

To nie ma znaczenia, jeżeli wypełni nulllub "", będzie wciąż ten sam rezultat.

Mustafa Ekici
źródło
0

Przekaż do funkcji tablicę zamiast poszczególnych parametrów i użyj operatora łączącego wartość null (PHP 7+).

Poniżej przekazuję tablicę z 2 elementami. Wewnątrz funkcji sprawdzam, czy ustawiona jest wartość item1, jeśli nie jest przypisana domyślna przechowalnia.

$args = ['item2' => 'item2',
        'item3' => 'value3'];

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }
zrozumiałem
źródło
Możesz używać $args['item1'] = $args['item1']??'default value'; w PHP 7+. jeśli $ args ['item1'] ma wartość null, to default valuestring zostanie przypisany do $ args ['item1']
Sadee