Jak pominąć opcjonalne argumenty w wywołaniu funkcji?

96

OK, zupełnie zapomniałem, jak pomijać argumenty w PHP.

Powiedzmy, że mam:

function getData($name, $limit = '50', $page = '1') {
    ...
}

Jak nazwałbym tę funkcję, aby środkowy parametr miał wartość domyślną (tj. „50”)?

getData('some name', '', '23');

Czy powyższe byłyby prawidłowe? Nie wydaje mi się, żeby to działało.

Chuck Le Butt
źródło
1
@ Chuck za to, czego dokładnie szukasz? Jak obejście lub klasa do zaimplementowania tego lub ...?
Rizier123
@ Rizier123 Proszę o sprawdzenie, czy obecna wersja PHP obsługuje pomijanie argumentów (podobnie jak inne języki). Aktualne odpowiedzi mogą być nieaktualne. Z opisu zlecenia: "Aktualne odpowiedzi mają pięć lat. Czy obecna wersja PHP coś zmienia?"
Chuck Le Butt
1
@ Chuck Wtedy prawdopodobnie odpowiedź będzie taka: nic się nie zmieniło; bez obejścia / kodu nie uzyskasz swojej funkcjonalności.
Rizier123
Wszystkie odpowiedzi tutaj wydają się zakładać, że masz kontrolę nad wywoływaną funkcją. Jeśli ta funkcja jest częścią biblioteki lub struktury, nie masz innego wyjścia, jak tylko określić coś dla argumentu 2, jeśli potrzebujesz argumentu 3. Jedyną opcją jest przeszukanie źródła funkcji i skopiowanie wartości domyślnej.
Snapey,
Nie można go pominąć, ale można przekazać domyślny argument za pomocą ReflectionFunction. Opublikowałem tę odpowiedź w podobnym pytaniu.
David

Odpowiedzi:

56

Twój post jest poprawny.

Niestety, jeśli chcesz użyć opcjonalnego parametru na samym końcu listy parametrów, musisz określić wszystko, aż do ostatniego parametru. Generalnie, jeśli chcesz mieszać i dopasowywać, podajesz im domyślne wartości ''lub nulli nie używaj ich wewnątrz funkcji, jeśli mają taką wartość domyślną.

zombat
źródło
1
Czy możesz podać przykład?
Jestem sobą
2
@iamme zrobić $limit = is_numeric($limit) ? $limit : '50';na początku getData funkcji
Kamafeather
40

Nie ma innego sposobu na „pominięcie” argumentu niż określenie domyślnego, takiego jak falselub null.

Ponieważ w PHP brakuje trochę cukru składniowego, często zobaczysz coś takiego:

checkbox_field(array(
    'name' => 'some name',
    ....
));

Który, jak elokwentnie powiedziano w komentarzach, używa tablic do emulacji nazwanych argumentów.

Daje to najwyższą elastyczność, ale w niektórych przypadkach może nie być potrzebne. Przynajmniej możesz przenieść wszystko, co według ciebie nie jest oczekiwane przez większość czasu na koniec listy argumentów.

Paolo Bergantino
źródło
2
Zidentyfikowałbym „coś takiego” jako „używanie tablic do emulacji nazwanych argumentów”, FWIW. :)
chaos
3
Pomimo większej elastyczności musisz pamiętać parametry, które powinieneś / możesz ustawić (ponieważ nie ma ich w podpisie). Ostatecznie może to nie być tak przydatne, jak się wydaje, ale oczywiście zależy to od kontekstu.
Felix Kling
Jakie byłyby więc najlepsze praktyki w takim scenariuszu?
Adam Grant
39

Nie, w ten sposób nie można pominąć argumentów. Możesz pominąć przekazywanie argumentów tylko wtedy, gdy znajdują się one na końcu listy parametrów.

Pojawiła się oficjalna propozycja: https://wiki.php.net/rfc/skipparams , która została odrzucona. Strona propozycji zawiera linki do innych pytań SO na ten temat.

Cristik
źródło
W takim razie w jaki sposób funkcje PHP mają opcjonalne więcej / mniej parametrów?
Jestem sobą
2
PHP obsługuje domyślne wartości parametrów. Ale te parametry muszą znajdować się na końcu listy.
Cristik
1
Przykłady można znaleźć w dokumentacji PHP
Cristik,
2
Targanie. Wkurza mnie. Jeśli nie podoba ci się funkcja, o którą prosi ktoś inny, po prostu jej nie używaj. Internet jest czasem najgorszy.
Lee Saxon,
@LeeSaxon problemem jest czytelność i przenośność. 1) Jeśli ktoś zdecyduje się pominąć parametry, a ktoś podczas debugowania kodu pomija ten fakt, pojawia się wielkie zamieszanie. 2) nowy kod nie mógłby działać na starszych wersjach bez poważniejszej przeróbki, a szanse na błędy byłyby bardzo wysokie.
Mark Walsh
6

Nic się nie zmieniło, jeśli chodzi o możliwość pominięcia argumentów opcjonalnych, jednak dla poprawnej składni i możliwości określenia NULL dla argumentów, które chcę pominąć, oto jak bym to zrobił:

define('DEFAULT_DATA_LIMIT', '50');
define('DEFAULT_DATA_PAGE', '1');

/**
 * getData
 * get a page of data 
 *
 * Parameters:
 *     name - (required) the name of data to obtain
 *     limit - (optional) send NULL to get the default limit: 50
 *     page - (optional) send NULL to get the default page: 1
 * Returns:
 *     a page of data as an array
 */

function getData($name, $limit = NULL, $page = NULL) {
    $limit = ($limit===NULL) ? DEFAULT_DATA_LIMIT : $limit;
    $page = ($page===NULL) ? DEFAULT_DATA_PAGE : $page;
    ...
}

Można to wywołać w ten sposób: getData('some name',NULL,'23');a każdy wywołujący funkcję w przyszłości nie musi za każdym razem pamiętać wartości domyślnych lub zadeklarowanej dla nich stałej.

Ibrahim Lawal
źródło
1
Prawdopodobnie chciałbyś ścisłego porównania ($ limit === null)
roelleor
4

Prosta odpowiedź brzmi: nie . Ale po co pomijać, gdy ponowne ułożenie argumentów pozwala to osiągnąć?

Twój to „ Nieprawidłowe użycie domyślnych argumentów funkcji ” i nie będzie działać tak, jak tego oczekujesz.

Dodatkowa uwaga z dokumentacji PHP:

W przypadku korzystania z argumentów domyślnych wszystkie wartości domyślne powinny znajdować się po prawej stronie argumentów innych niż domyślne; w przeciwnym razie rzeczy nie będą działać zgodnie z oczekiwaniami.

Rozważ następujące:

function getData($name, $limit = '50', $page = '1') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '', '23');   // won't work as expected

Wynik będzie:

"Select * FROM books WHERE name = some name AND page = 23 limit"

Prawidłowe użycie domyślnych argumentów funkcji powinno wyglądać następująco:

function getData($name, $page = '1', $limit = '50') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '23');  // works as expected

Wynik będzie:

"Select * FROM books WHERE name = some name AND page = 23 limit 50"

Umieszczenie wartości domyślnej po prawej stronie po wartościach innych niż domyślne zapewnia, że ​​zawsze przywróci domyślną wartość tej zmiennej, jeśli nie została zdefiniowana / podana. Oto link do odniesienia i skąd pochodzą te przykłady.

Edycja: ustawienie tego, nullco sugerują inni, może działać i jest inną alternatywą, ale może nie odpowiadać temu, co chcesz. Zawsze ustawi wartość domyślną na null, jeśli nie jest zdefiniowana.

Nelson Owalo
źródło
To najlepsza odpowiedź, ponieważ zawiera jasno zilustrowane wyjaśnienia składniowe! Dziękuję Ci!
Christian,
2

Dla każdego pominiętego parametru (musisz) przejść z parametrem domyślnym, aby być po bezpiecznej stronie.

(Ustalenie wartości null, gdzie domyślnym parametrem jest `` '' lub podobny lub odwrotnie, spowoduje kłopoty ...)

Frank Nocke
źródło
2

Jak wspomniano powyżej, nie będzie można pominąć parametrów. Napisałem tę odpowiedź, aby zapewnić dodatek, który był zbyt duży, aby umieścić go w komentarzu.

@Frank Nocke proponuje wywołanie funkcji z jej domyślnymi parametrami, czyli na przykład posiadanie

function a($b=0, $c=NULL, $d=''){ //...

powinieneś użyć

$var = a(0, NULL, 'ddd'); 

co funkcjonalnie będzie takie samo, jak pominięcie pierwszych dwóch ( $bi $c) parametrów.

Nie jest jasne, które z nich są wartościami domyślnymi ( 0wpisano, aby zapewnić wartość domyślną, czy jest to ważne?).

Istnieje również niebezpieczeństwo, że problem z wartościami domyślnymi jest powiązany z funkcją zewnętrzną (lub wbudowaną), gdy wartości domyślne mogą być zmieniane przez autora funkcji (lub metody). Więc jeśli nie zmieniasz swojego wywołania w programie, możesz nieumyślnie zmienić jego zachowanie.

Pewnym obejściem może być zdefiniowanie pewnych stałych globalnych, na przykład DEFAULT_A_B„domyślnej wartości parametru B funkcji A” i parametrów „pomiń” w ten sposób:

$var = a(DEFAULT_A_B, DEFAULT_A_C, 'ddd');

W przypadku klas łatwiej i bardziej elegancko jest zdefiniować stałe klasowe, ponieważ są one częścią zasięgu globalnego, np.

class MyObjectClass {
  const DEFAULT_A_B = 0;

  function a($b = self::DEFAULT_A_B){
    // method body
  }
} 
$obj = new MyObjectClass();
$var = $obj->a(MyObjectClass::DEFAULT_A_B); //etc.

Zauważ, że ta domyślna stała jest definiowana dokładnie raz w całym kodzie (nie ma wartości nawet w deklaracji metody), więc w przypadku pewnych nieoczekiwanych zmian, zawsze będziesz podawać funkcję / metodę z poprawną wartością domyślną.

Klarowność tego rozwiązania jest oczywiście lepsze niż dostarczanie surowych wartości domyślnych (jak NULL, 0itd.), Które nie mówią nic do czytnika.

(Zgadzam się, że dzwonienie podoba $var = a(,,'ddd');byłoby najlepszą opcją)

Voitcus
źródło
2

Nie możesz pominąć argumentów, ale możesz użyć parametrów tablicowych i musisz zdefiniować tylko 1 parametr, który jest tablicą parametrów.

function myfunction($array_param)
{
    echo $array_param['name'];
    echo $array_param['age'];
    .............
}

Możesz dodać tyle parametrów, ile potrzebujesz, nie musisz ich definiować. Kiedy wywołujesz funkcję, umieszczasz parametry w ten sposób:

myfunction(array("name" => "Bob","age" => "18", .........));
Vlad Isoc
źródło
0

Cóż, jak wszyscy już powiedzieli, że to, czego chcesz, nie będzie możliwe w PHP bez dodania jakichkolwiek linii kodu w funkcji.

Ale możesz umieścić ten fragment kodu na górze funkcji, aby uzyskać swoją funkcjonalność:

foreach((new ReflectionFunction(debug_backtrace()[0]["function"]))->getParameters() as $param) {
    if(empty(${$param->getName()}) && $param->isOptional())
        ${$param->getName()} = $param->getDefaultValue();
}

Więc w zasadzie debug_backtrace()otrzymuję nazwę funkcji, w której ten kod jest umieszczony, aby następnie utworzyć nowy ReflectionFunctionobiekt i zapętlić wszystkie argumenty funkcji.

W pętli po prostu sprawdzam, czy argumentem funkcji jest empty()AND argument jest „opcjonalny” (czyli ma wartość domyślną). Jeśli tak, po prostu przypisuję wartość domyślną do argumentu.

Demo

Rizier123
źródło
0

Ustaw limit na null

function getData($name, $limit = null, $page = '1') {
    ...
}

i wywołaj tę funkcję

getData('some name', null, '23');

jeśli chcesz ustawić limit, możesz podać jako argument

getData('some name', 50, '23');
Hari Kumar
źródło
0

Zgodnie z wcześniejszymi zaleceniami nic się nie zmieniło. Uważaj jednak, zbyt wiele parametrów (szczególnie opcjonalnych) jest silnym wskaźnikiem zapachu kodu.

Być może Twoja funkcja robi za dużo:

// first build context
$dataFetcher->setPage(1);
// $dataFetcher->setPageSize(50); // not used here
// then do the job
$dataFetcher->getData('some name');

Niektóre parametry można pogrupować logicznie:

$pagination = new Pagination(1 /*, 50*/);
getData('some name', $pagination);
// Java coders will probably be familiar with this form:
getData('some name', new Pagination(1));

W ostateczności zawsze możesz wprowadzić parametr ad-hoc :

$param = new GetDataParameter();
$param->setPage(1);
// $param->setPageSize(50); // not used here
getData($param);

(co jest tylko gloryfikowaną wersją mniej formalnej techniki tablic parametrów )

Czasami sam powód, dla którego parametr jest opcjonalny, jest zły. Czy w tym przykładzie $pagenaprawdę ma być opcjonalne? Czy uratowanie kilku postaci naprawdę robi różnicę?

// dubious
// it is not obvious at first sight that a parameterless call to "getData()"
// returns only one page of data
function getData($page = 1);

// this makes more sense
function log($message, $timestamp = null /* current time by default */);
RandomSeed
źródło
0

Ten fragment:

    function getData ($ name, $ options) {
       $ default = array (
            „limit” => 50,
            'page' => 2,
        );
        $ args = array_merge ($ domyślnie, $ opcje);
        print_r ($ args);
    }

    getData ('foo', tablica ());
    getData ('foo', array ('limit' => 2));
    getData ('foo', array ('limit' => 10, 'page' => 10));

Odpowiedź to :

     Szyk
    (
        [limit] => 50
        [strona] => 2
    )
    Szyk
    (
        [limit] => 2
        [strona] => 2
    )
    Szyk
    (
        [limit] => 10
        [strona] => 10
    )

nobody0day
źródło
2
Ładny. Ale małe wyjaśnienie byłoby pomocne.
showdev
Dobra odpowiedź, ale jeśli chcesz ominąć opcjonalny parametr, zrób to w ten sposób: function getData ($ name, $ args = array ()) {$ defaults = array ('limit': => 50, 'page' => 2) ; $ args = array_merge ($ defaults, $ args); } Teraz można wywołać tę funkcję tylko z pierwszym parametrem, takim jak ten: getData ('foo');
EchoSin
0

Oto, co bym zrobił:

<?php

    function getData($name, $limit = '', $page = '1') {
            $limit = (EMPTY($limit)) ? 50 : $limit;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

     echo getData('table');

    /* output name=table&limit=50&page=1 */

     echo getData('table',20);

    /* name=table&limit=20&page=1 */

    echo getData('table','',5);

    /* output name=table&limit=50&page=5 */

    function getData2($name, $limit = NULL, $page = '1') {
            $limit = (ISSET($limit)) ? $limit : 50;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

    echo getData2('table');

    // /* output name=table&limit=50&page=1 */

    echo getData2('table',20);

    /* output name=table&limit=20&page=1 */

    echo getData2('table',NULL,3);

    /* output name=table&limit=50&page=3 */

?>

Mam nadzieję, że to komuś pomoże

Michael Eugene Yuen
źródło
0

To trochę stare pytanie z wieloma technicznie kompetentnymi odpowiedziami, ale woła o jeden z nowoczesnych wzorców projektowych w PHP: Programowanie zorientowane obiektowo. Zamiast wstrzykiwać zbiór prymitywnych skalarnych typów danych, rozważ użycie „wstrzykniętego obiektu”, który zawiera wszystkie dane wymagane przez funkcję. http://php.net/manual/en/language.types.intro.php

Wstrzyknięty obiekt może mieć procedury walidacji właściwości itp. Jeśli tworzenie instancji i wstrzyknięcie danych do wstrzykniętego obiektu nie jest w stanie przejść całej weryfikacji, kod może natychmiast zgłosić wyjątek, a aplikacja może uniknąć niezręcznego procesu z potencjalnie niekompletnymi danymi.

Możemy podpowiedzieć wpisany obiekt, aby wychwycić błędy przed wdrożeniem. Niektóre pomysły zostały podsumowane w tym artykule sprzed kilku lat.

https://www.experts-exchange.com/articles/18409/Using-Named-Parameters-in-PHP-Function-Calls.html

Ray Paseur
źródło
0

Musiałem stworzyć Factory z opcjonalnymi parametrami, moim obejściem było użycie operatora koalescencji zerowej:

public static function make(
    string $first_name = null,
    string $last_name = null,
    string $email = null,
    string $subject = null,
    string $message = null
) {
    $first_name = $first_name ?? 'First';
    $last_name  = $last_name ?? 'Last';
    $email      = $email ?? '[email protected]';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';
}

Stosowanie:

$factory1 = Factory::make('First Name Override');
$factory2 = Factory::make(null, 'Last Name Override');
$factory3 = Factory::make(null, null, null, null 'Message Override');

Nie jest to najpiękniejsza rzecz, ale może być dobrym wzorem do wykorzystania w fabrykach do testów.

Lucas Bustamante
źródło
-1

Spróbuj tego.

function getData($name, $limit = NULL, $page = '1') {
               if (!$limit){
                 $limit = 50;
               }
}

getData('some name', '', '23');
Dev Danidhariya
źródło
-2

Nie możesz pominąć środkowego parametru w wywołaniu funkcji. Ale możesz obejść to:

function_call('1', '2', '3'); // Pass with parameter.
function_call('1', null, '3'); // Pass without parameter.

Funkcjonować:

function function_call($a, $b='50', $c){
    if(isset($b)){
        echo $b;
    }
    else{
        echo '50';
    }
}
Ronak Patel
źródło
Nie jestem pewien, dlaczego masz głosy negatywne, poza tym, że $ c również wymaga wartości domyślnej (nie możesz mieć żadnych wymaganych argumentów po opcjonalnym argumencie).
Frank Forte
-2

Jak zauważył @IbrahimLawal. Najlepszą praktyką jest po prostu ustawienie nullwartości. Po prostu sprawdź, czy przekazana wartość jest wartością, nullw której używasz zdefiniowanych wartości domyślnych.

<?php
define('DEFAULT_LIMIT', 50);
define('DEFAULT_PAGE', 1);

function getData($name, $limit = null, $page = null) {
    $limit = is_null($limit) ? DEFAULT_LIMIT : $limit;
    $page = is_null($page) ? DEFAULT_PAGE : $page;
    ...
}
?>

Mam nadzieję że to pomoże.

asp.patrickg
źródło
Po prostu skopiowałeś odpowiedź. Raczej mógłbyś go zagłosować lub skomentować.
Ibrahim Lawal
-6
getData('some name');

po prostu ich nie przekazuj, a domyślna wartość zostanie zaakceptowana

Shal
źródło