Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

La programmazione a oggetti in PHP 5

Le caratteristiche principali del nuovo PHP 5. Uno sguardo approfondito al nuovo modello di programmazione ad oggetti
Le caratteristiche principali del nuovo PHP 5. Uno sguardo approfondito al nuovo modello di programmazione ad oggetti
Link copiato negli appunti

A parecchi mesi di distanza dall'Le novità di PHP 5 di Andrea Cristaudo sulle novità di PHP 5, ora che abbiamo a disposizione la release definitiva, torniamo sull'argomento con un'ulteriore panoramica sulle caratteristiche del nuovo modello a oggetti del linguaggio.

Prima di proseguire con la lettura dei prossimi paragrafi consigliamo la lettura delle 25 domande e risposte su PHP 5 e dei numerosi articoli che freephp ha dedicato alla programmazionea oggetti in PHP 4.

Costruttori e distruttori

Una prima piccola innovazione di PHP 5 riguarda la possibilità di utilizzare un nome standardizzato per i costruttori, __construct(), così se la classe dovesse essere rinominata non sarà necessario modificare anche il nome del suo costruttore.

Un caratteristica del tutto nuova invece è l'introduzione di veri distruttori, attraverso il metodo predefinito __destruct(), non è più indispensabile quindi ricorrere a "workarounds" come l'utilizzo di register_shutdown_function().

I distruttori sono utili per "operazioni di pulizia", come ad esempio eliminare file temporanei e chiudere le connessioni a database.


<?php

class Php5File

{

   protected $resource ;

   protected $fname ;


/*

Costruttore, apre lo stream e inizializza l'oggetto

*/


   function __construct($nome) {

      

      $this->fname = $nome ;                  

      $this->resource = fopen($nome,'r') ;

   }


/*

Legge il contenuto del file

*/


   function getContent() {

    return fread($this->resource, filesize($this->fname)) ;

   }


/*

Chiude lo stream quando l'oggetto non è più necessario

e ogni riferimento è stato eliminato

*/


   function __destruct() {

      if(isset($this->resource)){

         fclose($this->resource) ;

      }

   }

}//END class

//Istanzia l'oggetto

$file = new Php5File('esempio.txt');

echo ('<pre>');

//Stampa il contenuto del file

echo($file->getContent()) ;

echo ('</pre>');

?>

La garbage collection che innesca il metodo __destruct non interviene più al termine dello script (come nel caso di register_shutdown_function in PHP 4) ma nel momento in cui viene eliminato l'ultimo riferimento all'oggetto (vedi anche paragrafo sui "singleton").

Oggetti, copie e riferimenti

Una delle mancanze più sentite in PHP 4 è rappresentata dal fatto che gli oggetti vengono trattati come variabili qualsiasi: ogni assegnazione determina una copia anziché la creazione di un riferimento. Si tratta di un comportamento non desiderabile quando si ha a che fare con gli oggetti, come risulta chiaro osservando il listato presentato più sotto: in PHP 4 $obj2 è una copia di $obj1, poiché la creazione di un alias avrebbe richiesto un assegnazione esplicita del tipo $obj2 &= $obj1.

<?php

$obj1 = new Num(6) ;

$obj2=$obj1 ;

//Metodo ipotetico: somma 4 a 6

$obj2->somma(4) ;

//In PHP 4 stampa 6, in PHP 5 stampa 10

echo($obj1->getVal()) ;

//Stampa 10

echo($obj2->getVal()) ;

?>

La gestione degli oggetti da parte di PHP 5 va esattamente nella direzione opposta: l'assegnazione di un oggetto ad un'altra variabile crea automaticamente un riferimento, mentre è la copia dell'oggetto a dover essere effettuata in maniera esplicita attraverso il metodo predefinito __clone().


<?php

$obj1 = new Num(6) ;

//Crea una copia dell'oggetto anzichè un alias

$obj2=$obj1->__clone() ;

//Metodo ipotetico: somma 4 a 6

$obj2->somma(4) ;


//Stampa 6 perchè il metodo somma() è stato

//chiamato su una copia e non sull'oggetto stesso


echo($obj1->getVal()) ;

//Stampa 10

echo($obj2->getVal()) ;

?>

Private, protected, public e la visibilità

Metodi e proprietà in PHP 4 sono sempre pubblici, il che significa che l'utilizzatore può accedere anche alle funzionalità interne di un oggetto violandone l'API (l'Application Program Interface alla quale facevamo riferimento nel glossario introduttivo). Questa assenza richiede una certa autodisciplina da parte del programmatore che si trovi a lavorare in team o comunque su librerie di classi realizzate da altri. Con l'introduzione dei modificatori "public", "protected" e "private", in PHP 5 è possibile decidere in quale modo un oggetto potrà essere utilizzato o una classe estesa, e nascondere il dettagli dell'implementazione dietro il guscio protettivo di un interfaccia pubblica.

La visibilità di default è "public" il che significa che non è necessario specificare questo modificatore. Chiunque può accedere liberamente a proprietà e metodi pubblici in quanto questi costituiscono l'API dell'oggetto. Se invece un membro è dichiarato "protected", soltanto i metodi definiti all'interno della stessa classe o in una delle classi discendenti potranno accedervi. "Private" è l'ultimo livello e il più restrittivo: consente l'accesso esclusivamente ai metodi dichiarati all'interno della classe stessa.

L'esempio successivo è piuttosto banale ma rende l'idea di come si possa gestire la visibilità di proprietà e metodi.


<?php

class VisibilityTest {

private $myVar;

public function __construct(){}

public function metodoPubblico() {

   echo("Questo è pubblico!rn");

}

protected function metodoProtetto() {

   /*

   giusto, la chiamata proviene dalla stessa classe

   */


   $this->metodoPrivato();

   echo("Questo è protetto!rn");

}

private function metodoPrivato() {

   $this->$myVar = 3;

   echo("Questo è privato! $this->myVar è {$this->$myVar}rn");

}

}

class VisibilityTest2 extends VisibilityTest {

public function __construct(){

                        

    parent::__construct() ;

                        

}

public function showMsg() {

   $this->metodoPubblico() ;


   /*

   Giusto, un metodo protetto è accessibile

   dalla classe discendente

   */


   $this->metodoProtetto() ;


   /*

   Sbagliato! Un metodo privato non è accessibile

   al di fuori della classe che lo ha definito

   */

   //$this->metodoPrivato() ;

}

}

$testObj = new VisibilityTest();

$testObj->metodoPubblico();


/*

Sbagliato! Tentativo di accesso a metodo protetto

all'esterno della definizione di una classe figlia

*/


//$testObj->metodoProtetto();


/*

Sbagliato

*/


//$testObj->metodoPrivato;

$testObj2 = new VisibilityTest2();

$testObj2->showMsg();

?>

Final

Un classe dichiarata "final" non può avere eredi, mentre quando questo modificatore è associato ad un membro impedisce l'"ovverride" (scavalcamento, sovrascrittura) di quest'ultimo da parte delle classi figlie.


<?php

final class FinalTest

{

   //codice

}

class FinalTest2

{

   final public function unMetodo()

   {

   

      //codice

   }

}


/*

Fatal error: non si può ereditare da una classe dichiarata final

*/


/*Decommenta per testare

class TestChild extends FinalTest

{

   //codice

}

class TestChild2 extends FinalTest2

{


   //

   //Cerca di fare l'ovverride ma genera un Fatal error,

   //non si può sovrascrivere un metodo dichiarato final.

   //


   public function unMetodo()

   {

   

      //codice

   }

}

*/

?>

Il modificatore "static" e i singleton

PHP 4 consente l'accesso statico (cioè indipendente dalla creazione di un oggetto) ai metodi di una classe attraverso la sintassi nomeClasse::metodo().

Le cose non cambiano molto con PHP 5 dove però viene introdotto il modificatore "static", valido anche per le proprietà dell'oggetto: un membro definito "static" è accessibile soltanto esternamente e, ad esempio, una proprietà dichiarata contemporaneamente "private" e "static" diviene di fatto una variabile di classe, legata cioè alla classe nel suo complesso e non alle diverse istanze. Le costanti di classe si comportano in modo simile alle variabili di classe, con la differenza che il loro valore non può essere modificato.


<?php

class StaticTest{

   Const TestConst = "Una costante" ;

   static public $testPubVar = "Una proprietà statica e pubblica" ;

   

   static private $testPrivVar = "Una proprietà statica e privata" ;

   //Vari metodi

}


/*

Tutto ok, stampa la stringa

*/


echo StaticTest::TestConst, "n" ;


/*

Tutto ok, stampa la stringa

*/

echo StaticTest::$testPubVar, "n" ;


/*

Fatal Error: anche una variabile statica, se privata, non è accessibile

dal'esterno della classe

*/


//echo StaticTest::$testPrivVar ;


/*

Non si può modificare una costante, dà errore

*/


//StaticTest::TestConst = "Costante modificata"


/*

Tutto ok modifica il contenuto della variabile

*/

StaticTest::$testPubVar = "Variabile di classe modificata"   ;

?>

Grazie a "static" è possibile controllare il numero di istanze che una classe può generare e anche implementare dei "singleton", cioè classi che possono avere una sola istanza: è una caratteristica che si rivela utile, ad esempio, quando un oggetto racchiude una connessione al database e non vogliamo che ve ne sia più d'una.


<?php

class TestSingleton{

    static private $istanza ;

   

    static private $num = 0 ;


    /*

    Metodo factory, restituisce sempre la stessa istanza della classe

    */


    public function metodoFactory(){

         

          if( !isset(TestSingleton::$istanza) ){

            

            TestSingleton::$istanza = new TestSingleton() ;

          }

         

          return(TestSingleton::$istanza) ;

    }

   

    /*

    Il costruttore, private, è accessibile

    soltanto dall'interno della classe

    */


    private function __construct(){

       TestSingleton::$num++ ;

    }

   

    private function __destruct(){

       TestSingleton::$num-- ;

    }

   

    /*

    Facendo l'overriding e rendendo private il metodo __clone ()

    impediamo anche che l'oggetto possa essere copiato.

    */


    private function __clone(){}

    static public function getNum(){

       return(TestSingleton::$num) ;

    }

   

    /*

    Innesca il distruttore

    */


    static public function eliminaIstanza(){

      

      TestSingleton::$istanza= NULL ;

    }

}


/*

Chiamiamo il metodo factory 3 volte

*/


$x = TestSingleton::metodoFactory() ;

$y = TestSingleton::metodoFactory() ;

$z = TestSingleton::metodoFactory() ;


//Il numero delle istanze è sempre 1


echo(TestSingleton::getNum()."rn") ;

//Eliminiamo tutti i riferimenti all'oggetto

unset($x) ;

unset($y) ;

unset($z) ;

TestSingleton::eliminaIstanza() ;

//Il numero delle istanze è 0

echo(TestSingleton::getNum()."rn") ;

?>

Dereferenziazione

PHP 4 non consente di dereferenziare direttamente gli oggetti restituiti da metodi o funzioni, invece con la nuova versione del linguaggio possiamo utilizzare una sintassi di questo tipo $oggetto->metodo()->metodo(): ci sono indubbi vantaggi dal punto di vista della chiarezza del codice ed è anche una sintassi più naturale quando ci interfacciamo a oggetti COM o Java.

Classi astratte

Una classe astratta non può essere istanziata: funge esclusivamente da modello che definisce metodi e proprietà per un certo numero di sotto-classi. Le classi astratte possono contenere anche metodi astratti, cioè metodi che non sono implementati nella classe in questione ma che devono obbligatoriamente essere implementati nelle sottoclassi.


<?php

abstract class Saluto{

   private $strSaluto ;


   /*

   Dichiara metodo astratto

   */


   abstract function mostraSaluto() ;

   

   /*

   Implementazione di due metodi concreti

   */

   protected function setSaluto($strSaluto){

      $this->strSaluto = $strSaluto ;

   }

   

   protected function getSaluto(){

      return($this->strSaluto) ;

   

   }

}

class SalutoConfidenziale extends Saluto{


   /*

   Inizializzazione

   */

   public function __construct(){

       $this->setSaluto('Ciao!') ;

   }

   

   /*

   È obbligatorio implementare il metodo astratto

   della classe parent altrimenti verrà generato un errore

   */

   public function mostraSaluto(){

                        

       echo( $this->getSaluto() ) ;

                        

   }

}

//Fatal error: Cannot instantiate abstract class saluto

//$x1= new Saluto() ;

$x2 = new SalutoConfidenziale() ;

$x2->mostraSaluto() ;

?>

Interfaccia

È un un tipo speciale di classe astratta nella quale tutti i metodi sono "public" e "abstract", stabilisce una regola alla quale l'API di tutte le classi figlie dovrà adeguarsi. Lo scopo è quello di rendere il codice più chiaro e consentire agli sviluppatori che lavorano in team di creare delle linee guida rigide a cui attenersi nelle varie fasi di sviluppo.

In questa panoramica non ci soffermeremo sulle interfacce in quanto valgono le regole già viste per le classi astratte, l'unica nota di rilievo riguarda la possibilità di ereditarietà multipla da più interfacce: una classe può ereditare contemporaneamente le API di interfacce differenti.


<?php


/*

Interfaccia: può contenere solo metodi pubblici e astratti

*/


interface TestInterface1

{

   function metodo1() ;

   

   function metodo2() ;

   

   function metodo3() ;

}

interface TestInterface2

{

   function metodo4() ;

   

   function metodo5() ;

   

   function metodo6() ;

}


/*

Una classe concreta che implementa entrambe le interfacce (ereditarietà multipla),

deve obbligatoriamente prevedere i metodi descritti nell'interfaccia

*/


class InterfaceChild implements TestInterface1, TestInterface2

{

   public function metodo1()

   {

   

      echo "Hello World!" ;

   

   }

   

   public function metodo2()

   {

   

      echo "Ciao mondo!" ;

   

   }

   

   public function metodo3()

   {

   

      echo "Hola mundo!" ;

   

   }

   

   public function metodo4()

   {

   

      echo "Questo è il metodo 4" ;

   

   }

   

   public function metodo5()

   {

   

       echo "Questo è il metodo 5" ;

   

   }

   

   public function metodo6()

   {

   

       echo "Questo è il metodo 6" ;

   

   }

}//END class

?>

Type Hinting

Pur rimanendo un linguaggio a tipizzazione debole e dinamica (al contrario di Java e C++), Php 5 introduce un'interessante caratteristica, chiamata "type hinting", che può essere utilizzata per richiedere che gli argomenti passati ad un determinato metodo siano (direttamente o indirettamente) istanze di una certa classe o interfaccia.


<?php

abstract class SalutoClass

{

   abstract function exec() ;

}

class ItSaluto extends SalutoClass

{

   function exec()

   {

   

      echo "Ciao mondo!" ;

   }

}

class EnSaluto extends SalutoClass

{

   function exec()

   {

   

      echo "Hello world!" ;

   }

}

class HintTest

{

   /*

   L'argomento deve essere obbligatoriamente un istanza di SalutoClass (rapporto is_a)

   */


   static function exec(SalutoClass $objSaluto)

   {

   

    $objSaluto->exec() ;

   

   }

}


/*

Istanziamo un oggetto da una delle classi ammesse

*/


$saluto = EnSaluto ;


/*

L'oggetto viene passato come argomento, se non si trattasse di un oggetto

derivato da SalutoClass il "type hinting" determina un fatal error

*/

//Stampa "Hello World!"

HintTest::exec($saluto) ;

?>

Intercettori

Gli intercettori sono metodi predefiniti che servono ad imbrigliare eventi particolari, ne abbiamo appena visto un paio, e sono __construct e __destruct(). Ne esistono tuttavia altri un po' meno intuitivi, come i metodi __set(), __get(), __call() ai quali presto dedicheremo un articolo apposito. Qui ci limitiamo a dire che il loro scopo è quello di favorire l'interazione tra gli oggetti, gestendo l'accesso a metodi e proprietà che non sono stati dichiarati in una classe.

Conclusioni

L'approccio di PHP 4 agli oggetti è estremamente lacunoso tuttavia, con l'impiego del linguaggio anche in progetti di dimensioni considerevoli, il ricorso a classi e patterns è divenuto una pratica comune che ha reso inevitabile l'evoluzione verso una maggiore conformità ai principi della programmazione a oggetti.

PHP 5 risponde a queste esigenze ma non ha trasformato Php in un linguaggio orientato agli oggetti puro (pensiamo a Java o a C#), inoltre è stato profuso un impegno notevole verso il mantenimento della retrocompatibilità, quindi chi lo desidera potrà tranquillamente continuare a programmare alla vecchia maniera. Comunque diverse caratteristiche non sono, e forse non lo saranno mai, disponibili, e così ad esempio non verranno supportati gli namespaces e l'ereditarietà multipla (se escludiamo il caso delle interfacce).

Siamo ben lontani dall'aver esaurito le numerose novità introdotte con la versione 5, molte meritano di essere approfondite in spazi appositi così presto ci occuperemo di eccezioni, iteratori, introspezione e streams.

Ti consigliamo anche