PHP Object Overloading

lun, giu 29, 2009

Per chi fa Web

PHPCome noto, il linguaggio PHP è una delle tecnologie lato server più utilizzate dagli sviluppatori web. I motivi di questa popolarità sono principalmente due:

  • Fornisce un’API specifica per interagire con Apache, il web server più diffuso al mondo.
  • Ha una sintassi molto simile al C. Uno dei linguaggi di programmazione che hanno fatto la storia dell’informatica.

Dalla versione 5 PHP è stato arricchito di nuovi aspetti permettendo la nascita ed il consolidamento di numerosi framework. Tra le caratteristiche di questo linguaggio ce n’è una che mi affascina particolarmente ed è l’object overloading. Generalmente con il termine overloading, ci si riferisce a quella caratteristica di un linguaggio che permette di dichiare più metodi con la stessa firma ma con numero e/o tipi di parametri diversi. In PHP, invece, l’object overloading è la caratteristica che permette agli oggetti di una classe, di interpretare chiamate a metodi o attributi che non sono stati definiti. Tutto ciò è possibile grazie ai cosiddetti “magic methods” che, una volta definiti, permetto di intercettare tutte le chiamate inconsistenti. Questi metodi sono:

  • __get(string $name): viene chiamato quando si cerca di leggere il valore di un attributo che non è stato dichiarato.
  • __set(string $name, mixed $value): viene chiamato quando si cerca di scrivere il valore di un attributo che non è stato dichiarato.
  • __isset(string $name): viene chiamato quando si utilizzano i metodi isset o empty su un attributo che non è stato dichiarato.
  • __unset(string $name): viene chiamato quando si utilizza il metodo unset su un attributo che non è stato dichiarato.
  • __call(string $name, array $arguments): viene chiamato quando si chiama un metodo che non è stato definito.
  • __callStatic(string $name, array $arguments): come il metodo precedente ma per le chiamate statiche (ad esempio NomeClasse::metodo()).

Utilizzando questi metodi è possibile creare classi i cui attributi e metodi sono dinamici. Ad esempio si consideri la seguente classe:

class Test {
  /**  Array dei dati overloaded  */
  private $data = array();
 
  /**
   * L'overloading non viene usato su
   * questo attributo perchè è dichiarato.
   */
  public $declared = 1;
 
  /**
   * Per questo attributo viene usato
   * l'overloading solo se si tenta di accedere
   * dall'esterno della classe.
   */
  private $hidden = 2;
 
  public function __set($name, $value) {
    $this->data[$name] = $value;
  }
 
  public function __get($name) {
    if (array_key_exists($name, $this->data)) {
      return $this->data[$name];
    }
 
    $trace = debug_backtrace();
    trigger_error(
      'Undefined property via __get(): ' . $name .
      ' in ' . $trace[0]['file'] .
      ' on line ' . $trace[0]['line'],
      E_USER_NOTICE);
    return null;
  }
 
  public function __isset($name) {
    return isset($this->data[$name]);
  }
 
  public function __unset($name) {
    unset($this->data[$name]);
  }
}

Ora immaginiamo che il nostro script PHP instanzi un oggetto della classe Test di cui sopra:

$obj = new Test();
 
/**
 * La prossima istruzione genererà una chiamata al metodo
 * __set definito in precedenza. I parametri $name e $value
 * assumeranno rispettivamente i valori 'attrOverloaded' e 'ciao mondo!'.
 * Il metodo __set quindi aggiungerà l'indice 'attrOverloaded'
 * all'array $data e ne associerà il valore 'ciao mondo!'
 */
$obj->attrOverloaded = 'ciao mondo!';
 
/**
 * La prossima istruzione genererà una chiamata al metodo
 * __get definitio in precedenza. Il parametro $name avrà
 * il valore 'attrOverloaded'. Il metodo restituirà il
 * rispettivo valore presente nell'array $data ovvero
 * 'ciao mondo!'
 */
echo $obj->attrOverloaded;
 
/**
 * Per la prossima istruzione non verrà utilizzato
 * l'overloading in quando declared è un attributo
 * dichiarato dell'oggetto.
 */
echo $obj->declared;
 
/**
 * Per la prossima istruzione verrà utilizzato
 * l'overloading in quanto hidden è un attributo
 * privato ma non essendo però definito nell'array
 * $data verrà lanciato l'errore.
 */
echo $obj->hidden;

Se è chiaro come funziona potete divertirvi a prevedere l’output dello script (e magari postarlo nei commenti).

Uno svantaggio dell’utilizzo di questa tecnica è che l’esecuzione di questi metodi è 4 volte più lenta di un normale metodo di un oggetto. Inoltre, si perde la nozione di attributi privati o protetti.  La comodità di questi metodi però ne favorisce il suo utilizzo. Per citare un esempio pratico, si consideri che uno degli impieghi più frequenti di PHP, è quello di utilizzarlo per creare model. I model sono classi che modellano i dati di una qualsiasi entità che solitamente risiede su database. Ogni volta che si vuole modificare un’entità già esistente occorre anche aggiornare il model corrispondente.  Ancora, se si aggiunge una nuova entità su database ecco che occorre creare un apposito model. Utilizzando l’object overloading, invece, è possibile realizzare un model universale in grado di adattarsi a qualsiasi entità con qualsiasi attributo.

, , , , ,

Questo articolo è stato scritto da:

Manuele Menozzi - autore di 4 articoli su Webgriffe® magazine.

Ingegnere informatico, entrato a far parte del team di sviluppo di Webgriffe da gennaio del 2009, si sta specializzando in tecnologie di sviluppo per il web.

Contatta l'autore

Scrivi un Commento