Come saltare gli argomenti opzionali in una chiamata di funzione?


94

OK, ho completamente dimenticato come saltare gli argomenti in PHP.

Diciamo che ho:

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

Come chiamerei questa funzione in modo che il parametro centrale prenda il valore predefinito (es. "50")?

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

Quanto sopra sarebbe corretto? Non riesco a farlo funzionare.


1
@ Chuck per cosa stai cercando esattamente? Come una soluzione alternativa o una classe per implementare questo o ...?
Rizier123

@ Rizier123 Chiedo di vedere se la versione corrente di PHP supporta il salto degli argomenti (come fanno gli altri linguaggi). Le risposte attuali potrebbero essere obsolete. Dalla descrizione della taglia: "Le risposte attuali risalgono a cinque anni fa. La versione corrente di PHP cambia le cose?"
Chuck Le Butt

1
@ Chuck Allora la risposta sarà probabilmente: che non è cambiato nulla; senza una soluzione alternativa / codice non otterrai la tua funzionalità.
Rizier123

Tutte le risposte qui sembrano presumere che tu abbia il controllo sulla funzione chiamata. Se quella funzione fa parte di una libreria o di un framework non hai altra scelta che specificare qualcosa per l'argomento 2 se hai bisogno dell'argomento 3. L'unica opzione è cercare la funzione nel sorgente e replicare il valore predefinito.
Snapey

Non è possibile saltarlo, ma puoi passare l'argomento predefinito usando ReflectionFunction. Ho pubblicato quella risposta in una domanda simile.
David

Risposte:


55

Il tuo messaggio è corretto.

Sfortunatamente, se hai bisogno di usare un parametro opzionale alla fine dell'elenco dei parametri, devi specificare tutto fino a quell'ultimo parametro. Generalmente, se vuoi combinare e abbinare, dai loro i valori predefiniti di ''o nulle non li usi all'interno della funzione se sono quel valore predefinito.


1
Puoi fornire un esempio?
io il

2
@iamme fare $limit = is_numeric($limit) ? $limit : '50';all'inizio del getData funzione
Kamafeather

40

Non c'è modo di "saltare" un argomento diverso da quello di specificare un valore predefinito come falseo null.

Poiché PHP non ha un po 'di zucchero sintattico quando si tratta di questo, vedrai spesso qualcosa del genere:

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

Che, come detto in modo eloquente nei commenti, utilizza gli array per emulare argomenti con nome.

Ciò offre la massima flessibilità, ma in alcuni casi potrebbe non essere necessario. Per lo meno puoi spostare tutto ciò che pensi non sia previsto per la maggior parte del tempo alla fine della lista degli argomenti.


2
Identificherei "qualcosa di simile" come "utilizzo di array per emulare argomenti con nome", FWIW. :)
caos

3
Nonostante sia più flessibile devi ricordare i parametri che dovresti / puoi impostare (dato che non sono nella firma). Quindi alla fine potrebbe non essere così utile come sembra, ma ovviamente questo dipende dal contesto.
Felix Kling

Quindi quali sarebbero le migliori pratiche per un simile scenario?
Adam Grant,

39

No, non è possibile saltare gli argomenti in questo modo. È possibile omettere il passaggio di argomenti solo se si trovano alla fine dell'elenco dei parametri.

C'era una proposta ufficiale per questo: https://wiki.php.net/rfc/skipparams , che è stata rifiutata. La pagina della proposta si collega ad altre domande SO su questo argomento.


Allora come fanno le funzioni PHP ad avere parametri opzionali di più / meno?
io il

2
PHP supporta i valori dei parametri predefiniti. Ma quei parametri devono essere alla fine dell'elenco.
Cristik

1
Puoi trovare esempi nella documentazione PHP
Cristik

2
Jerks. Mi fa così arrabbiare. Se non ti piace una funzionalità richiesta da qualcun altro, non usarla. Internet a volte è il peggiore.
Lee Saxon

@LeeSaxon il problema è la leggibilità e la portabilità. 1) Se qualcuno decide di saltare i parametri e qualcuno che esegue il debug del codice non vede questo fatto, si verifica una grande confusione. 2) il nuovo codice non potrebbe funzionare su versioni precedenti senza un notevole over haul e le possibilità di errori sarebbero molto alte.
Mark Walsh

6

Non è cambiato nulla riguardo alla possibilità di saltare argomenti opzionali, tuttavia per una sintassi corretta e per poter specificare NULL per gli argomenti che voglio saltare, ecco come lo farei:

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;
    ...
}

Questo può essere chiamato in questo modo: getData('some name',NULL,'23');e chiunque chiami la funzione in futuro non avrà bisogno di ricordare ogni volta i valori predefiniti o la costante dichiarata per loro.


1
Probabilmente vorresti un confronto rigoroso ($ limit === null)
roelleor

4

La semplice risposta è no . Ma perché saltare quando riordinando gli argomenti si ottiene questo risultato?

Il tuo è un " Utilizzo errato degli argomenti della funzione predefinita " e non funzionerà come ti aspetti.

Una nota a margine dalla documentazione PHP:

Quando si utilizzano argomenti predefiniti, qualsiasi valore predefinito dovrebbe trovarsi a destra di qualsiasi argomento non predefinito; altrimenti, le cose non funzioneranno come previsto.

Considera quanto segue:

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

L'output sarà:

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

L'utilizzo corretto degli argomenti della funzione predefinita dovrebbe essere così:

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

L'output sarà:

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

Mettere il valore predefinito alla tua destra dopo i valori non predefiniti ti assicura che rieseguirà sempre il valore predefinito per quella variabile se non è definito / fornito. Ecco un collegamento di riferimento e da dove provengono questi esempi.

Modifica: impostarlo nullcome altri suggeriscono potrebbe funzionare ed è un'altra alternativa, ma potrebbe non adattarsi a ciò che desideri. Imposterà sempre il valore predefinito su null se non è definito.


Questa è la risposta migliore poiché fornisce spiegazioni sintattiche chiaramente illustrate! Grazie!
Christian

2

Per qualsiasi parametro saltato (devi) andare con il parametro di default, per essere al sicuro.

(Accontentarsi di null dove il parametro predefinito è "" o simile o viceversa ti metterà nei guai ...)


2

Come accennato in precedenza, non sarai in grado di saltare i parametri. Ho scritto questa risposta per fornire qualche aggiunta, che era troppo grande per essere inserita in un commento.

@Frank Nocke propone di chiamare la funzione con i suoi parametri di default, quindi per esempio avere

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

dovresti usare

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

che funzionalmente sarà uguale all'omissione dei primi due ( $be $c) parametri.

Non è chiaro quali siano i valori predefiniti (viene 0digitato per fornire il valore predefinito o è importante?).

Esiste anche il pericolo che il problema dei valori predefiniti sia collegato a una funzione esterna (o incorporata), quando i valori predefiniti potrebbero essere modificati dall'autore della funzione (o del metodo). Quindi, se non modifichi la chiamata nel programma, potresti modificarne involontariamente il comportamento.

Una soluzione alternativa potrebbe essere quella di definire alcune costanti globali, come il DEFAULT_A_B"valore predefinito del parametro B della funzione A" e i parametri "ometti" in questo modo:

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

Per le classi è più semplice ed elegante definire costanti di classe, perché fanno parte dell'ambito globale, ad es.

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.

Si noti che questa costante predefinita è definita esattamente una volta in tutto il codice (non c'è alcun valore anche nella dichiarazione del metodo), quindi in caso di modifiche impreviste, si fornirà sempre alla funzione / metodo il valore predefinito corretto.

La chiarezza di questa soluzione è ovviamente migliore che fornire valori di default grezzi (come NULL, 0ecc.) Che non dicono nulla a un lettore.

(Sono d'accordo che chiamare come $var = a(,,'ddd');sarebbe l'opzione migliore)


2

Non puoi saltare gli argomenti ma puoi usare i parametri dell'array e devi definire solo 1 parametro, che è un array di parametri.

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

E puoi aggiungere tutti i parametri di cui hai bisogno, non è necessario definirli. Quando chiami la funzione, inserisci i tuoi parametri in questo modo:

myfunction(array("name" => "Bob","age" => "18", .........));

0

Bene, come hanno già detto tutti gli altri, ciò che vuoi non sarà possibile in PHP senza aggiungere alcuna riga di codice nella funzione.

Ma puoi posizionare questo pezzo di codice all'inizio di una funzione per ottenere la tua funzionalità:

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

Quindi, in pratica, con debug_backtrace()ottengo il nome della funzione in cui è posizionato questo codice, per creare un nuovo ReflectionFunctionoggetto e ripetere tutti gli argomenti della funzione.

Nel ciclo controllo semplicemente se l'argomento della funzione è empty()E l'argomento è "opzionale" (significa che ha un valore predefinito). Se sì, assegno semplicemente il valore predefinito all'argomento.

Demo


0

Imposta il limite su null

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

e chiamare a quella funzione

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

se vuoi impostare il limite puoi passare come argomento

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

0

Come consigliato in precedenza , non è cambiato nulla. Attenzione, però, troppi parametri (specialmente quelli opzionali) sono un forte indicatore dell'odore del codice.

Forse la tua funzione sta facendo troppo:

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

Alcuni parametri potrebbero essere raggruppati logicamente:

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

In ultima istanza, puoi sempre introdurre un oggetto parametro ad-hoc :

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

(che è solo una versione glorificata della tecnica di array di parametri meno formale )

A volte, la vera ragione per rendere opzionale un parametro è sbagliata. In questo esempio, è $pagedavvero pensato per essere opzionale? Salvare un paio di personaggi fa davvero la differenza?

// 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 */);

0

Questo frammento:

    funzione getData ($ name, $ opzioni) {
       $ default = array (
            'limit' => 50,
            'page' => 2,
        );
        $ args = array_merge ($ default, $ opzioni);
        print_r ($ args);
    }

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

La risposta è:

     Vettore
    (
        [limite] => 50
        [pagina] => 2
    )
    Vettore
    (
        [limite] => 2
        [pagina] => 2
    )
    Vettore
    (
        [limite] => 10
        [pagina] => 10
    )


2
Bello. Ma una piccola spiegazione sarebbe utile.
showdev

Buona risposta, ma se vuoi omettere il parametro opzionale fallo in questo modo: function getData ($ name, $ args = array ()) {$ defaults = array ('limit': => 50, 'page' => 2) ; $ args = array_merge ($ defaults, $ args); } Ora è possibile chiamare questa funzione solo con il primo parametro come questo: getData ('foo');
EchoSin

0

Questo è quello che farei:

<?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 */

?>

Spero che questo possa aiutare qualcuno


0

Questa è una specie di vecchia domanda con un numero di risposte tecnicamente competenti, ma richiede uno dei modelli di progettazione moderni in PHP: la programmazione orientata agli oggetti. Invece di iniettare una raccolta di tipi di dati scalari primitivi, considera l'utilizzo di un "oggetto iniettato" che contiene tutti i dati necessari per la funzione. http://php.net/manual/en/language.types.intro.php

L'oggetto iniettato può avere routine di convalida delle proprietà, ecc. Se la creazione di istanze e l'inserimento di dati nell'oggetto iniettato non è in grado di passare tutta la convalida, il codice può generare immediatamente un'eccezione e l'applicazione può evitare il complicato processo di gestione con dati potenzialmente incompleti.

Possiamo suggerire di digitare l'oggetto iniettato per rilevare gli errori prima della distribuzione. Alcune delle idee sono riassunte in questo articolo di qualche anno fa.

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


0

Ho dovuto creare una Factory con parametri opzionali, la mia soluzione è stata quella di utilizzare l'operatore di coalescenza nullo:

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 ?? 'foo@bar.com';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';
}

Utilizzo:

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

Non è la cosa più carina, ma potrebbe essere un buon modello da utilizzare nelle fabbriche per i test.


-1

Prova questo.

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

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

-2

Non è possibile saltare il parametro centrale nella chiamata di funzione. Ma puoi aggirare con questo:

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

Funzione:

function function_call($a, $b='50', $c){
    if(isset($b)){
        echo $b;
    }
    else{
        echo '50';
    }
}

Non sono sicuro del motivo per cui hai voti negativi, oltre a $ c che necessita anche di un valore predefinito (non puoi avere argomenti obbligatori dopo un argomento opzionale).
Frank Forte

-2

Come ha sottolineato @IbrahimLawal. È buona norma impostarli solo su nullvalori. Controlla solo se il valore passato è quello nullin cui utilizzi i valori predefiniti.

<?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;
    ...
}
?>

Spero che questo ti aiuti.


Hai semplicemente duplicato la risposta. Piuttosto avresti potuto votare o commentarlo.
Ibrahim Lawal

-6
getData('some name');

basta non passarli e il valore predefinito sarà accettato

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.