2011-01-05 12 views
5

scrivere il mio primo classe PHP reale e sono imbattuto in un problema, il seguente è il mio metodo __construct:Convalida del __construct, assicurandosi Vars di di tipo corretto

public function __construct($foo,$bar) { 
     $this->foo = $foo; 
     $this->bar = $bar; 
} 

Sia $foo e $bar sono tenuti, senza di loro, i metodi falliranno e daranno errori php. Va bene quando non vengono definiti quando l'oggetto viene istanziato:

$var = new Class(); 

come questo vi darà un errore (ad esempio Classe richiede 2 params, nessuno set). Ma se sono impostate, ma non del corretto tipo di dati in questo modo:

$var = new Class('33','ddd'); 

I miei metodi verranno comunque provare a correre con le variabili sbagliate, e quindi stampare gli errori.

Qual è la procedura migliore per convalidare questi? Nel costrutto o in ogni metodo?

La mia soluzione che sto usando in questo momento funziona, ma non sono sicuro se è il modo 'corretto':

//$foo needs to be a string with letters only 
//$bar needs to be an integer 
public function __construct($foo,$bar) { 
     $this->foo = $foo; 
     $this->bar = $bar; 
     if(!is_numeric($bar)){ 
     //print error msg 
     } 
     elseif(other validation case) 
     etc... 
} 

I concetti di programmazione OO sono abbastanza nuovo per me, così i collegamenti a qualsiasi materiale di riferimento che hai usato sarebbe molto apprezzato.

+1

si dovrebbe non "il messaggio di errore di stampa" in un costruttore, si dovrebbe generare un'eccezione (come in @ risposta di Gordon sotto). Lo scopo del costruttore è di avere un oggetto valido dopo che è stato richiamato. Semplicemente stampando un messaggio di errore invece di un'eccezione, molto probabilmente finirai con un oggetto non valido che rovinerà tutto il tuo programma. – netcoder

+0

No, non dovresti usare le eccezioni. Usa 'assert()' per controllare i tipi di variabile. Gli argomenti non validi sono innanzitutto un problema di sviluppo. Questo è dove volevi cogliere questi problemi. Le eccezioni dovrebbero essere utilizzate quando è necessario prevedere argomenti non validi durante il runtime e se è possibile implementare ragionevolmente la logica catch per riprovare con argomenti alternativi. Per esempio. [Assert vs Exceptions] (http://stackoverflow.com/questions/117171/design-by-contract-tests-by-assert-or-by-exception/117247#117247) ma vedi anche i duplicati .. – mario

+0

@mario: Sono d'accordo che 'assert' può essere usato per controllare i tipi internamente. Ma non in un costruttore. Un argomento errato passato a un costruttore è un'eccezione, un'eccezione è una situazione eccezionale in cui il programma non può continuare ulteriormente. Se un costruttore ha argomenti negativi passati, non dovrebbe continuare ulteriormente. Altrimenti, come ho detto, si finisce con un oggetto non valido e un programma funzionante che considera valido l'oggetto e continua fino a quando non si verificano alcuni errori importanti (immondizia che viene inserita ripetutamente nel DB per esempio). – netcoder

risposta

18

probabilmente sarei fare qualcosa di simile per evitare l'ingombro all'interno del ctor e per permettere alla classe di impostare loro valori internamente:

class MyClass … 

    protected $_foo; 

    /** 
    * @param String $foo String with letters only 
    * @param Integer $bar Any Integer 
    * @return void 
    * @throws InvalidArgumentException when $foo is not letters only 
    * @throws InvalidArgumentException when $bar is not an Integer 
    */ 
    public function __construct($foo, $bar) 
    { 
     $this->_setFoo($foo); 
     $this->_setBar($bar) 
    } 

    /** 
    * @param String $foo String with letters only 
    * @return void 
    * @throws InvalidArgumentException when String is not letters only 
    */ 
    protected function _setFoo($foo) 
    { 
     if (FALSE === $this->_consistsOfLettersOnly($foo)) { 
      throw new InvalidArgumentException(
       '$foo should consists of letters only' 
      ); 
     } 
     $this->_foo = $foo; 
    } 

    … 

Questo ha il vantaggio che se avete bisogno di esporre pubblicamente il setter in seguito, devi solo modificare la parola chiave di visibilità.

Effettuare la convalida nel proprio metodo non è strettamente necessario, ma penso che renda il codice più leggibile. Se trovi che un'altra proprietà in quella classe ha bisogno della stessa validazione, puoi anche riutilizzarla più facilmente.

+0

+1 per InvalidArgumentException. – netcoder

+2

Grazie per la spiegazione e l'aggiornamento della tua risposta. – Daniel

+0

Impressionante Gordon, grazie! – Jimbo

2

La convalida dei tipi nel costruttore (come nel tuo esempio) mi sembra la scelta logica, anche se è un po 'strano se gli argomenti sono facoltativi. Probabilmente si dovrebbe impostare i valori predefiniti sicuri se questo è caso via ...

public function __construct($foo=null, $bar=null)

Detto questo, è possibile utilizzare type hinting per gli argomenti in PHP, ma credo che questo funziona solo per gli array e le classi. (Non è una sorpresa, in PHP non esiste un numero intero o stringa.)

Come altri hanno già detto, dovresti anche assicurarti di eseguire la convalida in qualsiasi setter che hai o (meglio ancora) semplicemente chiamare i setter dall'interno del costruttore per garantire che non ci sia alcuna duplicazione del codice.

+0

Vedo, se non vengono fornite le impostazioni predefinite nella __contstruct, sono classificate come facoltative? – Daniel

+0

@Daniel - No, significa solo che sono stati impostati su un valore predefinito "noto". Come tale, puoi semplicemente controllare contro null nel codice di convalida del tuo setter. –

0

Se sono richiesti $ foo e $ bar, convalidarli in __construct è il posto migliore per farlo (in caso contrario: utilizzare un setter e convalidare lì). Se lo fai in ogni metodo, ti ritroverai con il codice duplicato. Immagina se un tipo di dati cambia, dovresti cambiare ogni metodo ...

Se ti piace essere davvero schizzinoso, puoi chiamare is_numeric prima di assegnare $ bar a $ this-> bar in quanto l'assegnazione non è necessaria se il controllo di un valore numerico non riesce.

+0

Non proprio: se nella convalida è presente un codice duplicato, è possibile creare un altro metodo privato, che può essere utilizzato dai setter che ne hanno bisogno. – xil3

0

Il modo migliore è quello di utilizzare getter e setter - ad esempio:

private $_foo; 
private $_bar; 

public function __construct($foo,$bar) { 
    $this->setFoo($foo); 
    $this->setBar($bar); 
} 

public function setFoo($foo) { 
    // validate here 

    $this->_foo = $foo; 
} 

public function getFoo() { 
    return $this->_foo; 
} 

... 

Molto più pulito in questo modo ...

+0

Questa metodologia ha origine in Java. Ma non per far rispettare tipi o vincoli di valore. [Perché i metodi getter e setter sono malvagi] (http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html) – mario

0

I suoi problemi non sono derivante dal fatto che si sta scrivendo orientato agli oggetti codice - è perché PHP è un loosely typed language. Ciò significa che le variabili possono contenere qualsiasi tipo di dati, da int a Object. È possibile applicare un certo tipo di tipo di colata in questo modo:

$foo = (int)$foo; 
$bar = (str)$bar; 
0

Elaborando un po 'la risposta di @ xil3 direi tutta Dan era alla ricerca di/esigenze è:

private $_foo; 
private $_bar; 

public function __construct($foo,$bar) { 
    if(is_int($foo)) { 
    $this->setFoo($foo); 
    } 
    if(is_string($bar)) { 
    $this->setBar($bar); 
    } 
} 

public function setFoo(int $foo) { 
    $this->_foo = $foo; 
} 

public function setBar(string $bar) { 
    $this->_foo = $bar; 
} 

... 

permettendo al suo oggetto da istanziato senza parametri senza il fatale, con vincoli di tipo sui setter.

0

Non si dovrebbe fare altro che impostare valori nel costruttore. Se avete bisogno di validare parametri del costruttore a quella setter (possibilmente privati), o utilizzare gli argomenti di tipo particolare, in modo da PHP convaliderà tipo per voi, per esempio:

__construct(SomeType $foo, AnotherType $bar); 

Per esempio è possibile utilizzare i tipi di dati SPL:

__construct(SplInt $integer, SplString $string); 

Read more about SPL data types

Problemi correlati