2011-12-19 12 views
16

Mam kilka metod "ustawiania" na różnych klasach, a dla wygody dodałem opcjonalny parametr $previous, który pobiera argument przez odniesienie i zapełnia go istniejącą wartością przed zastąpieniem go nowymi jeden. Na przykład:Testowanie opcjonalnych argumentów w PHP

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

To działa dobrze; jednak w niektórych okolicznościach odpowiednia metoda "getter" jest nieco procesochłonna, a jej bezwarunkowe działanie jest marnotrawstwem. Pomyślałem mogłem przetestować:

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

ta nie działa jednak, jak często zmienna przekazywana jako argument za $previous nie został wcześniej zdefiniowany w jego zakres, a domyślnie i tak null. Jedynym rozwiązaniem, jakie włamał się to:

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

Albo do jednej linii to:

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

Nie lubię ciało sposób jest uzależnione od indeksów argument (kiedy go wydaje się, że powinno być niepotrzebne) Czy istnieje lepszy sposób, aby osiągnąć to, co chcę tutaj?


Próbowałem:

if(isset($previous)){} 

if(!empty($previous)){} 

if(null !== $previous){} 

Ani pracy.

Możliwe rozwiązania dotąd:

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 - Tak, coś w obszarze:

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

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

@hoppa - przypadek użycia:

$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() 
    -> ... 

Zamiast:

$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

nie można zdefiniować wartość domyślną dla '$ previous' jako' FALSE (lub jakąś wartość z " wrong "type) - wtedy wiesz, że jest" null ", który został przekazany, ale jest to" FALSE "(lub cokolwiek innego), nie było. Ta metoda ma również dziury (użytkownik może przekazać wartość domyślną), ale myślę, że byłoby to przyzwoite podejście - szczególnie jeśli ustawisz domyślną wartość długiego losowego ciągu, który jest bardzo mało prawdopodobny w zmiennej, która została przekazana . – DaveRandom

+0

@DaveRandom - poprzednia wartość może być boolowskim 'false' w niektórych okolicznościach. Wybrałem 'null' dla semantycznego zamiaru" braku wartości ". – Dan

+0

Zobacz edytowany komentarz na temat długiego losowego ciągu - Przyznaję, że to nie jest piękne lub nieskazitelne podejście, ale to działa 99.99999% podejście ... – DaveRandom

Odpowiedz

3
nie

możliwie jak chcesz rozwiązać problem (testowania jakoś opcjonalnych argumentów), ale jest to jak bym go wdrożyć:

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

przypadek użycia przykład:

$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() 
    -> ... 

Dlaczego warto korzystać z innej funkcji?

Takie rozwiązanie ma kilka zalet:

  1. nazwa jest bardziej wyraźne, mniej zamieszania innych koderów
  2. żadnych ukrytych efektów ubocznych
  3. rozwiązuje problem (nieokreślone) zmienne już posiadające wartość
  4. żaden narzut nazywając func_num_args, lub jakaś inna funkcja "meta"

EDYCJA: literówka w kodzie.

EDIT 2: usuwa wartość domyślna & $ poprzedniego set_get_value() funkcji (dzięki draevor)

+0

Dobra alternatywa. Mała korekta - '& $ previous' nie musi mieć domyślnej wartości w drugiej funkcji, powinna tam być. – deviousdodo

+0

rzeczywiście :) edytowane! – catchmeifyoutry

1

wydobyte z uwagi/dyskusji powyżej:

W celu sprawdzenia, czy argument został przekazany masz 2 opcje - sprawdzić wartość argumentu przeciwko wartości (jak ty 've done with null) lub sprawdź liczbę argumentów.

Jeśli wybierzesz pierwszą opcję, nie ma wartości, która nie może zostać przekazana spoza tej funkcji, więc zawsze będzie szansa na fałszywe alarmy (to samo dzieje się teraz z wartością null). Przykład DaveRandoma z losowym łańcuchem powinien wystarczyć w większości przypadków, ale widzę go jako przesadę.

Myślę, że druga opcja jest najbardziej czysta (szybka, czytelna, itp.). Jako małe ulepszenie w stosunku do tego, co już zrobiłeś z func_get_args, użyłbym func_num_args - w ten sposób sprawdzasz liczbę przekazanych argumentów, a nie indeksy argumentów.

+0

Dzięki @draevor - Sprawdź moją edycję w "* Wypróbowałem/Możliwe rozwiązania *", ostatnia (* raz 5.4 trafi na ulice *) dobrze by działała dla klasy przydatności do ponownego użycia w cechach, biorąc pod uwagę ' Parametr $ previous' jest zawsze ostatni. Tak jak powiedziałem wcześniej, twoja odpowiedź jest z pewnością najlepszym kandydatem, jednak pozwolę ci na to trochę usiąść i oznaczyć twoją, jeśli nic innego nie wyjdzie z drewna. – Dan