Utilizzo degli argomenti predefiniti in una funzione


178

Sono confuso sui valori predefiniti per le funzioni PHP. Di 'che ho una funzione come questa:

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

Cosa succede se desidero utilizzare l'argomento predefinito per $ x e impostare un argomento diverso per $ y?

Ho sperimentato diversi modi e sto solo diventando più confuso. Ad esempio, ho provato questi due:

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

Ma entrambi non generano un argomento predefinito appropriato per $ x. Ho anche provato a impostarlo con il nome della variabile.

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

Mi aspettavo che qualcosa del genere funzionasse. Ma non funziona come mi aspettavo affatto. Sembra che non importa quello che faccio, finirò per digitare comunque gli argomenti predefiniti, ogni volta che invoco la funzione. E mi manca qualcosa di ovvio.


68
Probabilmente non ti stai perdendo nulla, probabilmente gli sviluppatori PHP hanno perso questo.
Matti Virkkunen,

Questa è una domanda interessante. In realtà non ho mai avuto la necessità di farlo come hai affermato poiché uso solo args predefiniti per espandere le funzioni ereditate da altri programmatori quando non sono sicuro di cosa dipenda dall'originale. Sono curioso di una risposta.
Kai Qing,

3
Una delle migliori domande che ho visto da un po '! Hai provato a non passare nulla? foo("blah", , "test");?
Madara's Ghost

2
La regola è che gli argomenti predefiniti possono seguire solo argomenti. Cioè potresti avere foo ("blah") e x, y sono predefiniti, oppure foo ("Blah", "xyz") e y è il valore predefinito.
Mouse Food

1
Comportamento atteso. Vedere la sezione "Valori argomenti predefiniti" in: php.net/manual/en/functions.arguments.php - il valore null e la stringa vuota sono accettati come argomenti effettivi alla funzione
jmdavalos

Risposte:


164

Suggerirei di modificare la dichiarazione di funzione come segue in modo da poter fare quello che vuoi:

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

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

    code here!

}

In questo modo, puoi effettuare una chiamata come foo('blah', null, 'non-default y value');e farla funzionare come desideri, in cui il secondo parametro $xottiene ancora il suo valore predefinito.

Con questo metodo, passare un valore nullo significa che si desidera il valore predefinito per un parametro quando si desidera sovrascrivere il valore predefinito per un parametro che lo segue.

Come indicato in altre risposte,

i parametri predefiniti funzionano solo come ultimi argomenti della funzione. Se si desidera dichiarare i valori predefiniti nella definizione della funzione, non è possibile omettere un parametro e sovrascriverne uno successivo.

Se ho un metodo che può accettare un numero variabile di parametri e parametri di vari tipi, dichiaro spesso la funzione simile alla risposta mostrata da Ryan P.

Ecco un altro esempio (questo non risponde alla tua domanda, ma si spera sia informativo:

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!");
    }
}

Perché non $ x = isset ($ x)? $ x: 'valore predefinito'; ?
zloctb,

@zloctb questo è un controllo null pulito e leggibile. Sembra che sia la preferenza dello sviluppatore. $ X = isset ($ x)? $ x: 'valore predefinito'; o $ x = is_null ($ x)? 'valore predefinito': $ x; funzionerebbe così come questa soluzione. È una scelta preferenziale in termini di leggibilità e manutenzione.
DeveloperWeeks

9
Giusto per chiarire i futuri lettori. La prima soluzione ovviamente rende impossibile assegnare un nullvalore reale al parametro, poiché ogni volta assegnerebbe il valore predefinito. Anche nel caso in cui avessi un altro valore speciale per quando effettivamente vuoi un null (ad esempio quando $ x == "use_null" rende $ x = null), allora non saresti in grado di assegnare un valore speciale come il valore letterale di parametro (in questo caso, la stringa "use_null"). Quindi assicurati di usare una "chiave" unica, mai desiderata, per quando vuoi usare il valore predefinito (e vuoi nullessere un'opzione valida)
DiegoDD

37

Gli argomenti opzionali funzionano solo al termine di una chiamata di funzione. Non è possibile specificare un valore per $ y nella funzione senza specificare anche $ x. Alcune lingue lo supportano tramite parametri denominati (ad esempio VB / C #), ma non PHP.

È possibile emularlo se si utilizza un array associativo per parametri anziché argomenti, ad es

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

    ...
}

Quindi chiama la funzione in questo modo:

foo(array('y' => 'my value'));

1
La tua condizione dovrebbe essere davvero isset($args['x']), poiché al momento sostituirebbe una stringa vuota, un array vuoto o falsecon il valore predefinito.
Tim Cooper,

@TimCooper lol Sono andato a cambiare in base al tuo primo commento che dovrebbe essere '=== null', sono andato e ho cambiato la mia risposta per usare invece isset, quindi sono tornato a vedere che hai cambiato anche il tuo commento. : D
Ryan P,

Ah, drat! Non mi piace per niente ... Oh beh, non puoi avere tutto. Suppongo che dovrò mettere un po 'più di pensiero nell'ordine dei miei argomenti di default allora. Grazie!
renosi

Sfortunatamente, non sarai in grado di assegnare null con questa risposta. Questa è un'opzione migliore: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Frank Forte,

20

In realtà è possibile:

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

Se ti piacerebbe farlo è un'altra storia :)


AGGIORNARE:

I motivi per evitare questa soluzione sono:

  • è (probabilmente) brutto
  • ha un evidente sovraccarico.
  • come le altre risposte dimostrano, ci sono alternative

Ma in realtà può essere utile in situazioni in cui:

  • non vuoi / non puoi cambiare la funzione originale.

  • potresti cambiare la funzione ma:

    • l'utilizzo null(o equivalente) non è un'opzione (vedi il commento di DiegoDD )
    • non vuoi andare né con un associativo né con func_num_args()
    • la tua vita dipende dal salvataggio di un paio di LOC

Per quanto riguarda le prestazioni, un test molto semplice mostra che l'utilizzo dell'API Reflection per ottenere i parametri predefiniti rende la chiamata della funzione 25 volte più lenta , mentre richiede ancora meno di un microsecondo . Dovresti sapere se riesci a conviverci.

Naturalmente, se intendi usarlo in un ciclo, dovresti ottenere in anticipo il valore predefinito.


1
Qual è la ragione per non voler fare questo? Sembra abbastanza utile.
Bryan,

10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}

2
Mi sto perdendo qualcosa? Questo non sembra rispondere alla domanda, ma ha 7 voti. Forse aggiungere un po 'di spiegazione sarebbe d'aiuto?
Sean the Bean,

3
@SeantheBean: vero, una spiegazione sarebbe migliore, ma il suo punto è: poiché PHP non ha argomenti nominati, usa un array associativo come unico parametro.
MestreLion,

1
È più utile usare l'esempio fornito nella domanda, ad esempio$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte,

4

L'unico modo che conosco per farlo è omettendo il parametro. L'unico modo per omettere il parametro è riorganizzare l'elenco dei parametri in modo che quello che si desidera omettere sia dopo i parametri che DEVI impostare. Per esempio:

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

Quindi puoi chiamare foo come:

foo("blah", "test");

Ciò comporterà:

$blah = "blah";
$y = "test";
$x = "some value";

3

Di recente ho avuto questo problema e ho trovato questa domanda e risposte. Mentre le domande precedenti funzionano, il problema è che non mostrano i valori predefiniti agli IDE che lo supportano (come PHPStorm).

inserisci qui la descrizione dell'immagine

se lo usi nullnon saprai quale sarebbe il valore se lo lasci vuoto.

La soluzione che preferisco è mettere il valore predefinito nella definizione della funzione anche:

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

L'unica differenza è che devo assicurarmi che siano uguali, ma penso che sia un piccolo prezzo da pagare per la chiarezza aggiuntiva.


L'uso interno di ReflectionFunction su se stesso risparmierebbe il prezzo!
Soggetto Delta

2

Non puoi farlo direttamente, ma un po 'di manipolazione del codice rende possibile emulare.

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

  // code
}

6
Funzionerebbe, ma trovo che usare nullin questa istanza sia concettualmente più pulito di false.
TRiG

Aggiungendo al commento di TriG, falseè una scelta più rischiosa rispetto a null, perché php è un linguaggio non rigoroso. !$xsarà vero per diversi input, che potrebbero sorprendere il programmatore: 0, ''. Considerando che con null, è possibile utilizzare !issetcome test più rigoroso.
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

?>

1

I miei 2 centesimi con operatore a coalescenza nullo ?? (dal PHP 7)

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

Puoi controllare altri esempi sul mio repl.it


0

Questo è il caso, quando l'oggetto è migliore, perché puoi impostare l'oggetto per contenere xey, impostare i valori predefiniti ecc.

L'approccio con l'array è vicino alla creazione di un oggetto (in effetti, l'oggetto è un insieme di parametri e funzioni che lavoreranno sull'oggetto, e la funzione che prende l'array funzionerà su alcuni parametri del mazzo)

Cerainly puoi sempre fare alcuni trucchi per impostare null o qualcosa del genere come predefinito


0

Puoi anche verificare se hai una stringa vuota come argomento in modo da poter chiamare come:

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

Sotto la funzione:

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!

}

Non importa se si riempie nullo "", si otterrà comunque lo stesso risultato.


0

Passa un array alla funzione, invece dei singoli parametri e usa l'operatore null coalescing (PHP 7+).

Di seguito, sto passando un array con 2 elementi. All'interno della funzione, sto verificando se è impostato il valore per item1, se non assegnato il vault predefinito.

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

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }

Puoi usare $args['item1'] = $args['item1']??'default value'; in PHP 7+. se $ args ['item1'] è nullo, la default valuestringa verrà assegnata a $ args ['item1']
Sadee,
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.