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

La programmazione ad oggetti con PHP 4

Cos'è e come utilizzare le classi in PHP4
Cos'è e come utilizzare le classi in PHP4
Link copiato negli appunti

Programmazione ad oggetti o programmazione strutturale?

La scelta è ardua (almeno per quanto riguarda PHP) e la sfida durerà in eterno. Ognuno ha le sue preferenze, soprattutto dovute all'abitudine, e nessuno può obbligare qualcuno ad usare un metodo oppure un altro. La OOP è il punto di forza di molti linguaggi di programmazione, utilizzata soprattutto in progetti molto ampi che richiedono una struttura rigida e compatta ed un'organizzazione "ad albero" molto efficente. È scontato dire che la OOP, come del resto la programmazione procedurale, ha i sui pregi ed i suoi difetti, e non è questo il luogo per votare a favore o contro una di queste o per elencarne pregi e difetti.

Una precisazione: il problema è che la programmazione ad oggetti (OOP d'ora in avanti) non è supportata in modo molto "convincente" dalla versione attuale di PHP, e soprattutto sembra essere un argomento ostico per molti. L'articolo che mi sta accingendo a scrivere comprenderà una spiegazione teorica (nulla di difficile), ed un piccolo esempio pratico per capire la sintassi da utilizzare in PHP nella creazione di una classe. Premetto che quanto scritto servirà solo fino alla release ufficiale di PHP5, che comprenderà un supporto molto più completo all'OOP e delle piccole variazioni nella sintassi.

Cominciamo con la teoria

Per capire cosa sia la OOP e come utilizzarla nei nosti script è fondamentale comprendere i seguenti concetti:

  • CLASSE: cominciamo con il concetto fondamentale. Cos'è una classe?? Una classe altro non è che un modello astratto per diversi oggetti che hanno caratteristiche simili, ed incorpora le caratteristiche di una famiglia particolare di oggetti. Il tutto è molto stringato (di fatti si tratta solamente di un articolo), ma capirete molto meglio seguendo l'esempio.
  • ISTANZA/OGGETTO: istanza ed oggetto sono sinonomi. Mentre una classe è la rappresentazione generale dell'oggetto, un'istanza ne è la rappresentazione effettiva, concreta.
  • EREDITARIETA': l'ereditarietà è uno dei concetti più importanti legati alla programmazione ad oggetti, ed ha un effetto immediato sulla struttura e sulla stesura delle applicazioni. È un meccanismo molto potente, che permette di specificare, nella definizione di una classe, solo le differenze rispetto ad un classe già esistente; l'ereditarietà da accesso alle informazioni relative a quest'ultima. L'ereditarietà, solitamente, ordina in una rigida ma efficente gerarchia le classi. Chiameremo superclasse la classe padre dalla quale una sottoclasse erediterà metodi e proprietà.
  • VARIABILE DI ISTANZA e VARIABILI DI CLASSE: la differenza è minima ma importante. Le variabili di classe definiscono le proprietà statiche ed inalterabili (questo non è vero per PHP, purtroppo) di un classe, mentre le variabili di istanza servono a definire le proprietà particolari di ogni oggetto (solitamente è compito del costruttore la loro inizializzazione).
  • METODO: un metodo è una funzione che rappresenta una "capacità" di una classe e quindi delle sue istanze e delle istanze delle sue eventuali sottoclassi. Nulla di più. Esitono metodi particolari che descriveremo in seguito.
  • COSTRUTTORE/DISTRUTTORE: un costruttore è una funzione che viene chiamata durante la creazione di un'istanza da un classe, e, normalmente, serve a definirne lo stato. Un costruttore non è obbligatorio. PHP non ha, invece, distruttori, funzioni chiamate quando un oggetto viene distrutto, quando cioè viene effettivamente eliminato dalla memoria, solitamente quando non vi sono più riferimenti.

La nostra prima classe

Appresi questi concetti possiamo passare all'analisi di una classe. Scrivo di seguito il listato completo, passerò successivamente a spiegarvi il concetto che sta alla base di questa classe ed il modo in cui utilizzarla:

class essereVivente{
var $specie;
var $ambiente;
//...ne avrei potute aggiungere a migliaia...

function essereVivente($specie,$ambiente){
$this->specie = $specie;
$this->ambiente = $ambiente;
}

function muori(){
echo "se continueranno a morire esseri della mia specie (".$this->specie.") ci estingueremon";
}

function doveVivo(){
echo "vivo in".$this->ambiente;
}
//anche di metodi ne potrei aggiungere a migliaia...
}

class animale extends essereVivente{

var $peso;
var $altezza;
var $gasRespirato = "ossigeno";
var $arti;

function animale($specie,$ambiente,$peso,$altezza,$arti){
$this->essereVivente($specie,$ambiente);
$this->specie = $specie;
$this->peso = $peso;
$this->altezza = $altezza;
$this->arti = $arti;
echo "Sono nato!!";
}

function respira(){
echo "Sono un essere vivente, e stò respirando ".$this->gasRespirato;
}

function salta($spazio){
if($this->altezza * 5 > $spazio){
echo "Ho saltato ".$spazio." metri";
}else{
echo "Al massimo posso saltare ".($this->altezza*5)." metri!";
}
}
}

class essereUmano extends animale{

var $lingua;
var $sport;
var $nome;
var $eta;
var $sesso;

function essereUmano($nome,$sesso,$ambiente,$peso,$altezza,$eta,$lingua,$sport){
$this->animale("Essere umano",$ambiente,$peso,$altezza,4);
$this->lingua = $lingua;
$this->eta = $eta;
$this->sport = $sport;
$this->nome = $nome;
$this->sesso = $sesso;
}

function guida(){
if($this->eta < 18){
echo "Non sono neanche maggiorenne, come posso guidare??";
}else{
echo "Segno della croce e via!!Anche se non ho ancora preso la patente!";
}
}

function parla($frase){
echo $frase."nSe vuoi te la dirò anche in ".$this->lingua;
}
}

class donna extends essereUmano{

function donna($nome,$ambiente,$altezza,$lingua){
$this->essereUmano($nome,"femmina",$ambiente,"fruscello",$altezza,"una giovincella",$lingua,"nessuno");
}

function guida(){
echo "3 morti e 15 feriti...";
}

function pensa(){
echo "Scusa?Che hai detto??";
}

function maQuantiAnniHai(){
echo $this->eta;
}
}

class uomo extends essereUmano{

function uomo($nome,$ambiente,$peso,$altezza,$eta,$lingua,$sport){
$this->essereUmano($nome,"maschio",$ambiente,$peso,$altezza,$eta,$lingua,$sport);
}

function pensa(){
echo "Sono troppo un genio, che penso a fare??";
}

function gioca(){
echo "Stò già giocando a ".$this->sport." !";
}
}

Utilizzo della classe

Bene...copiate queste classi completamente inutili su un documento PHP e visualizzatelo: la pagina dovrebbe essere completamente vuota. Questo perchè avete creato la CLASSE, nessuna ISTANZA, quindi tutto ciò che è presente sul documento è solamente il modello astratto (ricordate?), nulla di "reale" ed attivo. Le classi che ho creato sopra rappresentano in modo molto semplice e stringato quello che potrebbe essere un albero delle specie viventi.

Abbiamo la classe essereVivente che definisce alcuni tratti comuni tipici delle varie specie, siano queste animali, vegetali o cos'altro. Una seconda classe animale(che eredita dalla prima) che definisce proprietà tipiche di un solo ramo degli esseri viventi: quello appunto degli animali. La classe essereUmano eredita da animale, ed anch'essa definisce delle caratteristiche generali per un essere umano, tipiche di entrambi i sessi. Le ultime due classi, donna e uomo, definiscono in modo astratto l'essere umano uomo e l'essere umano donna, e per questo motivo ereditano dalla classe essereUmano. Il tutto può essere riassunto nello schema seguente (che vi consiglio di preparare ogni volta che decidete di sviluppare un complesso sistema di classi):

essereVivente
     |
     |____ animale
              |
              |____ essereUmano
                         |
                         |____ donna
                         |
                         |____ uomo

Lo schema precedente definisce la struttura del nostro pseudoprogramma. Guardando velocemente questo grafico, possiamo capire che, in caso volessimo utilizzare la classe donna nelle nostre applicazioni, questa avrà TUTTI i metodi e le proprietà delle classi essereVivente,animale e essereUmano (le sue superclassi, ricordate?), ma nulla in comune con la classe uomo. Passiamo ora all'analisi line-by-line del codice.

Riga 1:

class essereVivente{

questa è la sintassi minima per la creazione di una classe. Di seguito trovate un piccolo schemino che rappresenta la sintassi corretta (tra parentesi quadre la parte opzionale):

class nome_classe [extends nome_superclasse]{
}

Come potete notare, non è obbligatorio che una classe abbia una superclasse, ma in caso avvenga, la superclasse può essere una sola. Infatti PHP, come il più blasonato Java, non supporta l'ereditarietà multipla (punto di forza di C++ e Python), che permette di ereditare metodi e proprietà da più genitori. Notiamo che nome_classe segue le stesse regole per la definizione di un nome di una funzione o di una variabile (qundi niente numerini all'inizio ecc...), e non può assumere il nome di una funzione già definita.

Righe 2-3

var $specie;
var $ambiente;

Qui, prima di definire il costruttore o ogni altra funzione, definiamo le variabili di classe o di istanza. Notate che, differentemente da altri linguaggi di programmazione, PHP permette di creare solo variabili pubbliche, a cui vi si può accedere in lettura ed in scrittura d qualsiasi parte dello script. La sintassi corretta per la definizione di una variabile è la seguente:

var nome_variabile [= valore];

Come potete vedere, è obbligatorio porre la "parolina" var PRIMA di scrivere il nome della vostra variabile, pena un errore di debug. Il valore non è obbligatorio, questo perchè, in questo modo, è possibile differenziare (virtualmente, non praticamente per ora) le variabili di istanza (quelle senza valore, che saranno inizializzate in seguito) e le variabili di classe (quelle con un valore iniziale che, in teoria, non dovrebbe essere variato). Notiamo che le variabili possono assumere (in caso siano di classe, quindi inizializzate direttamente nella definizione) solo i seguenti tipi di valore:

  • numerico (1, 100, 10.001, ecc...)
  • stringa ("1", "ciao", "casc casmcsa clacsòlmcòasc", ecc...)
  • array (array(), array("chiave" => valore,...), array(valore,valore,...), ecc...)
  • costanti (PHP_OS, ecc...)

Quindi non possiamo fare riferimento a variabili globali, non possiamo scrivere espressioni del tipo:

//ERRATO!
var $bella_o_no = (($GLOBALS["misura_seno"]+$_POST["misura_culo"]-$_SERVER["misura_fianchi"])/2 == $_SERVER["misura_fianchi"]);
//ERRATO!

Non possiamo neppure utilizzare funzioni (nè user defined, nè globali) e, cosa da tenere molto in considerazione, non possiamo istanziare nuove classi (per chi si sta chiedendo: "come si istanzia una classe??" veda più avanti).

Riga 6:

function essereVivente($specie,$ambiente){

Come potete notare, questa non è altro che la definizione di una normalissima funzione. Infatti, se ci troviamo all'interno della definizione di una classe, tutte le funzioni definite (che seguono le normali regole di definizione di una funzione in PHP) diventano automaticamente metodi della classe. È inutile che vi scriva la sintassi corretta di una funzione, ma in questa riga c'è da tenere conto ancora di un altro fattore: il nome della funzione è lo stesso del nome della classe che lo contiene. Non è un errore, bensì è il modo in cui si definisce un costruttore in PHP.

Ricordo, per i neofiti provenienti dall'oscuro mondo Java, che in PHP NON è attualemente possibile eseguire l'overloading dei metodi. Ricordate quindi: l'unica cosa che differenzia un metodo da un costruttore, è che quest'ultimo DEVE avere il nome della classe nel quale è definito. Il costruttore non restituisce nulla, e può essere anche ommesso.

Righe 7-8:

$this->specie = $specie;
$this->ambiente = $ambiente;

In queste 2 righe di codice è racchiusa un'informazione molto importante: abbiamo fatto riferimento alle varibili $specie ed $ambiente (che erano variabili di istanza, ma anche se fossero state varibili di classe non avrebbe fatto differenza) in modo un po particolare:

$this->nome_var_senza_simbolo_dollaro

D'ora in poi, infilatevi bene in testa che l'unico modo per riferirsi ad una variabile di classe o di istanza, e quindi l'unico modo di modificare un oggetto, è quello di anteporre al nome della variabile senza $ il prefisso $this->.

Il significato di "->" ve lo spiegherò più avanti. Per ora vi basti sapere questo. Oltretutto è fondamentale sapere che $this-> va anteposto anche a nomi di funzioni definite all'interno della classe stessa, in caso volessimo utilizzarle da un'altra parte, sempre nella stessa classe. L'uso di $this si limita a dire a PHP di riferirsi alla variabile di QUESTA (this) classe, non ad un'altra variabile presente nel blocco di codice. Quindi scrivere:

$this->variabile = $variabile;

è perfettamente lecito. Infatti questo comando assegna il valore della variabile locale $variabile, alla variabile di classe o di istanza omonima.

Altri punti fondamentali della classe

Righe 21-35:

class animale extends essereVivente{

var $peso;
var $altezza;
var $gasRespirato = "ossigeno";
var $arti;

function animale($specie,$ambiente,$peso,$altezza,$arti){
$this->essereVivente($specie,$ambiente);
$this->specie = $specie;
$this->peso = $peso;
$this->altezza = $altezza;
$this->arti = $arti;
echo "Sono nato!!";
}

Questo è un'esempio di classe estesa. A parte farvi vedere la sintassi, mi interessava farvi notare che, all'interno del costruttore della sottoclasse, abbiamo una chiamata al costruttore della superclasse. Questo è obbligatorio se nella sottoclasse abbiamo un costruttore, altrimenti l'ereditarietà non avrebbe effetto alcuno sulla sottoclasse. In caso non si abbia un costruttore, consiglio comunque caldamente di inserirlo solo per richiamare il costruttore della superclasse; infatti,nel primo caso (no costruttore), un'istanza della classe figlio sarebbe considerata un'istanza della classe padre, mentre nel secondo caso (sì costruttori) otterresti due ID differenti.

Righe 87-89:

function guida(){
echo "3 morti e 15 feriti...";
}

Da queste righe dovreste notare una cosa: guida() è una funzione definita ANCHE nella superclasse della classe in cui si trova (la classe donna). Qui vi è una seconda definizione. Darà un errore?? Fate una prova, e poi guardate il risultato. Non dà errore: difatti l'operazione di definire metodi con nome identico a metodi della superclasse è chiamata ridefinizione, e permette di cambiare il comportamento di un metodo in base all'oggetto dal quale è stato creato. In questo caso una chiamata a guida() stamperebbe "3 morti e 15 feriti", e non "Segno della croce e via!!Anche se non ho ancora preso la patente!", come era stato definito nella superclasse essereUmano.

La teoria è stata lunga, due accenni alla pratica

Di seguito, vi spiegherò come istanziare una classe e come lavorarci. Il tutto è molto semplice, anche se andrebbero fatte delle precisazione sulla creazione di istanze per riferimento che mi risparmierò perchè vanno fuori dal contesto dell'articolo . Una classe si istanzia nel seguente modo:

nome_variabile = new nome_classe[(arg1,...,argN)];

nel caso pratico:

$miaNonna = new donna("Isabella","campagna","1.60","dialetto milanese");

Come vedete non c'è nulla di complesso a parte una cosa che potrebbe risultare poco chiara: nel caso in cui la classe da istanziare NON abbia costruttore, le parentesi tonde possono essere inserite vuote (senza parametri attuali), o possono essere ommesse. Questione di stile. Io consiglio la seconda.

Per fare riferimento alla variabili di una classe o ai suoi metodi, useremmo la seguente struttura:

nome_istanza->nome_variabile;
nome_istanza->nome_metodo([arg1,...,argN]);

La chiamata ad un metodo necessita dell'utilizzo delle parentesi tonde, che NON possono essere ommesse. Nel nosto caso particolare avremo:

//facciamo guidare mia nonna!!
$miaNonna->guida();
//Stampiamo il suo peso:
echo $miaNonna->peso;

Il tutto non mi sembra affatto complesso...dateci un'occhiata e capirete subito come utilizzare il simbolo "->". Una precisazione: in caso una variabile di classe (o istanza) contenga un'istanza di un'altro oggetto, per riferirci alle variabili o metodi di quest'ultimo faremo:

istanza_1->var_contente_istanza2->metodo_o_var_di_istanza2

Naturalmente il tutto può essere nidificato, ed utilizzato anche all'interno della classe usando $this al posto del nome della prima istanza.

Se si vuole fare riferimento ad un metodo di una classe che non è ancora stata istanziata, seguite la sintassi seguente:

nome_classe::nome_metodo([arg1, ... ,argN])

Un esempio con la nostra classe non ha molto senso, comunque dovreste aver capito correttamente come funziona il tutto. Normalmente questa sintassi viene usata in caso si vogliano richiamare delle funzioni definite all'interno di classi particolari, che hanno il solo scopo di racchiudere dentro di loro un grupo di metodi con stesso campo di applicazione. Nulla vi vieta di utilizzarli in altri ambiti. Chiarimenti, se neccessari, li aggiungerò in seguito. NON potete accedere alle variabili usando il costrutto con "::".

L'uso di parent

parent::nome_metodo([arg1, ..., arg2])

serve per permettere, all'interno della definizione di una classe, di riferirsi a metodi dalla superclasse direttamente, senza che questa sia stata istanziata nel costruttore. Usare parent: è un caloroso consiglio ma non un obbligo; infatti potete riferirvi direttamente ai metodi della superclasse con nome_super_classe::metodo.

Consiglio caldamente tutti coloro che fossero interessati ad iniziare a darci sotto con la OOP in PHP di scaricarsi PHP-GTK e fare delle prove con lui: qui sarete obbligati ad usarle. Allego una piccola classe PHP-GTK di esempio con esecuzione compresa. Riuscite a commentarmela correttamente line-by-line (i neofiti naturalmente, NON i secchioni...) ?

<?php

class GtkApplication{
var $os;
var $mainWindow;

function GtkApplication(){

if(!class_exists('gtk')){
$this->os = PHP_OS;
if(strstr(strtoupper($this->os),'WIN')){
dl('php_gtk.dll');
}else{
dl('php_gtk.so');
}
}

}

function setMainWindow($mainWindow = false){

if(!$mainWindow){
print 'You must specify a valid GtkWindow istance as setMainWindow argument';
return false;
}else{
$this->mainWindow = $mainWindow;
return true;
}
}

function deleteEvent(){
//print 'Event deleted';
return false;
}

function destroy(){
//print 'Exit';
Gtk::main_quit();
return true;
}

function mainLoop(){
$this->mainWindow->connect('destroy',array($this,'destroy'));
$this->mainWindow->connect('delete-event',array($this,'deleteEvent'));
$this->mainWindow->show_all();
Gtk::main();
}
}

/*TEST*/
//YOU MUST ISTANCE GtkApplication AFTER istancing or extending from Gtk* classes
// ^^^^^

$myApp = new GtkApplication();
class mainWin extends GtkWindow{
function mainWin($name = '',$border = 10,$dimensions = false,$pos = GTK_WIN_POS_CENTER){
$this->GtkWindow();
$this->set_border_width($border);
$this->set_title($name);
$this->set_position($pos);
if($dimensions){
$this->set_usize($dimensions[0],$dimensions[1]);
}
}
}

$main = new mainWin('prova');
$myApp->setMainWindow($main);
$myApp->mainLoop();
?>

Conclusione

Prima di chiudere ricordo che, nella programmazione ad oggetti, è fondamentale svolgere il lavoro nel modo più ordinato possibile. Strutturate bene il vostro programma usando degli schemi appositi su un foglio oppure usando UML, in modo da avere un quadro generale del funzionamento dell'intera struttura. Solo allora potrete iniziare a scrivere codice. In questo modo vi eviterete lavoro inutile successivamente, ve lo garantisco, ed avrete la possibilità di avere sottomano, anche in futuro, lo schema della struttura del vostro programma, con notevoli vantaggi sia per voi che per altre persone che desiderino modificare il vostro prodotto.

Attendendo PHP5, vi saluto e vi auguro buon lavoro. Se ci saranno novità nella programmazione ad oggetti in PHP, sarò pronto ad informarvi.

Ti consigliamo anche