2009-09-22 13 views
9

Mi piace l'implementazione di Hash di Ruby in cui è possibile inizializzare l'oggetto Hash con un valore predefinito. Al momento sto lottando con l'implementazione di un oggetto simile in PHP. Questo è il mio primo sparo (non funzionante) a questo.Array PHP con valore predefinito per gli indici non esistenti

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

La seconda affermazione fallirà. Passando attraverso il codice si mostra che viene chiamato offsetGet e viene eseguita la clausola if. Tuttavia, qualsiasi valore di matrice è nullo. Qualche idea per implementazioni alternative?

Sono stanco di scrivere

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

invece di scrivere

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

Quando si accede a una chiave che non esiste, 'offsetGet' sarà imposta quella chiave con il valore predefinito (vedi 'offsetSet'). Ciò significa che la prossima volta esisterà. Se non si desidera tale comportamento e si ottiene sempre il valore predefinito corrente per le chiavi non esistenti, rimuovere la chiamata 'offsetSet'. – Gumbo

+0

Grazie, Gumbo per l'importante chiarimento, questo comportamento mi ha sconcertato nel mio primo test, ma è il comportamento previsto. La domanda in ora ha risposto e lascerò il codice di test difettoso così com'è. – chiborg

risposta

6
$da->setDefault(1); 

È inoltre possibile utilizzare la funzione di __construct magia:

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

Grazie, hai notato il mio errore! È $ da-> setDefault (1) invece di $ da-> setDefault = 1! Oh, l'imbarazzo! Con questa modifica il mio esempio funziona come previsto. Ho anche pensato di passare il valore predefinito nel costruttore ma volevo conservare gli argomenti del costruttore originale di ArrayObject. – chiborg

+0

come fare se (if()) funziona? – Codler

+0

@Codler: in nessun modo ... grazie, risolto. – erenon

-3

Perché così complicato ?

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

Ora devi solo chiamare:

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

Ma vedo, il vostro è molto più pulito.

+3

L'utilizzo della classe ArrayObject mantiene tutte le variabili e i metodi di supporto in un'unica posizione. Dove l'uso della funzione * global * ha una probabilità molto più elevata di portare a problemi di manutenzione in futuro. – null

3

Provare il 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); 
    } 
} 

Ora è solo bisogno di usare chiavi diverse, come l'accesso in lettura inizializzerà che gli elementi di matrice:

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

Si potrebbe utilizzare la mia piccola biblioteca ValueResolver in questo caso, ad esempio:

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

e non dimenticare di usare namespace use LapaLabs\ValueResolver\Resolver\ValueResolver;

Ci sono anche la capacità di fusione di caratteri, ad esempio se il valore della vostra variabile dovrebbe essere integer, in modo da utilizzare questo:

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

Controllare la docs per ulteriori esempi

Problemi correlati