Programmazione.it v6.4
Ciao, per farti riconoscere devi fare il login. Non ti sei ancora iscritto? Che aspetti, registrati adesso!
Info Pubblicit� Collabora Autori Sottoscrizioni Preferiti Bozze Scheda personale Privacy Archivio Libri Corsi per principianti Forum
Properties alla C# in PHP
Scritto da Filippo Fadda il 05-03-2012 ore 01:23
Come saprete in PHP non esiste il concetto di property. La documentazione - a mio avviso erroneamente - chiama le variabili di classe properties, quando in realt queste ultime sono ben diverse dalle properties di C#. Quelle che la documentazione di PHP chiama properties non sono altro che pure e semplici variabili di classe, alle quali si pu assegnare, come per tutti i membri, un differente livello di visibilit: private, protected, public.

Come avviene in C++, per settare una variabile di classe privata (chiamiamola con il suo nome), necessario implementare un setter apposito, e richiamarlo. Un setter, lo dice la parola stessa, per l'appunto un metodo preposto a settare una variabile di classe. Nel seguito trovate l'implementazione di una classe Doc con una variabile $id definita private. Per essa stato implementato un setter, chiamato seguendo la convenzione setId. Il metodo permette di assegnare un valore alla variabile di classe privata.
  1. <?php
  2.  
  3. class Doc {
  4.   private $id;
  5.  
  6.   public function setId($value) {
  7.     $this->id = $value;
  8.   }
  9. }

In PHP un membro dichiarato private visibile soltanto all'interno della classe alla quale appartiene. Pertanto, per poter scrivere il valore della variabile di classe $id, dovremo usare il metodo pubblico setId:
  1. <?php
  2.  
  3. $doc = new Doc();
  4. $doc->setId(123);

Se infatti tentassimo di settare direttamente $doc->id, l'interprete tornerebbe giustamente un errore, poich violeremmo una regola base dell'information hiding.

C#, cos come fanno altri linguaggi, offre un meccanismo pi elegante, basato per l'appunto sulle properties.
In C# infatti posso scrivere:
  1. class TimePeriod
  2. {
  3.     private double seconds;
  4.  
  5.     public double Hours
  6.     {
  7.         get { return seconds / 3600; }
  8.         set { seconds = value * 3600; }
  9.     }
  10. }

I metodi get e set (due parole chiave) vengono automaticamente richiamati quando si tenta di leggere o scrivere la propriet Hours.
  1. class Program
  2. {
  3.     static void Main()
  4.     {
  5.         TimePeriod t = new TimePeriod();
  6.  
  7.         // Assigning the Hours property causes the 'set' accessor to be called.
  8.         t.Hours = 24;
  9.  
  10.         // Evaluating the Hours property causes the 'get' accessor to be called.
  11.         System.Console.WriteLine("Time in hours: " + t.Hours);
  12.     }
  13. }

Come mostra il breve spezzone di codice (preso dal sito Microsoft), possibile direttamente settare la property Hours, poich il compilatore chiamer automaticamente l'apposito setter.
Ebbene, non vi niente di male nel continuare ad utilizzare setId($value), ma ammetterete che il concetto di property, che proprio di C#, pi immediato e soprattutto decisamente pi elegante.

Come fare la stessa cosa in PHP e renderla disponibile per tutte le classi? Assodato che in PHP tutto ci non normalmente possibile, vediamo come, con qualche trucchetto, adattare il concetto di property a PHP.
Nella pratica non vogliamo far altro che poter scrivere:
  1. <?php
  2.  
  3. $doc = new Doc();
  4. $doc->id = 123;

Desideriamo poter settare direttamente $doc->id, anche se $id una variabile di classe privata. Per far ci dobbiamo istruire l'interprete, in modo tale che chiami il metodo setId($value), ogni qualvolta tentiamo di assegnare un valore alla variabile di classe $id, affinch questa diventi una vera e propria property.

PHP fortunatamente mette a disposizione quelli che tecnicamente vengono chiamati Magic Methods. Sono dei metodi richiamati automaticamente quando tentiamo di leggere, settare ed eseguire altre operazioni su una qualunque variabile di classe. In particolare sono quattro i metodi che ci interessano: __set, __get, isset e __unset.
Vogliamo dunque fare in modo che il metodo setId($value), precedentemente definito, venga chiamato automaticamente ogni qualvolta venga assegnato un valore a $doc->id. A tal fine dobbiamo fare l'override del magic method __set, come illustrato di seguito:
  1. <?php
  2.  
  3. class Doc {
  4.   private $id;
  5.  
  6.   public function setId($value) {
  7.     $this->id = $value;
  8.   }
  9.  
  10.   public function __set($name, $value) {
  11.     if (method_exists($this, ($method = 'set'.ucfirst($name))))
  12.       $this->$method($value);
  13.     else
  14.       throw new BadMethodCallException("Method $method is not implemented for property $name.");
  15.   }
  16. }

Il metodo verifica l'esistenza dell'apposito setter, in questo caso setId, per poi richiamarlo.

La tecnica applicabile per tutte le variabili di classe, bisogna solo ricordarsi, naturalmente, di implementare gli appositi setter e getter, come peraltro si fa in C#. In questo modo non si viola l'information hiding, poich alla variabile di classe $id si accede comunque tramite il setter pubblico setId($value), richiamato automaticamente dal magic method __set($name, $value), di cui stato effettuato l'override.

Adesso la nostra classe Doc dispone di una propriet $id (ora possiamo chiamarla cos); ma cosa fare per le altre classi? Non possiamo pensare di effettuare l'override dei Magic Methods in ciascuna classe, n possiamo pensare di far derivare tutte le nostre classi da un'unica superclasse, poich non avrebbe senso avere delle properties anche laddove non servono. La soluzione a questo problema viene con PHP 5.4.

PHP 5.4 introduce una nuova entit chiamata Trait. Fondamentalmente si tratta di un copia/incolla a livello di interprete. E' un modo un po' sporco, ma assolutamente pratico, di simulare l'ereditariet multipla. I trait sono sostanzialmente dei contenitori con proprie funzioni membro. A differenza delle classi, i trait non possono essere istanziati. Sono dei meri contenitori che semplificano il riuso di codice. Le classi, attraverso la keyword use, la stessa che si usa per i namespace, possono infatti includere il contenuto di un trait al loro interno. In sostanza tutti i metodi di un trait diventano metodi della classe che lo utilizza. Pi classi possono includere il medesimo trait e trait differenti possono essere usati da una stessa classe. Una classe pu contemporaneamente avere un anchestor e utilizzare pi trait. L'iniezione di codice viene fatta direttamente dall'interprete, per cui il codice non viene realmente duplicato.

Non ci resta dunque che spostare l'implementazione dei Magic Methods dalla classe Doc ad un trait che chiameremo Properties:
  1. <?php
  2.  
  3. trait Properties {
  4.  
  5.   public function __get($name) {
  6.     if (method_exists($this, ($method = 'get'.ucfirst($name))))
  7.       return $this->$method();
  8.     else
  9.       throw new BadMethodCallException("Method $method is not implemented for property $name.");
  10.   }
  11.  
  12.   public function __isset($name) {
  13.     if (method_exists($this, ($method = 'isset'.ucfirst($name))))
  14.       return $this->$method();
  15.     else
  16.       throw new BadMethodCallException("Method $method is not implemented for property $name.");
  17.   }
  18.  
  19.   public function __set($name, $value) {
  20.     if (method_exists($this, ($method = 'set'.ucfirst($name))))
  21.       $this->$method($value);
  22.     else
  23.       throw new BadMethodCallException("Method $method is not implemented for property $name.");
  24.   }
  25.  
  26.   public function __unset($name) {
  27.     if (method_exists($this, ($method = 'unset'.ucfirst($name))))
  28.       $this->$method();
  29.     else
  30.       throw new BadMethodCallException("Method $method is not implemented for property $name.");
  31.   }
  32. }

A questo punto il codice della classe Doc sar:
  1. class Doc {
  2.   use Properties;
  3.  
  4.   private $id;
  5.  
  6.   public function setId($value) {
  7.     $this->id = $value;
  8.   }
  9. }

Come detto i metodi del trait verranno iniettati nella classe dall'interprete e saranno quindi disponibili come se fossero parte della stessa. Le variabili di classe di ogni classe che far uso del trait Properties, diverrano di fatto delle properties alla C#.
Precedente: In beta Visual Studio 11 ed il .NET Framework 4.5
Successiva: PHP 5.4, un piccolo grande aggiornamento
Commenti:  Primi  «  Meno recenti  «  11 - 11 di 11
Intervento di Ezio Meneghini a.k.a. santecaserio del 10-12-2012 ore 23:02, Ancona (AN)
Plebeo
Plebeo
(13 interventi)
Iscritto il 10-01-2009
cdimauro ha scritto:
Infatti dicevo "come minimo Delphi". :P

Ma sicuro che le propriet di LISP/Smalltalk implementino le stesse funzionalit / meccanismo?

Certo, era pi che altro una curiosit storica :) Ma sono abbastanza sicuro che un programmatore ancora pi vecchio di me ti avrebbe trovato un esempio ancora pi datato! :)

SmallTalk aveva una sintassi sua (PHP, Java, Delphi... hanno tutti la stessa identica sintassi, a parte pochi dettagli) ma la logica quella tipica di un linguaggio OO.

Lisp pi funzionale che OO, diciamo che assomiglia a JavaScript. Ma le propriet ci sono.

In realt, se ci pensi bene, non hai nemmeno bisogno di un linguaggio OO per creare oggetti. Esempio: un db relazionale; le stored procedure sono i metodi pubblici; le sp che non hai i permessi per chiamare sono metodi privati; una tabella a cui non puoi accedere contiene le propriet private; una a cui puoi accedere contiene quelle pubbliche. E' un oggetto a tutti gli effetti, basta che tu lo pensi come tale.
(manca solo l'ereditariet... ma potresti crearla)
Perfino un set di programmi chiamabili dalla riga di comando pu essere un oggetto.
Commenti:  Primi  «  Meno recenti  «  11 - 11 di 11
Copyright Programmazione.it™ 1999-2017. Alcuni diritti riservati. Testata giornalistica iscritta col n. 569 presso il Tribunale di Milano in data 14/10/2002. Pagina generata in 0.343 secondi.