2009-09-22 11 views
9

Uwielbiam implementację Ruby w języku Ruby, w której można zainicjować obiekt Hash z wartością domyślną. W tej chwili zmagam się z implementacją podobnego obiektu w PHP. To jest mój pierwszy (niepracujący) strzał.Tablica PHP z domyślną wartością dla nieistniejących indeksów

class DefaultArray extends ArrayObject { 

    protected $_defaultValue; 

    public function setDefault($defaultValue) { 
    $this->_defaultValue = $defaultValue; 
    } 

    public function offsetExists($index) { 
    return true; 
    } 

    public function offsetGet($index) { 
    if(!parent::offsetExists($index)) { 
     if(is_object($this->_defaultValue)) 
     $default = clone $this->_defaultValue; 
     else 
     $default = $this->_defaultValue; 

     parent::offsetSet($index, $default); 
    } 
    return parent::offsetGet($index); 
    } 
} 

$da = new DefaultArray(); 
assert($da["dummy"] == null); 
$da->setDefault = 1; 
assert($da["dummy2"] == 1); 

Drugie asercja nie powiedzie się. Krok po kroku pokazuje, że offsetGet jest wywoływane, a klauzula if jest wykonywana. Niemniej jednak dowolna wartość tablicy ma wartość NULL. Wszelkie pomysły na alternatywne wdrożenia?

Jestem zmęczony pisania

if(!isset($myarr['value'])) 
    $myarr['value'] = new MyObj(); 
$myarr['value']->myVal=5; 

zamiast tylko pisanie

$myarr['value']->myVal=5; 
+0

Podczas uzyskiwania dostępu do klucza, który nie istnieje, 'offsetGet' będzie ustaw ten klucz na wartość domyślną (patrz "offsetSet"). Oznacza to, że następnym razem będzie istnieć. Jeśli nie chcesz tego zachowania i zawsze otrzymujesz aktualną wartość domyślną dla nieistniejących kluczy, usuń wywołanie 'offsetSet'. – Gumbo

+0

Dzięki, Gumbo za ważne wyjaśnienie, to zachowanie wprawiło mnie w zakłopotanie podczas pierwszego testu, ale jest zamierzonym zachowaniem. W odpowiedzi na pytanie, na które odpowiedział, zostawiam wadliwy kod testowy w niezmienionym stanie. – chiborg

Odpowiedz

6
$da->setDefault(1); 

Można również użyć __construct magiczną funkcję:

class DefaultArray extends ArrayObject 
{ 
    public function __construct($value = null){ 
     if(is_null($value)) 
     { 
      $this->value = 'default'; 
     } else { 
      $this->value = $value; 
     } 
    } 
} 
+0

Dzięki, dostrzegłeś mój błąd! To $ da-> setDefault (1) zamiast $ da-> setDefault = 1! Och zakłopotania! Po tej modyfikacji mój przykład działa zgodnie z oczekiwaniami. Myślałem również o przekazaniu wartości domyślnej w konstruktorze, ale chciałem zachować oryginalne argumenty konstruktora ArrayObject. – chiborg

+0

jak to zrobić (jeśli()) działa? – Codler

+0

@Codler: w żaden sposób ... dzięki, naprawione. – erenon

-3

Dlaczego tak skomplikowane ?

function initVal($value) { 
    global $myarr; 
    if(!isset($myarr['value'])) 
     $myarr['value'] = new MyObj(); 
} 

Teraz wystarczy zadzwonić:

initVal('bla'); 
$myarr['bla']->bla = 'bla'; 

Ale widzę, twój jest o wiele bardziej zadbane.

+3

Korzystanie z klasy ArrayObject zachowuje wszystkie zmienne i metody pomocnicze w jednym miejscu. Gdzie użycie funkcji * global * ma o wiele większe prawdopodobieństwo, że doprowadzi do problemów z utrzymaniem w przyszłości. – null

3

Wypróbuj magic methods __get.

class DefaultArray extends ArrayObject { 
    protected $_defaultValue; 

    public function setDefault($defaultValue) { 
     $this->_defaultValue = $defaultValue; 
    } 

    public function __get($index) { 
     return $this->offsetGet($index); 
    } 

    public function offsetGet($index) { 
     if(!parent::offsetExists($index)) { 
      if (is_object($this->_defaultValue)) { 
       $default = clone $this->_defaultValue; 
      } else { 
       $default = $this->_defaultValue; 
      } 
      parent::offsetSet($index, $default); 
     } 
     return parent::offsetGet($index); 
    } 
} 

Teraz wystarczy użyć różnych kluczy jak dostęp odczytu zainicjuje że elementy tablicy:

$da = new DefaultArray(); 
assert($da['foo'] == null); 
$da->setDefault(1); 
assert($da['bar'] == 1); 
0

Można użyć mój maleńki bibliotekę ValueResolver w tym przypadku, na przykład:

class DefaultArray extends ArrayObject 
{ 
    public function __construct($value = null){ 
     $this->value = ValueResolver::resolve($value, 'default'); // returns 'default' if $value is null 
    } 
} 

i nie zapomnij użyć przestrzeni nazw use LapaLabs\ValueResolver\Resolver\ValueResolver;

Istnieje również możliwość rzutowania, na przykład jeśli wartość zmiennej powinna być integer, więc to wykorzystać:

$id = ValueResolver::toInteger('6 apples', 1); // returns 6 
$id = ValueResolver::toInteger('There are no apples', 1); // returns 1 (used default value) 

Sprawdź docs po więcej przykładów

Powiązane problemy