Dato che è stata introdotta successivamente, la programmazione ad oggetti in PHP4 può risultare limitata, soprattutto a chi
ha avuto modo di sperimentare questo paradigma di programmazione in altri ambiti come ad esempio Java.
L'elemento fondamentale della OOP è la classe, una classe contiene la definizione di dati (in terminologia Object Oriented: proprietà) e di funzioni (metodi) che condividono alcune caratteristiche tali da suggerirne l'incapsulamento in una singola struttura, la classe appunto.
La definizione di una classe utilizza l'istruzione class:
class la_mia_prima_classe
{
var $variabile;
function la_mia_prima_classe ($in)
{
$this->variabile = $in;
}
}
Le proprietà vengono definite tramite l'istruzione var seguite dal nome della variabile, successivamente abbiamo definito il metodo la_mia_prima_classe, si noti come questo metodo abbia lo stesso nome della classe, questo fatto lo identifica come costruttore della classe. Un costruttore di una classe viene invocato automaticamente dall'interprete PHP quando la classe viene istanziata, quando cioè viene creato un oggetto di quella classe.
All'interno del costruttore abbiamo assegnato il valore della variabile $in alla proprietà $variabile tramite il costrutto $this->variabile = $in;
, la variabile $this si riferisce all'oggetto corrente e può essere utilizzato solo all'interno di un metodo di una classe.
Sempre all'interno della definizione di una classe è possibile fare riferimento ai metodi utilizzando la sintassi $this->metodo
.
Una classe non può essere utilizzata direttamente dato che in effetti non è niente altro che una dichiarazione di proprietà e metodi che definiscono quindi un particolare tipo di dato. Quello che ci permette di utilizzare queste definizione è la creazione di un oggetto come istanza della particolare classe:
$oggetto = new la_mia_prima_classe("qualcosa in input");
print $obj->variabile;
L'operatore new
crea una nuova istanza di la_mia_prima_classe
e la assegna a $oggetto
.
Il codice quindi produrrà l'output:
qualcosa in input
I metodi della classe definita possono essere utilizzati sia dinamicamente cioè istanziando un oggetto (è il caso dell'esempio precedente) sia in modo statico cioè utilizzando la classe come un namespace. Vediamo un esempio:
class seconda_classe
{
var $variabile;
function seconda_classe ($in)
{
$this->$variabile = $in;
}
function moltiplica ($num1, $num2)
{
return $num1*$num2;
}
}
print seconda_classe::moltiplica(1234,4321);
utilizzando l'operatore ::
possiamo utilizzare staticamente il metodo (la funzione) moltiplica che abbiamo definito all'interno della classe.
Uno dei problemi che si riscontra più spesso utilizzando gli oggetti in PHP4 è dovuto al fatto che quando si passa un oggetto come argomento di una funzione, esso viene trattato esattamente come ogni altro tipo di dato, viene cioè passato per valore: l'interprete crea una copia dell'oggetto e la passa alla funzione,tale copia verrà distrutta altermine dell'esecuzione della funzione stessa.
Una delle prerogative della OOP è la ereditarietà: la possibilità cioè di definire una classe figlio che mutui dalla classe padre le proprietà e i metodi avendo la possibilità di aggiungerne di nuovi. In PHP4 l'ereditarietà viene attuata tramite l'estensione di una classe:
class padre
{
var $var_padre;
function padre ($in)
{
$this->$variabile = $in;
}
function moltiplica ($num)
{
return $this->var_padre * $num;
}
}
class figlio extends padre
{
var $var_figlio;
function figlio ($in)
{
$this->var_figlio = $in;
$this->var_padre = $in + 100;
}
}
$oggetto = new figlio (2);
print $oggetto->moltiplica(4);
come si può notare anche istanziando un oggetto di classe figlio possiamo utilizzare direttamente il metodo moltiplica
che avevamo definito nella classe padre.
Quando si renda necessario passare degli oggetti tra diverse pagine PHP è possibile serializzare gli oggetti cioè indicare all'interprete PHP di esportare le propietà attuali dell'oggetto, cosi da poterle passare come parametri GET o POST alla pagina successiva che potrà cosi "ricostruire" l'oggetto.
Le funzioni che possiamo utilizzare sono
serialize($oggetto)
e
unserialize($oggetto)
PHP rende inoltre disponibili due funzioni "magiche" (per "magiche" si intende che indicando il nome esatto della funzione l'interprete associerà un comportamento particolare):
__sleep()
e
__wakeup()
La prima (per "addormentare" l'oggetto) verrà eseguita ogni volta che un oggetto della classe viene serializzato, la seconda invece (quindi per "svegliare" l'oggetto) quando esso viene de-serializzato.
PHP5 introduce una vasta gamma di novita', sia nella logica della programmazione che nelle estensioni utilizzate:
-Nuovo modello ad oggetti: grazie alla nuova infrastruttura il PHP5 fornisce tutti gli strumenti per la programmazione ad oggetti. Le keyword public, private, protected per definire la visibilita' delle proprieta' degli oggetti, un costruttore standard __construct(), un distruttore standard __destructor(), introduzione delle interfacce, ecc.
-Metodi e proprieta' static: Il modello ad oggetti utilizza anche la proprieta' static per definire sia metodi che proprieta' di un oggetto come statici e quindi invocabili senza un'istanza della classe in cui sono definiti.
-Metodo __autoload() invocato ogni qual volta si tenta d'istanziare una classe non ancora definita.
-Paradigma try-throw-catch: in tutti i linguaggi evoluti esiste questo altrettanto evoluto paradigma di gestione specializzata di errori ed eccezioni.
-SPL: Standardizzazione delle librerie tramite l'utilizzo della libreria Standard PHP Library che implementa anche una nuova gestione degli iteratori (utilizzabili anche tramite riferimenti) per eliminare alcune limitazioni del PHP4.
-Argomenti delle funzioni: a differenza del PHP4, in PHP5 e' possibile definire un valore di default anche per gli argomenti delle funzioni passati come riferimenti.
-Standardizzazione XML: Migrazione verso le librerie libxml utilizzate dal parser DOM viste le notevoli prestazioni. Introdotto anche il parser con organizzazione ad albero SimpleXML.
-Nuovissima estensione MySQLi per sfruttare appieno le funzionalita' di MySQL 4.1 (nuovo protocollo, prepared statement, ecc).
-SQLite embedded in PHP5, senza la necessita' di un DBMS ma con ottime prestazioni per piccoli-medi progetti.
-Tidy, potente estensione per moltissime operazioni si file HTML (parsing, pulizia, riparazione, ecc.).
-Introspezione, ovvero la possibilita' di ottenere informazioni riguardanti lo script in esecuzione.
-Nuovo supporto per gli ambienti multi-threaded.
Riferimento alle variabili, alle funzioni, agli oggetti, ecc, tramite stringhe
Premessa: in linguaggio PHP, tutte le variabili, le funzioni, ecc, sono gestite tramite una tabella hash che utilizza come chiave il nome della variabile, della funzione o dell'oggetto (e a loro volta le proprieta' e i metodi dell'oggetto).
In PHP, proprio grazie alla gestione tramite la tabella hash per la gestione delle variabili, e' possibile utilizzare una particolare tecnica di riferimento per "indirizzo". Per esempio, analizzando il codice seguente si puo' vedere che sono state inizializzate 2 variabili:
$prima = "Questa e' la prima";
$seconda = "Questa e' la seconda";
Per accedere alle due variabili si puo' fare:
${"prima"} = $seconda;
Indicando tra parentesi graffe ( '{' e '}' ) il nome della variabile sotto forma di stringa ( ovvero con apice singolo ' o doppio " ) , quindi indicando la chiave da ricercare nella tabella hash delle variabili. Dopo l'esecuzione di questa istruzione la variabile "$prima" conterra' la stringa "Questa e' la seconda".
Vediamo il seguente codice:
$prima = "Sono la prima";
$riferimento = "prima";
echo $$riferimento;
Il codice produce in output la stringa
"Sono la prima"
questo perche' la variabile che si stampa non e' '$riferimento
', ma e' la variabile con chiave uguale al contenuto di '$riferimento
', ovvero '$prima
'. Questa tecnica e' identica alla precedente, ma permettendo di dinamicizzare l'accesso alle variabili.
Si puo' utilizzare la stessa tecnica di accesso anche per chiamare delle funzioni, vediamo un esempio che utilizza le due tecniche illustrate insieme:
function restituisci5()
//in questo linea viengono create le stringhe "restituisci5", "restituisci10", "restituisci15"
{
return 5;
}
function restituisci10()
{
return 10;
}
function restituisci15()
{
return 15;
}
$somma = 0;
$funzione="";
for( $i=5; $i<=15; $i=$i+5 )
{
$funzione = "restituisci".$i;
$somma = $somma + $funzione();
//$funzione() chiama la funzione definita con il nome contenuto in $funzione, quindi riferimento5, riferimento10 e riferimento15
}
echo $somma;
Questo spezzone di codice produce in output la somma "30", risultato delle operazioni:
- "$somma = 0 + 5
" nel primo ciclo
- "$somma = 5 + 10
" nel secondo ciclo
- "$somma = 15 + 15
" nel terzo e ultimo ciclo
La stessa cosa si puo' applicare agli oggetti e ai metodi degli oggetti, molto utili per esempio per caricare dinamicamente diiverse versioni di un oggetto come nell'esempio:
//qua vengono gestiti i dati
class HttpAPI {
var $lastAPI;
function HttpAPI()
{
$fp = fopen("http://myserver.api.responder/getData?username=myUser");
//qua vengono gestiti i dati
$lastAPI = $dataResult;
}
function getAPIData()
{
return $lastAPI;
}
}
class HttpAPI_2 {
var $lastAPI;
var $otherInternalVal;
function HttpsAPI_2()
{
$fp = fopen("https://my_other_server.api.responder2/getOtherData?login=myLogin");
$lastAPI = $dataResult;
}
function getAPIData()
{
return $lastAPI;
}
}
//In questo modo viene istanziato l'oggetto HttpAPI
$quali_API_devo_usare = "HttpAPI";
$obj = new $quali_API_devo_usare();
echo $obj->getAPIData();
//In questo modo viene istanziato l'oggetto HttpsAPI2
$quali_API_devo_usare = "HttpsAPI2";
$obj = new $quali_API_devo_usare();
echo $obj->getAPIData();
Negli script PHP dove viene definita una classe non e' possibile "rompere" la dichiarazione con i tag di script in questo modo:
class NuovaClasse{
?>
<?php
function NuovaClasse($str)
{
echo $str;
}
}
?>
E' pero' possibile e spesso molto utile inserire i tag di inizio/fine script all'interno dei metodi:
<?php
class NuovaClasse{
function NuovaClasse($str,$color)
{
?>
<font color="<?php echo $color; ?>">
<?php echo $str; ?>
</font>
<?php
}
}
?>
E' anche necessario fare attenzione ai nomi dei metodi. In particolare:
-E' sconsigliato definire i metodi con il nome delle funzioni base PHP (mail()
,print()
,var_dump()
,ecc...)
-E' sconsigliato definire i metodi con il nome che inizia con il doppio underscore ( '__' ) perche' viene riservato a funzioni "magiche"
-Non e' possibile definire metodi con i nomi '__sleep()
', '__wakeup
'.
-Non e' possibile definire un metodo 'stdClass' poiche' e' un nome riservato del motore Zend
In PHP non e' possibile inizializzare staticamente le proprieta' di un oggetto, se e' necessaria un' inizializzazione deve essere fatta nel costruttore come viene mostrato nell'esempio seguente. C'e' pero' un'eccezione a quanto detto, infatti e' possibile inizializzare staticamente gli array.
<?php
class NuovaClasse{
var $str='io sono statica'
var $arr=array("Questo", "e", "un", "array", "statico");
function NuovaClasse()
{
$this->PrintVars();
}
function PrintVars()
{
echo $str;
echo $arr[0]." ".$arr[1]." ".$arr[2]." ".$arr[3]." ".$arr[4] ;
}
}
?>
Questa porzione di codice produce in output:
Questo e un array statico
Il codice piu' corretto, per motivi di leggibilita', sarebbe comunque quello che inizializza tutte le prorpieta' nel costruttore come il seguente:
<?php
class NuovaClasse{
var $str;
var $arr;
function NuovaClasse()
{
$this->str='io sono statica'
$this->arr=array("Questo", "e", "un", "array", "statico");
$this->PrintVars();
}
function PrintVars()
{
echo $str;
echo $arr[0]." ".$arr[1]." ".$arr[2]." ".$arr[3]." ".$arr[4] ;
}
}
?>
Utilizzo delle variabili tramite riferimenti.
In PHP non esistono veri e propri puntatori, esiste pero' il modo di accedere dinamicamente alle variabili tramite il nome (argomento trattato siccessivamente) e tramite riferimento. Non si puo' ottenere un vero e priprio indirizzo della variabile, ma si crea un alias per una variabile. Nell'esempio seguente vediamo come:
$variabile = "Sono una variabile";
$alias_variabile = &$variabile;
A questo punto '$alias_variabile
' contiene il riferimento hash alla variabile '$variabile
', quindi la linea
echo $alias_variabile;
produce in output la stringa
"Sono una variabile"
Questa tecnica e' molto comoda per il passaggio di argomenti alle funzioni per rifermimento e non per valore.Per esempio invece di:
funzione($variabile);
si scrive
funzione(&$variabile);
Come utilizzare funzioni annidate in C.
Le funzioni annidate (nested functions) altro non sono che funzioni dichiarate all'interno di un'altra funzione.
Le nested functions non fanno parte dell' ANSI C, esse rappresentano esclusivamente un'estensione, relativamente utile, del GCC.
Per quanto concerne l'ambito di una funzione annidata possiamo dire che è prettamente locale, richiamarla esterna causerebbe un errore.
Dalla funzione padre (ovvero quella funzione ove è dichiarata la nested), la funzione nested erediterà tutte le variabili.
Graficamente l'impostazione è questa:
1. FUNZIONE PADRE
<--1.a VARIABILI--> [ int a ]
2. FUNZIONE ANNIDATA
<--2.a VARIABILI--> [ *-> a]
| return (...)
| exit
Concludiamo offrendo un esempio d'utilizzo di funzioni nested:
#include <stdio.h>
int main ( void )
{
int nested (int a, int b)
{
return a + b;
}
int uno = 5;
int due = 10;
fprintf(stdout, "%d + %d = %d\nLa funzione annidata funziona !\n", uno, due, nested(uno,due));
return 0;
}
Compiliamo ed osserviamo l'output:
$ gcc nested.c -o nested
$ ./nested
5 + 10 = 15
La funzione annidata funziona !