Una classe è una collezione di variabili e funzioni che utilizzano le variabili definite all'interno della classe stessa per svolgere determinate azioni. Di seguito è riportato il codice semplificato per definire una classe:
| <?php class nome_classe { var $nome_variabile; // DEFINIZIONI VARIABILI var ... function nome_funzione ( $var1, $var2, ... ) // DEFINIZIONI FUNZIONI { ... } } ?> |
Un esempio di classe è il seguente:
| <?php class Cart { var $items; // Articoli nel carrello // Aggiunge $num articoli di $artnr nel carrello function add_item ($artnr, $num) { $this->items[$artnr] += $num; } // Prende $num articoli di $artnr e li rimuove dal carrello function remove_item ($artnr, $num) { if ($this->items[$artnr] > $num) { $this->items[$artnr] -= $num; return true; } else { return false; } } } ?> |
Il codice definisce una classe chiamata Cart composta da un array associativo che archivia gli articoli nel carrello e due funzioni per aggiungere e rimuovere gli articoli dal carrello stesso.
Avvertimento: NON spezzate una definizione di classe in più file o in più blocchi PHP. Il seguente codice non funziona:
| <?php class test { ?> <?php function test() { print 'OK'; } } ?> |
In PHP 4, sono permesse inizializzazioni di variabili con valori costanti solamente grazie all'uso di var. Per inizializzare variabili con valori non costanti, bisogna creare una funzione di inizializzazione che viene eseguita automaticamente durante la fase di istanza della classe stessa. Questo tipo di funzione si chiama costruttore.
| <?php /* questo non funziona in PHP 4. */ class Cart { var $todays_date = date("Y-m-d"); var $name = $firstname; var $owner = 'Fred ' . 'Jones'; var $items = array("VCR", "TV"); } /* Questo è corretto. */ class Cart { var $todays_date; // Queste sono le PROPERTIES var $name; var $owner; var $items; function Cart() // Questo è il COSTRUTTORE { $this->todays_date = date("Y-m-d"); $this->name = $GLOBALS['firstname']; /* etc ... */ } } ?> |
Si definisce Property di una classe la variabile chiamata con il var all'inizio della classe stessa, ed avente un nome qualsiasi.
Istanziare una classe significa assegnare la classe ad una variabile usando la funzione natia new. Per creare quindi una variabile oggetto si usa l'operatore new.
I costruttori sono funzioni che esistono in una classe e che sono chiamate automaticamente quando si crea una nuova istanza di una classe con new. In PHP 3, una funzione si transforma in in un costruttore quando ha lo stesso nome di una classe. In PHP 4, una funzione diventa un costruttore quando ha lo stesso nome di una classe ed è definita all'interno della classe stessa; la differenza è sottile, ma cruciale.
| <?php class Cart2 { var $items; function add_item ($artnr, $num) { $this->items[$artnr] += $num; } function remove_item ($artnr, $num) { if ($this->items[$artnr] > $num) { $this->items[$artnr] -= $num; return true; } else { return false; } } } $cart2 = new Cart2; $cart2->add_item("10", 1); $another_cart2 = new Cart2; $another_cart2->add_item("0815", 3); print_r($cart2); print_r($another_cart2); ?> |
Il codice sopra, genera gli oggetti $cart2 e $another_cart2, dalla classe Cart2. La funzione add_item() dell'oggetto $cart2 è chiamata per aggiungere una ricorrenza dell'articolo numero 10 a $cart2 (una sorta di carrello spesa). Ad $another_cart2 sono aggiunte 3 ricorrenze dell'articolo numero 0815.
L'output è:
cart2 Object ( [items] => Array ( [10] => 1 ) )
cart2 Object ( [items] => Array ( [0815] => 3 ) )
Il separatore del pathname nome_oggetto / funzione_classe è ->.
$cart2->items e $another_cart2->items sono due diverse variabili che differiscono per il nome.
Si noti che la variabile si chiama $cart2->items, e non $cart2->$items, questo perchè le variabili in PHP si scrivono con un unico simbolo di dollaro.
Per poter accedere alle funzioni e alle variabili interne di una classe si usa la pseudo-variabile $this che può essere letta come 'la mia\il mio' o 'di questo oggetto'.
Spesso si ha bisogno di avere classi con variabili e funzioni simili ad altre classi. É buona norma definire una classe in modo generico, sia per poterla riutilizzare spesso, sia per poterla adattare a scopi specifici. Per facilitare questa operazione, è possibile generare classi per estensione di altre classi. Una classe estesa o derivata ha tutte le variabili e le funzioni della classe di base (questo fenomeno è chiamato 'eredità') più tutto ciò che viene aggiunto dall'estensione.
Non è possibile che una sottoclasse, ridefinisca variabili e funzioni di una classe madre.
Una classe estesa dipende sempre da una singola classe di base: l'eredità multipla non è supportata. Le classi si estendono usando la parola chiave 'extends'.
| <?php class Named_Cart extends Cart2 { var $owner; function set_owner ($name) { $this->owner = $name; } } $ncart = new Named_Cart; // Crea un carrello con nome $ncart->set_owner("kris"); // Assegna il nome al carrello print $ncart->owner; // stampa il nome del proprietario $ncart->add_item("10", 1); // (funzionalità ereditata da Cart) ?> |
Qui viene definita una classe Named_Cart che ha tutte le funzioni e variabili di Cart2 più la variabile $owner e la funzione set_owner(). Viene creato un carrello con nome con il metodo usato in precedenza, in più la classe estesa permette di settare o leggere il nome del carrello.
La relazione mostrata è chiamata relazione "genitore-figlio". Si crea una classe di base, poi utilizzando extends si crea una nuova classe basata sulla classe genitore: la classe figlia. Successivamente si può usare la classe figlia come classe base per un'altra classe.
A volte è utile riferirsi alle funzioni ed alle variabili di classi base o riferirsi alle funzioni di classi senza istanziarle. L'operatore :: è usato per questi scopi.
| <?php class A { function example() { echo "Sono la funzione originale A::example()"; } } class B extends A { function example() { echo "Sono la funzione ridefinita B::example()"; A::example(); } } // non viene istanziato nessun oggetto dalla classe A. // ma il codice stampa // Sono la funzione originale A::example() A::example(); // crea un oggetto dalla classe B. $b = new B; // questo codice stampa // Sono la funzione ridefinita B::example() // Sono la funzione originale A::example() $b->example(); ?> |
L'esempio chiama la funzione example() della classe A, ma senza creare un'istanza di A, di modo che la funzione non si possa richiamare con $a->example(). example() è chiamata come 'funzione della classe', e non come funzione di un oggetto della classe.
Si possono usare funzioni della classe, ma non le variabili della classe. Infatti, non esiste nessun oggetto nel momento della chiamata della funzione. Quindi, la funzione della classe non può usare le variabili dell'oggetto (ma può usare le variabili locali e globali) e $this non può essere usato. Nel suddetto esempio, la classe B ridefinisce la funzione example(). La funzione originale definita nella classe A è adombrata e non più disponibile, a meno che voi non chiamiate esplicitamente con l'operatore ::, scrivendo A::example() per richiamare la funzione.
E' possibile ritrovarsi a scrivere classi con codice che si riferisce a variabili e funzioni di classi base. Ciò è particolarmente VERO se una classe derivata è un perfezionamento o una specializzazione di una classe base.
Invece di usare il nome letterale della classe, bisognerebbe usare il nome speciale parent, che si riferisce al nome della classe base definita nella dichiarazione di extends. Usando questo metodo, si evita di usare il nome della classe base nel codice scritto. Se l'albero di eredità cambiasse durante lo sviluppo della classe, il cambiamento si ridurrebbe semplicemente alla modifica della dichiarazione extends della classe.
| <?php class A { function example() { echo "Sono la funzione originale A::example()"; } } class B extends A { function example() { echo "Sono B::example() e fornisco una funzionalità aggiuntiva."; parent::example(); } } $b = new B; // Il codice chiama B::example(), che a sua volta chiama A::example(). $b->example(); ?> |
La visibiltà di una property o di un metodo, può essere definita prefissando la loro dichiarazione con una parola chiave: "public", "protected" o "private". Public dichiara un metodo o una variabile che può essere vista da tutti, Protected invece limita l'accesso alla classe che la definisce ed alle sue sottoclassi, mentre Private consente l'accesso alla sola classe che la definisce.
Esempio:
| <?php class MyClass { public $public = 'Public'; protected $protected = 'Protected'; private $private = 'Private'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj = new MyClass(); echo $obj->public; // Funziona echo $obj->protected; // va in Errore echo $obj->private; // va in Errore $obj->printHello(); // Mostra Public, Protected e Private class MyClass2 extends MyClass { // Si può ridichiarare i metodi public protected, ma non quello privato protected $protected = 'Protected2'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj2 = new MyClass2(); echo $obj->public; // Funziona echo $obj2->private; // Indefinita echo $obj2->protected; // va in Errore $obj2->printHello(); // Mostra Public, Protected2, e non Private ?> |
Nell'esempio seguente, invece, viene mostrato lo stesso concetto di visibilità applicato però ai metodi di una classe:
| <?php class MyClass { // il Costruttore deve essere public public function __construct() { } // dichiarazione di un metodo public public function MyPublic() { } // dichiarazione di un metodo protected protected function MyProtected() { } // dichiarazione di un metodo private private function MyPrivate() { } // questo è public function Foo() { $this->MyPublic(); $this->MyProtected(); $this->MyPrivate(); } } $myclass = new MyClass; $myclass->MyPublic(); // Funziona $myclass->MyProtected(); // va in Errore $myclass->MyPrivate(); // va in Errore $myclass->Foo(); // Public, Protected e Private funzionano class MyClass2 extends MyClass { // questo è public function Foo2() { $this->MyPublic(); $this->MyProtected(); $this->MyPrivate(); // va in Errore } } $myclass2 = new MyClass2; $myclass2->MyPublic(); // Funziona $myclass2->Foo2(); // Public e Protected funzionano, ma non Private ?> |
Di seguito è riportato un esempio di variabile statica:
| <?php class foo { static $my_static = 5; public $my_prop = "asd"; } print foo::$my_static; $obj = new foo; print $obj->my_prop; ?> |
La differenza quindi tra una variabile statica ed una non, è che è possibile accederci senza istanziare la classe, ma usando i :: .
Di seguito, invece, è mostrato un esempio di metodo statico, la cui definizione è la stessa:
| <?php class foo { public static function my_static_method() { ... } } print foo::$my_static; $obj = new foo; print $obj->my_prop; ?> |
La gestione delle Eccezioni non è presente in PHP 4, ma è presente nella versione 5, che introduce tale concetto nello stesso modo di quello presente ad esempio in Java. Viene supportata la clausola "catch all" ma non la "finally". L'eccezione può essere gestita anche nei blocchi catch, esattamente come in Java; ma se questa non viene gestita in alcun modo, l'applicazione va in eccezione. Di seguito è riportato il codice php necessario per gestire una generica eccezione:
| <?php try { $error = 'throw questo errore'; throw new Exception($error); // Il codice che segue ad una eccezione non viene eseguito. echo 'Mai eseguita!'; } catch (Exception $e) { echo 'Eccezione trovata: ', $e->getMessage(), "\n"; } // Continua l'esecuzione dell'applicazione echo 'Hello World'; ?> |
Il blocco Try viene usato per gestire parte del codice php dell'applicazione che potrebbe generare un'eccezione, che viene però riportata e quindi gestita dal blocco seguente catch attraverso la parola chiave throw; in genere il blocco catch printa il tipo di errore. Nel blocco try, le istruzioni che seguono la riga del throw, non vengono eseguite nel caso in cui si genera un'eccezione.
E' sempre possibile definire una calsse madre Exception contenente metodi generici per la gestione di errori:
| <?php class Exception { protected $message = 'Unknown exception'; // exception message protected $code = 0; // user defined exception code protected $file; // source filename of exception protected $line; // source line of exception function __construct($message = null, $code = 0); final function getMessage(); // message of exception final function getCode(); // code of exception final function getFile(); // source filename final function getLine(); // source line final function getTrace(); // an array of the backtrace() final function getTraceAsString(); // formated string of trace /* Overrideable */ function __toString(); // formated string for display } ?> |
ed una classe figlia, MyException, che estenda quella madre:
| <?php class MyException extends Exception { // Redefine the exception so message isn't optional public function __construct($message, $code = 0) { // some code // make sure everything is assigned properly parent::__construct($message, $code); } // custom string representation of object */ public function __toString() { return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; } public function customFunction() { echo "A Custom function for this type of exception\n"; } } ?> |
Di seguito è riportato il codice di una classe esempio per testare le eccezioni di cui sopra:
| <?php class TestException { public $var; const THROW_NONE = 0; const THROW_CUSTOM = 1; const THROW_DEFAULT = 2; function __construct($avalue = self::THROW_NONE) { switch ($avalue) { case self::THROW_CUSTOM: // throw custom exception throw new MyException('1 is an invalid parameter', 5); break; case self::THROW_DEFAULT: // throw default one. throw new Exception('2 isnt allowed as a parameter', 6); break; default: // No exception, object will be created. $this->var = $avalue; break; } } } // Example 1 try { $o = new TestException(TestException::THROW_CUSTOM); } catch (MyException $e) { // Will be caught echo "Caught my exception\n", $e; $e->customFunction(); } catch (Exception $e) { // Skipped echo "Caught Default Exception\n", $e; } // Continue execution var_dump($o); echo "\n\n"; // Example 2 try { $o = new TestException(TestException::THROW_DEFAULT); } catch (MyException $e) { // Doesn't match this type echo "Caught my exception\n", $e; $e->customFunction(); } catch (Exception $e) { // Will be caught echo "Caught Default Exception\n", $e; } // Continue execution var_dump($o); echo "\n\n"; // Example 3 try { $o = new TestException(TestException::THROW_CUSTOM); } catch (Exception $e) { // Will be caught echo "Default Exception caught\n", $e; } // Continue execution var_dump($o); echo "\n\n"; // Example 4 try { $o = new TestException(); } catch (Exception $e) { // Skipped, no exception echo "Default Exception caught\n", $e; } // Continue execution var_dump($o); echo "\n\n"; ?> |