PHP

18. Gli Oggetti e l'Ereditarietà

Il concetto dell'ereditarietà, è uno dei più importanti della programmazione orientata agli oggetti, a cui si appoggiano altri metodi avanzati di programmazione come ad esempio il Polimorfismo o le Classi Astratte che tratteremo nei capitoli seguenti.

L'ereditarietà ci consente di creare delle classi (classi derivate o sottoclassi) basate su classi già esistenti (classi base o superclassi).

Un grande vantaggio è quello di poter riutilizzare il codice di una classe di base senza doverlo modificare.

L'ereditarietà ci consente quindi di scrivere del codice molto più flessibile, in quanto permette una generalizzazione molto più forte di un concetto, rendendo più facile descrivere una situazione di vita reale.

Pensate alla classe base come ad un oggetto che descrive un concetto generale, e pensate invece alle sottoclassi come ad una specializzazione di tale concetto, esteso mediante proprietà e metodi aggiuntivi.

Imparare a programmare utilizzando questi concetti è un passo fondamentale per costruire applicazioni di un certo livello.

Vediamo ora un esempio dove definiremo una classe base e di seguito due derivate (sottoclassi) attraverso la parola chiave extends.
Segue la definizione della classe madre (base) :

Animale.php

<?php

	class Animale // Classe Base
	{
		public $zampe;
		public $ordine;
		public $nome;

		public function Animale($z, $o, $n) // Costruttore
		{
			$this->zampe = $z;
			$this->ordine = $o;
			$this->nome = $n;
		}

		protected function stampaDati() // Metodo protetto : può essere richiamato solo dalle classi derivate
		{ echo $this->nome . " : Zampe = " . $this->zampe . " / Ordine = " . $this->ordine; }
	}

?>

Cane.php

<?php

	require_once("Animale.php");

	class Cane extends Animale // "Sottoclasse" o "Classe Derivata"
	{
		public function Cane() // Costruttore classe derivata
		{
			parent::Animale(4, "Vertebrati", "Cane"); // Chiamata al costruttore della classe madre

			/* oppure più generalizzato

			parent::__construct(4, "Vertebrati", "Cane"); */
		}

		public function stampaDati($suono)
		{
			echo "Faccio $suono perchè sono ";
			parent::stampaDati(); // Chiamata al metodo protetto della classe madre
		}
	}

?>

Gallina.php

<?php

	require_once("Animale.php");

	class Gallina extends Animale // "Sottoclasse" o "Classe Derivata"
	{
		public function Gallina() // Costruttore classe derivata
		{
			parent::Animale(2, "Vertebrati", "Gallina"); // Chiamata al costruttore della classe madre
		}

		public function stampaDati($suono)
		{
			echo "Faccio $suono perchè sono ";
			parent::stampaDati(); // Chiamata al metodo protetto della classe madre
		}
	}

?>

Nel codice soprastante abbiamo dichiarato una classe base "Animale" e due classi derivate da questa : "Cane" e "Gallina".

Il metodo stampaDati di Animale è dichiarato con il modificatore di accesso protected, ossia può essere utilizzato solo all'interno della classe madre Animale e all'interno di tutte le classi da essa derivate, quindi anche Cane e Gallina.

Per richiamare il costruttore della classe madre dalle figlie è sufficiente utilizzare parent:: invece di this, in questo modo accediamo direttamente alla classe base, allo stesso modo con cui self:: viene usato al posto di this.

Ora vediamo un frammento di codice dove utilizziamo le classi appena dichiarate :

test.php

<?php

	require_once("Cane.php");
	require_once("Gallina.php");

	$cane = new Cane();
	$cane->stampaDati("bau");

	echo "\n\n<br /><br />\n\n";

	$gallina = new Gallina();
	$gallina->stampaDati("chicchirichì");

?>

Il risultato ottenuto.

In questo modo possiamo ampliare e modificare "Animale" senza effettivamente modificarne la classe e quindi il file Animale.php.

Sarà sufficiente fare le modifiche necessarie direttamente alla classi derivate e, qualora si presentassero nuove esigenze, sarà possibile creare una nuova sottoclasse di Animale senza intaccare il funzionamento delle due derivate Cane e Gallina, o addirittura potremmo creare una nuova sottoclasse derivata da una delle sottoclassi di Animale come nell'esempio che segue.

Alano.php

<?php

	require_once("Cane.php");

	class Alano extends Cane
	{
		private $suono;

		public function Alano()
		{
			parent::Cane();
			$this->suono = "wuoff";
		}

		public function stampaDati()
		{
			parent::stampaDati($this->suono);
			echo " / Razza : Alano";
		}
	}

?>

test.php

<?php

	require_once("Alano.php");

	$cane = new Alano();
	$cane->stampaDati();

?>

Il nuovo risultato ottenuto.

Nota : PHP non supporta l'ereditarietà multipla, offrendoci come alternativa le Interfacce.

Funzioni variabili applicate agli Oggetti

Nell'ultima parte del capitolo sulle Funzioni, abbiamo parlato delle funzioni variabili.

Vedremo ora come è possibile applicare il medesimo concetto ai metodi di una classe.

<?php

	class OggettoInventato
	{
		function metodo1()
		{ echo "Ciao sono il primo metodo!<br />\n"; }

		function metodo2()
		{ echo "Ciao sono il secondo metodo!<br />\n"; }

		function metodo3()
		{ echo "Ciao sono il terzo metodo!<br />\n"; }
	}

	$obj = new OggettoInventato();

	$num = 3;
	$prefisso = "metodo";
	
	for ($i = 1; $i <= $num; $i++)
	{
		$funzione = $prefisso . $i;
		$obj->$funzione();
	}

?>

Il codice soprastante produce questo risultato.

Per richiamare i metodi con le variabili dall'interno della classe, è sufficiente sostituire $obj con $this.

<?php

	class OggettoInventato
	{
		function metodo1()
		{ echo "Ciao sono il primo metodo!<br />\n"; }

		function metodo2()
		{ echo "Ciao sono il secondo metodo!<br />\n"; }

		function metodo3()
		{ echo "Ciao sono il terzo metodo!<br />\n"; }

		function richiamaMetodi()
		{
			$num = 3;
			$prefisso = "metodo";
	
			for ($i = 1; $i <= $num; $i++)
			{
				$funzione = $prefisso . $i;
				$this->$funzione();
			}
		}
	}

	$obj = new OggettoInventato();
	$obj->richiamaMetodi();

?>

Il risultato ottenuto è il medesimo.

17. Oggetti e Costanti

PHP 5 ci consente di definire delle costanti all'interno delle classi.
L'utilizzo è simile alle costanti globali descritte nel nono capitolo di questa guida.

Potete pensare alle costanti delle classi, come a degli attributi statici che non possono però essere modificati una volta dichiarati e definiti.

Prendiamo nuovamente in esempio la vecchia classe "Colore" esaminandone un utilizzo con le costanti :

Colore.php

<?php

	class Colore
	{
		const ROSSO = "#FF0000";
		const VERDE = "#00FF00";
		const BLU = "#0000FF";

		static public function stampaRosso()
		{
			echo "<font color=\"" . self::ROSSO . "\">Il valore esadecimale del colore rosso è : ";
			echo self::ROSSO . "</font><br />\n";
		}
	}

?>

Come con le costanti globali è necessario omettere il simbolo del dollaro $ durante la dichiarazione.
I nomi delle costanti in PHP 5 sono sempre Case Sensitive, ed è buona norma scriverle tutte in maiuscolo per distinguerle immediatamente come costanti, anche se non è obbligatorio.

test.php

<?php

	require_once("Colore.php");

	echo Colore::ROSSO . "<br />\n";
	Colore::stampaRosso();

?>

test.php produce questo risultato.

16. Gli Oggetti e i Membri statici

Prima di introdurre il concetto dei membri statici, è necessario sapere che per utilizzare un membro di una classe, un attributo o un metodo, è obbligatorio creare un'istanza di tale classe attraverso la parola chiave new, come illustrato nel capitolo precedente.

$utente = new Persona("John", "Doe", "1-1-1970");

In questo modo, ogni istanza della classe avrà la propria copia di ogni attributo e ogni metodo.

Talvolta può risultare utile voler rendere accessibile un membro di una classe senza doverne inizializzare un'istanza, e questo è appunto possibile dichiarando il membro come statico.

Per capire meglio, prendiamo un esame un'ipotetica classe "Colore", che avrà una serie di colori di base che sarà utile rendere accessibili sempre, anche quando effettivamente non ci sarà utile creare un'istanza della suddetta classe.

Colore.php

<?php

	class Colore
	{
		static $rosso = "#FF0000";
		static $verde = "#00FF00";
		static $blu = "#0000FF";

		/*
			... altri attributi
		*/

		public function Colore()
		{ /* codice costruttore */ }

		/*
			... altri metodi
		*/

		static public function stampaColore($colore)
		{
			echo "<font color=\"" . Colore::${$colore} . "\">Il valore esadecimale del colore $colore è : ";
			echo Colore::${$colore} . "</font><br />\n";
		}
	}

?>

Ora che abbiamo dichiarato la nostra nuova classe Colore, vediamo come è possibile utilizzare i suoi 3 attributi statici.
E' sufficiente specificare il nome della classe seguito dai doppi due punti e il nome del membro :

test.php

<?php

	require_once("Colore.php");

	echo "<font color=\"" . Colore::$rosso . "\">Il valore esadecimale del colore rosso è : ";
	echo Colore::$rosso . "</font><br />\n";

	echo "<font color=\"" . Colore::$verde . "\">Il valore esadecimale del colore verde è : ";
	echo Colore::$verde . "</font><br />\n";

	echo "<font color=\"" . Colore::$blu . "\">Il valore esadecimale del colore blu è : ";
	echo Colore::$blu . "</font><br />\n";

?>

Richiamando la pagina test.php, otterremo questo risultato.

Se in un secondo momento decidiamo che per il nostro sito è più adatto un rosso scuro, sarà sufficiente assegnarvi tale valore all'inizio dello script, rendendo così effettiva la modifica in tutto il codice, senza peraltro modificare la classe che potrebbe essere condivisa con altre applicazioni anche esterne al server.

Nell'esempio che segue, stavolta utilizzeremo il metodo statico "stampaColore()" per visualizzare i colori sul browser.

test.php

<?php

	require_once("Colore.php");

	Colore::stampaColore("rosso");

	Colore::$rosso = "#C11C1C"; // Rosso più scuro

	Colore::stampaColore("rosso");

?>

L'esempio soprastante genera questo risultato.

Per accedere ai membri statici dall'interno della classe, $this non è adeguato e genera il seguente errore :
Fatal error: Using $this when not in object context in C:\AppServ\www\test\Colore.php

Il modo corretto per accedervi dall'interno è mediante il costrutto self :

<?php

	class Colore
	{
		static $rosso = "#FF0000";

		// ...

		static public function stampaColore($colore)
		{
			self::$rosso = "#FF0000";

			echo "<font color=\"" . Colore::${$colore} . "\">Il valore esadecimale del colore $colore è : ";
			echo Colore::${$colore} . "</font><br />\n";
		}
	}

?>

Concludo il capitolo con una nota per chi è già avvezzo con il concetto dell'ereditarietà.
Se accedete a un membro statico della classe madre all'interno della figlia, ricordatevi di sostituire self con parent.

15. Oggetti

Introdurrò ora uno dei concetti più importanti di PHP 5 : gli Oggetti.

La programmazione orientata agli oggetti è alla base di ogni applicazione solida e potente, e ci consentirà di scrivere del codice estremamente flessibile ed elastico, facendoci risparmiare talvolta anche ore ed ore di lavoro.

Possiamo pensare ad un oggetto come ad un tipo di dato più complesso e personalizzato, non esistente fra i tipi tradizionali di PHP, ma bensì ideato e creato da noi.

Lo scopo è quello di creare un tipo di dato più complesso, per soddisfare richieste che potremo comunque adempiere con i tipi tradizionali, ma con molta meno difficoltà e tempo.

Gli oggetti sono formati principalmente da attributi e metodi.

Gli attributi sono delle variabili proprietarie dell'oggetto, semplici o complesse, quindi anche Array o Oggetti.

I metodi invece, sono delle funzioni proprietarie dell'oggetto, e fra questi ce ne sono due molto importanti :
i Costruttori e i Distruttori.

Se non definirete un costruttore e un distruttore per la vostra classe (oggetto), PHP li rimpiazzerà con dei metodi propri di default, in quanto questi due metodi sono essenziali per il corretto fuzionamento della classe.

Il costruttore verrà chiamato automaticamente da PHP, ogni volta che verrà creato un oggetto (istanza), mentre il distruttore sarà chiamato quando l'oggetto verrà distrutto, ossia quando non esiste più alcun riferimento all'oggetto oppure alla fine dello script.

Per dichiarare un costruttore, è sufficiente creare una funzione all'interno della classe che abbia lo stesso nome di quest'ultima, oppure potete usare la parola chiave __construct().

Nell'esempio che segue creeremo un nuovo tipo di dato, l'oggetto "Persona", con gli attributi "nome", "cognome" e "data di nascita" e il metodo "stampaPersona()" per visualizzare i dati sul browser. Per creare la nuova classe utilizzeremo il costrutto del linguaggio class :


Persona.php

<?php

	class Persona
	{
		public $nome = ""; // attributo
		public $cognome = ""; // attributo
		public $datanascita = ""; // attributo

		public function Persona($n_nome, $n_cognome, $n_data) // costruttore
		{
			$this->nome = $n_nome;
			$this->cognome = $n_cognome;
			$this->datanascita = $n_data;
		}

		public function stampaPersona() // metodo
		{
			echo "Nome : " . $this->nome . "<br />\n";
			echo "Cognome : " . $this->cognome . "<br />\n";
			echo "Data di nascita : " . $this->datanascita . "<br />\n";
		}
	}

?>

Come potete notare il nome del file è uguale a quello della classe, così come lo è il nome del costruttore.
Il costruttore prende in input tre parametri ($n_nome, $n_cognome, $n_data) che andrà a memorizzare rispettivamente nei propri attributi ($nome, $cognome, $datanascita), a cui accederà tramite la parola chiave this.

Con this l'oggetto può richiamare i suoi attributi e metodi, in quanto this indica l'oggetto stesso.
Subito dopo this segue l'operatore di selezione -> che punta ad un determinato attributo o metodo alla sua destra, appartenente all'oggetto alla sua sinistra (this).

Ricordate sempre che in questi casi, il simbolo del dollaro $, va solo su this e non sul nome dell'attributo/metodo, a meno che non si stia usando il metodo delle variabili funzione, di cui discuteremo la variante per oggetti nei prossimi capitoli.

Infine troviamo il metodo stampaPersona() che semplicemente stampa gli attributi dell'oggetto tramite il costrutto echo.

La parola chiave public sarà illustrata nel prossimo capitolo assieme a protected e private.

Ora vedremo come creare ed utilizzare un'istanza dell'oggetto "Persona" dichiarato nella pagina precedente.

test.php

<?php require_once("Persona.php"); ?>
<html>

	<head>
		<title>Test</title>
	</head>

	<body>
	<?php

		$utente = new Persona("Mario", "Rossi", "10-01-1980");
		$utente->stampaPersona();

	?>

	</body>
</html>

Il codice soprastante produce questo risultato.
Per creare una nuova istanza della classe "Persona", abbiamo usato la parola chiave new seguita dal costruttore della classe con i rispettivi parametri.
A seguire richiamiamo il metodo "stampaPersona()" con l'operatore di selezione -> descritto in precedenza.

Osservando la classe nell'esempio della pagina precedente "Persona.php", possiamo notare che tutti gli attributi, il costruttore e il metodo, sono dichiarati con la parola chiave public.

Questo ci consente di usarli e richiamarli liberamente nello script attraverso un'istanza della suddetta classe. La parola chiave public è un modificatore di accesso, che serve a stabilire che tipo di restrizioni si devono avere lavorando su quel dato.

Public, Protected e Private

Di seguito una breve descrizione di public e le sue varianti protected e private :

  • public - I membri (attributi o metodi) della classe dichiarati public, possono essere utilizzati sia all'interno che all'esterno della classe madre e di quelle derivate da essa (ereditarietà) ($this->membro oppure $oggetto->membro)
  • protected - I membri dichiarati protected, possono essere utilizzati solo all'interno delle classi madri e derivate ($this->membro)
  • private - I membri dichiarati private, possono essere utilizzati solo all'interno della classe madre ($this->membro)

Tornando alla classe "Persona", poichè abbiamo dichiarato gli attributi come public, il linguaggio ci consente di agire direttamente su di essi, senza dover richiamare il costruttore ogni volta che dobbiamo modificare un attributo dell'istanza ($utente) :

$utente = new Persona("John", "Doe", "1-1-1970");

$utente->nome = "Santiago";
$utente->cognome = "Arnavisca";

$utente->stampaPersona(); // output = Nome : Santiago / Cognome : Arnavisca / Data di nascita : 1-1-1970

Se invece avessimo usato la restrizione private per gli attributi della classe "Persona", avremmo ricevuto dal server il seguente errore sollevato dalla riga $utente->nome = "Santiago";
Fatal error: Cannot access private property Persona::$nome in C:\AppServ\www\test\test.php

Avremmo ottenuto un errore equivalente anche utilizzando il modificatore di accesso protected, in quanto protected stabilisce che l'attributo o metodo così dichiarato, può essere utilizzato solo all'interno della classe, ma a differenza di private, ne consente l'utilizzo anche nelle classi derivate, argomento che tratteremo più avanti nel capitolo della Ereditarietà.

14. Librerie e file esterni

Costruendo delle vere applicazioni web in PHP, vi accorgerete che sarà impossibile sviluppare del codice ordinato e flessibile senza suddividere l'applicazione in più file o librerie.

Vedremo ora come è possibile creare una libreria esterna all'applicazione includendola in quest'ultima.
Per farlo è necessario creare un nuovo file, dove sarà possibile inserire variabili, funzioni, oggetti e tutto il resto come avviene in una normale pagina PHP.

Solitamente quando si crea un nuovo file di libreria, gli si assegna una estensione .inc che sta per include, ma in questo modo se non viene configurato correttamente il server, il codice diventa leggibile dall'esterno, creando problemi di sicurezza.

Vi potrà capitare di produrre del codice che non installerete voi, perciò vi consiglio in ogni caso, di dare l'estensione .php anche ai file di libreria, in modo che vengano sempre interpretati come tali dal server rendendo illegibile il codice dal lato client.


Include & Require

PHP ci mette a disposizione quattro costrutti del linguaggio, non richiamabili quindi con il metodo delle variabili funzione :

  • include() - Include il file specificato e solleva un Warning se non viene trovato.
  • require() - Include il file specificato e solleva un Fatal error se non viene trovato interrompendo l'esecuzione dello script.
  • include_once() - Include il file specificato durante l'esecuzione dello script e solleva un Warning se non viene trovato.
    Se il file è stato già incluso, include_once non lo includerà nuovamente.
  • require_once() - Include il file specificato durante l'esecuzione e solleva un Fatal error se non viene trovato interrompendo l'esecuzione dello script. Se il file è stato già incluso, require_once non lo includerà nuovamente.

L'unica differenza fra i metodi include e require, è la gestione degli errori.

Include infatti solleva un Warning che non interrompe l'esecuzione dello script, mentre require produce un Fatal error, non esitate quindi ad usare require se il file incluso è di vitale importanza per la corretta esecuzione dello script.


Esempio di file esterno

Creeremo ora un nuovo file con estensione .php, per contenere alcune variabili e funzioni che useremo nell'applicazione principale, e che chiameremo "libreria.php" (N.B. : fate attenzione all'estensione del file, se siete sotto Windows assicuratevi di avere disattivato l'opzione "Nascondi le estensioni per i tipi di file conosciuti" sotto "Strumenti" -> "Opzioni cartella..." -> "Visualizzazione").

<?php
	// libreria.php

	$settimana = array();
	array_push($settimana, "Domenica");
	array_push($settimana, "Lunedì");
	array_push($settimana, "Martedì");
	array_push($settimana, "Mercoledì");
	array_push($settimana, "Giovedì");
	array_push($settimana, "Venerdì");
	array_push($settimana, "Sabato");

	function stampaData()
	{
		global $settimana;

		$data = "Oggi è " . $settimana[date("w" ,time())];
		$data .= " " . date("d/m/Y - G:i", time());

		return $data;
	}
?>

Ora includiamo la nostra libreria tramite la direttiva require_once per richiamare la nuova funzione "stampaData()" :

<?php require_once("libreria.php"); ?>

<html>
	<head>
		<title>Test</title>
	</head>

	<body>

	<?php echo stampaData(); ?>
	</body>
</html>

Questo è il risultato ottenuto.

Ci sono due sintassi corrette per utilizzare questi costrutti :

  • require_once("libreria.php");
  • require_once "libreria.php";

Potete usarli all'interno dei blocchi condizionali come IF ELSE, con l'accorgimento di racchiuderli sempre fra parentesi graffe, anche quando c'è solo un'istruzione, o potreste avere effetti indesiderati :

// Esempio corretto
if ($condizione)
{ include "libreria1.php"; }
else
{ include "libreria2.php"; }

Inoltre, essendo dei costrutti speciali che non richiedono parentesi tonde, bisogna prestare particolare attenzione quando si effettuano dei controlli sui valori che restituiscono, avendo cura di usare la seguente sintassi :

// Non corretto
if (include('libreria.php') == 'success')
{ echo 'OK'; }
 
// Corretto
if ((include 'libreria.php') == 'success')
{ echo 'OK'; }

Come si intuisce dall'esempio precedente, è possibile gestire i valori restituiti da include e require, semplicemente utilizzando il costrutto return nel file che si desidera includere.
Per capire meglio riporto direttamente l'esempio della Documentazione Ufficiale di PHP :

<?php
	// file "return.php"

	$var = 'PHP';
	return $var;
?>

<?php
	// file "noreturn.php"

	$var = 'PHP';
?>

<?php
	// file "testreturns.php"

	$temp = include 'return.php';
	echo $temp; // Stampa : 'PHP'

	$temp = include 'noreturn.php';
	echo $temp; // Stampa : 1
?>

Se includete un file all'interno di una funzione, tutto il codice all'interno del file sarà come se fosse stato dichiarato e definito all'interno di questa funzione, di conseguenza le variabili non saranno accesibili dall'esterno a meno di non essere dichiarate come globali.

Condividi contenuti