PHP dispone di un certo un mero di funzioni dette “metodi magici“. Si tratta di metodi che vengono attivati al verificarsi di terminati eventi, ed aventi una sintassi particolare nel nome. Difatti li possiamo riconoscere facilmente visto che iniziano tutti con un doppio undescore.
Due di questi (__construct
e __destruct
) li abbiamo già conosciuti in un mio precedente post: capire i costruttori ed i distruttori, mentre l’elenco completo lo potete trovare direttamente nella documentazione ufficiale: PHP Magic Methods.
In questo articolo voglio soffermarmi su un paio di metodi magici, cioè __get()
e __set()
, soprattutto dal punto di vista della loro utilità e possibilità di utilizzo nel “mondo reale”.
Come funzionano
Le funzioni __get
, e __set
naturalmente si riferiscono alla programmazione OOP, quindi devono essere dichiarate all’interno di una classe.
Questi metodi sono invocati quando la proprietà è inaccessibile (o non esiste, quindi è inaccessibile).
class Foo { private $baz = "baz_value"; public function __get($name) { echo 'Attributo non trovato'; } } $foo = new Foo(); echo $foo->baz;
Nel codice precedente verrà invocato il metodo __get
visto che la proprietà $baz
anche se esiste è privata, quindi non accessibile direttamente dall’esterno.
[box type=”note”]Se $baz
fosse stata pubblica allora il metodo non sarebbe stato invocato.[/box] Stesso discorso per quanto riguarda __set, che sarà invocato quando si cerca di settare il valore di un attributo inaccessibile.
class Foo { public function __set($name, $value) { echo 'Attributo non trovato'; } } $foo = new Foo(); $foo->baz = 'baz_value';
__get e __set come getter e setter
Bene ora che abbiamo capito cono funzionano, la domanda nasce spontanea. Può essere considerata una buona pratica utilizzare i metodi magici __get
e __set
come sostituti di dei canonici getter e setter?
Facciamo alcune considerazioni aiutandoci con qualche esempio.
Usando __get e __set
class Foo { private $baz; private $boo; public function __get($name) { if (property_exists($this, $name)) { return $this->$name; } } public function __set($name, $value) { if (property_exists($this, $name)) { $this->$name = $value; } } } $foo = new Foo(); $foo->baz ='baz_value'; $foo->boo = 'boo_value'; echo $foo->baz; echo $foo->boo;
Lo snippet di codice precedente è piuttosto intuitivo, abbiamo usato i metodi magici per ottenere e settare i valori di proprietà non direttamente accessibili dall’esterno. Vediamo lo stesso esempio usando questa volta un approccio più tradizionale.
Usando i tradizionali getters e setters
class Foo { private $baz; private $boo; public function getBaz() { return $this->baz; } public function setBaz($value) { $this->baz = $value; } public function getBoo() { return $this->boo; } public function setBoo($value) { $this->boo = $value; } } $foo = new Foo(); $foo->setBaz('baz_value'); $foo->setBoo('boo_value'); echo $foo->getBaz(); echo $foo->getBoo();
La prima cosa da notare è che da entrambi gli esempi otterremo lo stesso identico risultato.
A prima vista la prima soluzione potrebbe apparire come la migliore o quantomeno quella più concisa e snella. Due metodi contro quattro (che aumenterebbero con l’aumentare delle proprietà).
Tuttavia ci non diversi aspetti negativi e pericolosi da considerare:
__get
e__set
sono decisamente più lenti (fino a 10 volte) di getter / setter- Rendono impossibile il completamento automatico del codice e questo è un grave problema (personalmente non conosco un IDE che sia in grado di farlo).
- Le APIs risultano poco chiare.
- Il sistema è più difficile da capire, soprattutto per i nuovi arrivati.
Considerazioni finali
I metodi magici non sono sostituti di getter e setter. Come spiegato consentono di gestire le chiamate a proprietà inaccessibili che altrimenti causerebbero un errore. Quindi a mio avviso sono da considerarsi più legati alla gestione degli errori che altro.
Nel caso in cui, tuttavia, scegliessimo si utilizzare __get
e __set
come getter e setter questo non è un errore, ma solamente, almeno dal mio punto di vista, una cattiva pratica.
In questo caso è raccomandato utilizzare un array:
class Foo { protected $_values = array(); public function __get($key) { return $this->_values[$key]; } public function __set($key, $value) { $this->_values[$key] = $value; } }
In questo modo siamo sicuri che non è possibile accedere alla variabile in un’altro modo per evitare collisioni (notare che $_value
è protetta).
2 risposte su “PHP – metodi magici __get e __set best practice”
Ottimo articolo, chiaro e conciso.
Simply wish to say your article is as amazing.
The clearness for your submit is just excellent and i could suppose you’re a
professional on this subject. Fine with your permission let me
to clutch your RSS feed to keep updated with approaching post.
Thanks one million and please carry on the enjoyable work.