back to top

PHP e la Programmazione Orientata agli Oggetti

1. PHP e la programmazione orientata agli Oggetti

Con la vesione 5 di PHP gli sviluppatori hanno rivolto particolare attenzione al supporto della programmazione per oggetti (object oriented o OOP); questa metodologia era infatti prevista solo parzialmente per il comunque utilizzatissimo PHP 4. L’approccio per oggetti rappresenta un’importante novità che in un futuro non molto lontano potrebbe rivoluzionare il nostro modo di produrre codice per la generazione dinamica di contenuti (scripting server side).

Quando digitiamo il listato per uno script, utilizzare la metodologia per oggetti impone di suddividere una determinata problematica in parti differenti e tra loro indipendenti, vengono quindi stabiliti degli oggetti, si potrà poi intervenire su uno solo o solamente su alcuni di essi senza il rischio di compromettere la struttura generale dell’applicativo.

La OOP permette quindi interventi veloci e mirati sul codice: al presentarsi di un nuovo problema (ad esempio, la necessità di un aggiornamento), non dovremo modificare sostanzialmente l’impianto di uno script, basterà lavorare sui singoli oggetti interessati dalla modifica. Ciò non vuol dire che gli oggetti siano tra loro entità isolate e non comunicanti, anzi, il vantaggio della OOP stà proprio nella capacità di interagire che essi possiedono pur conservando lo status di entità univoche.

Fino a PHP4, la concezione del codice si è basata principalmente sul paradigma procedurale, una metodologia fondata sul raggruppamento di parti di uno script ripetute in frammenti di listato (procedure) riutilizzabili e richiamabili ogni volta a seconda della necessità. Le prcedure si configurano quindi come dei sottoprogrammi destinati a svolgere particolari funzioni, come ad esempio il controllo su un’occorrenza o un’operazione matematica, queste funzioni, sempre valide, potranno essere invocate nel codice restante.

Con PHP5, la scrittura del codice può basarsi su un nuovo approccio, nelle OOP infatti il listato non ruota attorno alle procedure ma ai dati stessi che dovranno essere elaborati tramite lo script.

Se volessimo definire l’essenza di un oggetto, potremo identificarlo come una particolare entità di tipo logico dotata di un’identità univoca (non ripetibile), che contiene nello stesso tempo le informazioni su cui il codice dovrà intervenire e le metodologie attraverso le quali s’interverrà sulle informazioni contenute.

Troppo complesso? Soltanto in apparenza! In realtà anche noi nella nostra vita quotidiana siamo circondati di "oggetti"; per esempio, un orologio viene riconosciuto come tale in quanto possiede componenti e funzioni che lo rendono uno strumento utile per misurare il tempo, anche costruendolo nelle forme più strane esso sarà per noi comunque un orologio in quanto la sua destinazione finale non andrà a mutare.

Abbiamo detto in precedenza che gli oggetti sono univoci, essi infatti posseggono un nome che dovrà essere unico nel complesso dello script di cui andrà a far parte; potrebbe sorgere nuovamente il dubbio sull’oggetto concepito quale realtà isolata, ma ciò non è vero in quanto gli oggetti recanti caratteristiche similari possono essere raggruppati all’interno di classi.

Per classi intendiamo definire delle tipologie di dato a cui fanno capo gli oggetti della medesima natura; le classi definisco per i loro oggetti due elementi fondamentali:

  1. attributi, o anche proprietà, che hanno lo scopo di rappresentare le informazioni salvate all’interno dello spazio di memoria da essi contenuto;
  2. metodi, una rappresentazione delle funzioni con cui un oggetto può intervenire sugli attributi.

Semplificando quanto appena esposto, possiamo definire una classe come un insieme di oggetti; questi ultimi, di conseguenza, potranno essere definiti come istanze della classe di appartenenza. Si istanzia un oggetto quando viene assegnata ad una determinata variabile una porzione di memoria in cui devono essere allocati gli attributi e i metodi relativi ad una classe specifica. Gli oggetti che vengono definiti all’interno di una medesima classe sono uguali tra loro per attributi e metodi, si differenziano però tramite il nome e il contenuto in informazioni degli attributi.

Mentre un oggetto "esiste" nel momento in cui esso viene definito imponendogli un nome, una classe non è mai un’entità reale ma semplicemente un modello di riferimento per riconoscere e classificare oggetti simili che fanno capo ad essa. Questo concetto, molto importante, può essere chiarito con un riferimento alla metodologia procedurale: una variabile viene definita nel momento in cui è valorizzata, potremo valorizzarla con una stringa di caratteri, e sarà una variabile stringa, potremo valorizzarla con un valore numerico, e sarà una variabile numerica. Quindi tra un oggetto e una classe abbiamo lo stesso rapporto che si prefigura tra una variabile e il relativo tipo di dato.

2. Incapsulazione, ereditarietà e polimorfismo

La programmazione orientata agli oggetti prevede l’esistenza di tre componenti fondamentali che caratterizzano profondamente ogni aspetto di questa metodologia differenziandola dall’approccio procedurale, stiamo parlando di: incapsulazione, ereditarietà e polimorfismo. Parliamo di concetti estranei a chi fino ad ora non ha mai digitato listati secondo la logica OOP, cercheremo quindi di analizzarli singlarmente provando a coglierne le specificità.

Il concetto di incapsulazione si ricollega alla possibilità di rendere "invisibili" in sede di scrittura del codice determinati dettagli ricollegati allo sviluppo di una classe specifica; il risultato ottenuto sarà quindi quello di impedire l’accesso ai dettagli interessati da parte di porzioni di codice esterni non ricompresi nell’insieme in oggetto. Dal punto di vista degli oggetti, in virtù del fatto che questi ultimi interagiscono fra loro tramite i messaggi e i metodi, il loro interscambio sarà reso più agevole dalla possibilità di celare alcuni dettagli; infatti, al momento della stesura del listato non si avrà la necessità di conoscere come un certo oggetto è stato realizzato, basterà avere a disposizione i metodi e le proprietà di cui è portatore. Quindi, tramite l’incapsulazione abbiamo la possibiltà di creare scripts più "leggeri" in termini di numero di righe di codice.

L’incapsulazione, crea una gerarchia tra i soggetti utilizzatori in grado di accedere all’applicativo. Ad esempio, sarà possibile celare gli attributi relativi agli oggetti di una classe permettendo all’utente unicamente l’accesso ai metodi. Per fare un esempio, pensiamo ad una sala giochi, chiunque può entrare e divertirsi con i videogames attraverso le modalità di gioco proposte, pagando ne avrà il pieno utilizzo ma non potrà certo modificare alcun gioco secondo le sue preferenze. Quando vengono celati gli attributi degli oggetti di una classe, i metodi appartenenti ad essa sono rappresentati per mezzo di un’interfaccia; con il termine interfaccia intendiamo un collegamento che funge da mediatore tra gli oggetti con lo scopo di trasferire dati da un oggeto all’altro; per mezzo di questo medium l’utilizzatore avrà la possibilità di operare con le proprietà sulla base dei metodi disponibili.

Per quanto riguarda il concetto di ereditarità, con essa si configura un processo mirato alla creazione di gerarchie interne alle classi, grazie ad esso saranno maggiori le opportunità di riutilizzare porzioni del listato alla base di un applicativo. Per mezzo dell’ereditarietà, potremo concepire una classe principale o generale, che chiameremo classe base oppure superclasse, con la funzione di definire le particolarità di uno specifico insieme di oggetti. La classe generale potrà quindi essere ereditata da delle classi secondarie o sottoclassi, chiamate anche classi derivate, che incrementeranno la classe base tramite i loro specifici elementi. Qunidi, a loro volta, le classi secondarie rappresenteranno delle superclassi per quelle successive protraendo la scala gerarchica.

Una classe derivata, "figlia" della principale, incorpora per definizione ogni informazione e ogni proprietà della classe base ereditata e, questo avviene senza perdere i propri metodi e i propri attributi; il meccanismo descritto comporta non pochi vantaggi, infatti, sulla base dell’ereditarietà della OOP potremo definire una classe specificando semplicemente le differenze di quest’ultima con un’altra classe.

Quello dell’ereditarietà è un concetto abbastanza semplice da comprendere in quanto si basa sull’emulazione della realtà che ci circonda, infatti, nella vita quotidiana abbiamo numerosi esempi riguardanti il funzionamento di questo meccanismo. Si pensi ad una classe chiamata animali, essa si trasfomerà in una classe base quando le classi derivate mammiferi e rettili erediteranno da essa le proprietà e i metodi mantenendo le proprie peculiarità; mammiferi e rettili saranno pur sempre degli animali in quanto dotati di caratteristiche similari che li rendono sottoclassi della medesima classe base, ciò non di meno preserveranno specificità in grado di renderli distinguibili tra loro. Procedendo nella scala gerachica, le due classi secondarie potranno suddividersi al loro interno in ulteriori derivate, ad esempio, in felini e bovini, in serpenti e lucertole, da cui tigri e bisonti, cobra e …

Passiamo ora all’ultimo concetto base dell’OOP, il cosiddetto polimorfismo che è strettamente collegato alle dinamiche proprie dell’ereditarietà; con il polimorfismo è possibile originare comportamenti e risultati diversi impiegando i medesimi metodi a carico di oggetti differenti. Il meccanismo polimorfico, è quindi fondato sulla possibilità di ridefinire in delle classi derivate comportamenti dati da uno o più metodi ereditati da una classe base. Potremmo così dare origine a metodi in grado di operare su oggetti di natura differente, sarà quindi compito dell’ambiente in cui funziona l’applicativo decidere quale metodo utilizzare "preferendolo" ad un altro sulla base delle azioni da svolgere sui differenti oggetti.

Il meccanismo del polimorfismo diventa palese nei processi di overloading, in questi ultimi infatti le classi derivate ridefiniscono metodi predefiniti nella classe base. Quando verrà utilizzato il metodo sottoposto a ridefinizione, quello prorio della superclasse verrà ignorato; con l’overloading, posto un insieme di classi in relazione gerarchica, per ciascuna di esse saremo in grado di impiegare diversamente un metodo presente in altre classi.

3. Definizione di una classe e Information Hiding

Per definizione di una classe intendiamo l’atto di indicare quali saranno le caratteristiche e quali i comportamenti riferiti ad un determinato insieme di oggetti. Doveremo innanzitutto denominare la classe da chiamare in causa, dopo di chè potremo imporre durante la stesura del codice dello scripts le possibilità di accesso ad attributi e metodi attraverso delle regole; stiamo parlando delle regole di visibilità note nella OOP come Information Hiding. Abbiamo fondamentalmente tre regole specificabili:

  1. public per cui la disponibilità di un membro sarà estesa all’intero applicativo;
  2. private per cui la disponibilità di un membro verrà limitata alla sola classe in cui esso è definito;
  3. protected per cui la disponibilità di un membro sarà limitata alla classe in cui viene definito e alle classi da essa derivate.

Naturalmente, con il termine "membro" intendiamo indicare le proprietà e i metodi relativi ad un oggetto. Sarà poi possibile svolgere un’ulteriore operazione, cioè definire un costruttore e un distruttore dell’oggetto stesso. Tramite un costruttore, che introdurremo con la sintassi __construct(), verrà inizializzata la struttura dei dati, esso verrà richiamato automaticamente nel momento della creazione di un oggetto; il distruttore, che introdurremo con la sintassi __destruct(), avrà come obbiettivo la distruzione della struttura di dati e sarà richiamato all’atto della distruzione di un oggetto.

Quindi potremo definire una classe utilizzando la seguente sintassi:

class Nome_della_classe
{
  dichiarazione delle proprietà

  introduzione del costruttore e del distruttore

  dichiarazione e definizione dei metodi
}

Passiamo ora ad un esempio pratico di definizione di una classe:

<?php
/*definizione di una classe e Information Hiding*/

//denominiamo la classe
class Animali 
{
  //regole di visibilità
  public $cane = "Il cane mangia il gatto.<br />\n";
  public $gatto = "Il gatto mangia il topo .<br />\n";
  private $topo = "Povero topo.<br />\n";

  //introduciamo una funzione
  function show() 
  {
    $this->cane;
    $this->gatto;
    $this->topo;
  }
}

//istanza dell'oggetto
$obj = new Animali();

//stampa a video
echo $obj->cane; 
echo $obj->gatto; 
echo $obj->topo; 
?>

L’esempio proposto può essere testato tramite browser una volta salvato in un file ".php" denominato a piacere e inserito nella root del Web server: il costrutto "class" indica la fase iniziale della dichiarazione della classe a cui viene dato un nome; vengono quindi indicati gli attributi, i metodi e l’oggetto viene istanziato in modo da poter operare sugli attributi tramite i metodi.

Come si ha modo di notare, sono state introdotte le regole di visibilita:

public $cane = "Il cane mangia il gatto.<br />\n";
public $gatto = "Il gatto mangia il topo .<br />\n";
private $topo = "Povero topo.<br />\n";.

Una menzione finale và fatta per il costrutto this, si tratta di una keyword utilizzata esclusivamente all’interno di una classe, con essa si indica un oggetto che ha invocato un metodo.

4. Costruttori e distruttori degli oggetti

Nell’articolo precedente abbiamo introdotto i concetti di costruttore e distruttore. Per mezzo di un costruttore (sintassi: __construct()), inizializzeremo una struttura di dati, esso entrerà in gioco alla creazione di un oggetto; invece, il distruttore (sintassi: __destruct()), presiede alla distruzione di una struttura di dati e viene richiamato per la distruzione di un oggetto.

E’ utile proporre alcuni esempi pratici di listato in cui vengono introdotti costruttore e distruttore:

<?php
/*il costruttore*/

//definizione di una superclasse
class Mammiferi
{
  //sintassi del costruttore
  function __construct() 
  {
    echo "Costruttore nella classe base.<br />\n";
  }
}

//Ereditarietà: definizione di una sottoclasse
class Felini extends Mammiferi 
{
  function __construct() 
  {
    echo "Costruttore nella classe derivata.<br />\n";
  }
}

//istanza
$obj = new Mammiferi();
$obj = new Felini();
?>

Quindi, il costruttore è stato introdotto subito dopo la dichiarazione della classe e viene richiamato al momento dell’istanzia di un oggetto. Nel codice è inoltre possibile notare la sintassi utile a produrre il meccanismo dell’ereditarietà.

Passiamo ora al distruttore:

<?php
/*il distruttore*/

//definizione della classe
class Killer 
{
  //costruttore
  function __construct() 
  {
    $this->obj = "oggetto";
    echo "Costruisci l'" . $this->obj . ".<br>\n";
  }

  //distruttore
  function __destruct()
  {
    echo "Distruggi l'" . $this->obj . ".<br>\n";
  }
}

//istanza
$obj = new Killer();
?>

Nel listato di costruzione e distruzione, per la classe denominata "Killer" abbiamo dichiarato e istanziato un oggetto associabile alla variabile $obj; è stato quindi richiamato il costruttore della classe per la stampa a video della stringa desiderata. Una volta eseguita l’operazione per cui il codice è stato creato, possiamo rilasciare le risorse impiegate dall’oggetto, dovrà essere quindi richiamato il distruttore.

5. PHP e MySQL, approccio procedurale e per oggetti

Nel corso di questa breve serie di articoli abbiamo descritto i vantaggi propri della metodologia per oggetti; in particolare la OOP ci consente di scrivere codice "snello", portabile e semplice da modificare.

La possibilità di intervenire sul dato, invece che sulla procedura di manipolazione del dato stesso, ci permette di operare aggiornamenti e correzioni senza stravolgere i nostri listati in senso strutturale.

Sia ben chiaro però che la programmazione per oggetti, pur rappresentando un’evoluzione rispetto alle precedure, non rappresenta nè una scorciatoia per lo sviluppatore nè un sistema da ritenersi in assoluto "migliore" per la scrittura del codice. E’ uno strumento in più che richiede un nuovo approoccio mentale allo scripting server side in PHP, inutile illudersi che con la OOP si possa creare con poche righe un applicativo che prima richiedeva pagine e pagine di listato.

Sulla scorta di quanto esposto finora presentiamo al lettore una problematica, quella relativa alla connessione con il DBMS MySQL e selezione di un database, affrontata sia secondo la logica procedurale che con l’ausilio dell’approccio per oggetti.

Logica procedurale:

<?php
/*Connessione a MySQL con selezione del database 
motodologia procedurale*/

$host = 'localhost';
$user = 'user';
$pass = 'password';
$db = 'archivio';
@mysql_connect($host, $user, $pass);
@mysql_select_db($db);

.........codice..........
?>

Logica per oggetti:

<?php
/*Connessione a MySQL con selezione del database
motodologia per oggetti*/

class mysql
{
  function mysql()
  {
    $this->host = 'localhost';
    $this->user = 'user';
    $this->pass = 'password';
    $this->db = 'archivio';
    $this->connessione();
  }

  function connessione()
  {
    $this->conn_sel = @mysql_connect($this->host, $this->user, $this->pass);
    @mysql_select_db($this->db);
  }
}

if (new mysql())
{
.........codice..........
} 
?>

Come si avrà facilmente modo di notare, le logiche seguite nelle due metodologia si differenziano sostanzialmente. Nel primo caso (logica procedurale) seguiamo il noto meccanismo basato sulla valorizzazione di variabili e passaggio delle stesse alle funzioni (procedura); nel secondo caso (logica OOP) variabili e funzioni entrano a far parte di una classe che li ricomprende come oggetti da istanziare al momento del loro utilizzo.

Altri contenuti interessanti

Pubblicitร 
Claudio Garau
Claudio Garau
Web developer, programmatore, Database Administrator, Linux Admin, docente e copywriter specializzato in contenuti sulle tecnologie orientate a Web, mobile, Cybersecurity e Digital Marketing per sviluppatori, PA e imprese.

Leggi anche...

Cannot modify header information – headers already sent: come risolvere l’errore PHP

L'errore di PHP cannot modify header information - headers...

Ricavare l’estensione di un file con PHP

Quando si lavora con i file in un'applicazione web,...

GD Library: creazione, manipolazione e ridimensionamento immagini con PHP

Le librerie GD (o GD Library), sono componenti fondamentali...

PHP: impostare il fuso orario italiano

Le tue pagine PHP non mostrano lโ€™orario corretto? Probabilmente...

5 script PHP per gestire BBCode

A volte può aversi l'esigenza di dover offrire agli...
Pubblicitร