2011-12-19 19 views
16

Ho alcuni metodi "setter" tra le classi e per comodità ho aggiunto un parametro opzionale $previous, che accetta un argomento per riferimento e lo popola con il valore esistente prima di sostituirlo con il nuovo uno. Ad esempio:Test degli argomenti opzionali in PHP

public function set_value($key, $value, &$previous = null) 
{ 
    $previous = $this->get_value($key); 
    $this->_values[$key] = $value; 
    return $this; 
} 

Questo funziona correttamente; tuttavia, in alcune circostanze, il corrispondente metodo "getter" è un processo un po 'intenso, e il suo funzionamento incondizionato è uno spreco. Ho pensato che avrei potuto provare:

if(null !== $previous) 
{ 
    $previous = $this->get_value($key); 
} 

Questo non funziona, però, come spesso la variabile passata come argomento per $previous non è stata definita in precedenza nel suo campo di applicazione, e il default è null comunque. L'unica soluzione Ho messo fuori è:

public function set_value($key, $value, &$previous = null) 
{ 
    $args = func_get_args(); 
    if(isset($args[2]) 
    { 
     $previous = $this->get_value($key); 
    } 
    $this->_values[$key] = $value; 
    return $this; 
} 

O, per una sola riga è:

if(array_key_exists(2, func_get_args())) 
{ 
    // ... 
} 

non mi piace il corpo del metodo essendo affidamento sugli indici di argomenti (quando sembra che non dovrebbe essere necessario) Esiste un modo più pulito per ottenere ciò che sto cercando qui?


ho provato:

if(isset($previous)){} 

if(!empty($previous)){} 

if(null !== $previous){} 

Né lavoro.

Possibili soluzioni così lontane:

if(func_num_args() == $num_params){} 

if(array_key_exists($param_index, func_get_args())){} 

// 5.4 
if(isset(func_get_args()[$param_index])){} 

// 5.4 
if(func_num_args() == (new \ReflectionMethod(__CLASS__, __FUNCTION__)) 
    ->getNumberOfParameters()){} 

@DaveRandom - Quindi, qualcosa nella zona di:

define('_NOPARAM', '_NOPARAM' . hash('sha4096', microtime())); 

function foo($bar = _NOPARAM) 
{ 
    // ... 
} 

@hoppa - Caso d'uso:

$obj->set_something('some_key', $some_value, $previous) // set 
    ->do_something_that_uses_some_key() 
    ->set_something('some_key', $previous) // and reset 
    ->do_something_that_uses_some_key() 
    -> ... 

Invece di:

$previous = $obj->get_something('some_key'); // get 
$obj->set_something('some_key', $some_value) // set 
    ->do_something_that_uses_some_key(); 
    ->set_something($previous) // and reset 
    ->do_something_that_uses_some_key(); 
    -> ... 
+3

non potresti definire il valore predefinito per '$ previous' come' FALSE (o qualche valore del " errato "tipo) - allora si sa che è' nullo' è stato passato, ma è 'FALSE' (o qualsiasi altra cosa) non lo era. Anche questo metodo presenta dei buchi (l'utente potrebbe passare il valore predefinito) ma penso che sarebbe un approccio decente, specialmente se si imposta il valore predefinito una stringa casuale lunga che è altamente improbabile che si trovi nella variabile che è stata passata . – DaveRandom

+0

@DaveRandom - Il valore precedente potrebbe essere un 'falso' booleano in alcune circostanze. Ho scelto "null" per il suo intento semantico, "assenza di valore". – Dan

+0

Vedere il commento modificato sulla stringa lunga casuale - Ammetto che non è un approccio bello o perfetto, ma è un approccio al 99.99999% ... – DaveRandom

risposta

3
non

possibilmente come si voleva risolvere il problema (test argomenti in qualche modo opzionali), ma questo è come vorrei implementarlo:

public function set_value($key, $value) 
{ 
    $this->_values[$key] = $value; 
    return $this; 
} 
public function set_get_value($key, $value, &$previous) 
{ 
    $previous = $this->get_value($key); 
    $this->_values[$key] = $value; 
    return $this; 
} 

Caso d'uso Esempio:

$obj->set_get_something('some_key', $some_value, $previous) // set AND get 
    ->do_something_that_uses_some_key() 
    ->set_something('some_key', $previous) // and reset 
    ->do_something_that_uses_some_key() 
    -> ... 

Perché usare un'altra funzione?

Questa soluzione ha alcuni vantaggi:

  1. il nome è più esplicito, meno confusione per altri codificatori
  2. senza effetti collaterali nascosti
  3. risolve il problema con (undefined) variabili già avere un valore
  4. nessun sovraccarico di chiamare func_num_args, o qualche altra funzione "meta"

MODIFICA: digitare il codice.

EDIT 2: rimosso valore di default di & $ set_get_value precedente() function (grazie a draevor)

+0

Buona alternativa. Una piccola correzione - '& $ precedente' non ha bisogno di avere un valore predefinito nella seconda funzione, è pensato per essere lì. – deviousdodo

+0

infatti :) modificato! – catchmeifyoutry

1

Estratto dal commento/discussione di cui sopra:

Al fine di verificare se è stato passato l'argomento che si hanno 2 opzioni - controllare il valore dell'argomento contro un valore (come si hai terminato con null) o controlla il numero di argomenti.

Se si va con la prima opzione, non c'è alcun valore che non può essere passato dall'esterno della funzione, quindi ci sarà sempre una possibilità di falsi positivi (la stessa cosa che sta accadendo ora con null). L'esempio di DaveRandom con una stringa casuale dovrebbe essere sufficiente per la maggior parte dei casi, ma lo considero eccessivo.

Penso che la seconda opzione sia la più pulita (veloce, leggibile, ecc.). Come un piccolo miglioramento rispetto a quello che hai già fatto con func_get_args, userei func_num_args - in questo modo controllerai il numero di argomenti passati, non gli indici degli argomenti.

+0

Grazie @draevor - Controlla la mia modifica sotto "* Ho provato/Possibili soluzioni *", l'ultima (* una volta che 5.4 ha colpito le strade *) avrebbe funzionato bene per la ri-usabilità agnostica della classe in un tratto, dato il ' Il parametro $ previous' è sempre l'ultimo. Come ho detto prima, la tua risposta è sicuramente il miglior candidato, lascerò stare seduto per un po ', e segnalo se non uscirà niente dalla falegnameria. – Dan

Problemi correlati