Advertisement
  1. Code
  2. Coding Fundamentals
  3. OOP

PHP orientato agli oggetti per principianti

Scroll to top
Read Time: 34 min

() translation by (you can also view the original English article)

Per molti programmatori PHP la programmazione orientata agli oggetti è un concetto che spaventa, pieno di sintassi complicata e di altri ostacoli. Come ho scritto nei dettagli nel mio libro, Pro PHP and jQuery  (in inglese), imparerete i concetti dietro la programmazione orientata agli oggetti (in inglese object-oriented programming, OOP), uno stile di scrittura di codice dove si raggruppano azioni collegate in classi per poter creare codice più compatto e più efficiente.

Capire la programmazione orientata agli oggetti

La programmazione orientata agli oggetti è uno stile di scrittura di codice che permette agli sviluppatori di raggruppare compiti simili in classi. Questo aiuta a far sì che il codice segua il principio "non ripeterti" (in inglese don't repeat yourself, DRY) e sia facile da manutenere.

"La programmazione orientata agli oggetti è uno stile di scrittura di codice che permette agli sviluppatori di raggruppare compiti simili in classi".

Uno dei maggiori vantaggi del principio di programmazione DRY è che, se cambia un pezzo di informazione nel vostro programma, di solito è necessaria una sola modifica per aggiornare il codice. Uno dei più grandi incubi per gli sviluppatori è manutenere codice dove i dati sono dichiarati mille volte, il che vuol dire che ogni modifica al programma diventa un gioco infinitamente più frustrante di Dov'è Wally? per cercare le funzionalità e i dati duplicati.

L'OOP può intimidire molti sviluppatori, perché introduce sintassi nuova e, a un primo sguardo, appare ben più complessa del semplice codice procedurale, o riga per riga. Comunque, se la si guarda più da vicino, l'OOP è in realtà un approccio molto immediato e sostanzialmente più semplice alla programmazione.

Capire gli oggetti e le classi

Prima di andare troppo a fondo nelle sottigliezze dell'OOP è necessaria una comprensione di base delle differenze tra oggetti e classi. Questa sezione esaminerà i componenti fondamentali delle classi, le loro differenti capacità e alcuni dei loro usi.

Riconoscere le differenze tra oggetti e classi

Foto di Instant Jefferson e di John Wardell

Gli sviluppatori iniziano a parlare di oggetti e di classi, e questi sembrano essere termini interscambiabili. Non è così, comunque.

Tanto per cominciare, c'è confusione nell'OOP: gli sviluppatori esperti iniziano a parlare di oggetti e di classi, e questi sembrano essere termini interscambiabili. Non è così, comunque, anche se la differenza può essere difficile da iniziare a capire subito.

Una classe, per esempio, è come un progetto per una casa. Definisce su carta la forma della casa, con relazioni ben definite e pianificate tra le diverse parti della casa, anche se la casa non esiste.

Un oggetto, quindi, è come la casa vera e propria costruita secondo quel progetto. I dati memorizzati nell'oggetto sono come il legno, i cavi e il cemento che compongono la casa: senza essere messi insieme secondo il progetto, sono solo un mucchio di roba. Comunque, quando si mette tutto insieme, diventa una casa utile e organizzata.

Le classi formano la struttura dei dati e delle azioni e usano queste informazioni per costruire gli oggetti. Si può costruire più di un oggetto contemporaneamente dalla stessa classe, e ciascuno è indipendente dagli altri. Continuando con la nostra analogia edilizia, è simile al modo di costruire un intero isolato dallo stesso progetto: 150 case diverse che hanno tutte lo stesso aspetto ma hanno dentro famiglie diverse
e un diverso arredamento.

Strutturare le classi

La sintassi per creare una classe è piuttosto immediata: si dichiara una classe usando la parola chiave class, seguita dal nome della classe e da un insieme di parentesi graffe ({}):

1
<?php
2
3
class MyClass
4
{
5
  // Class properties and methods go here

6
}
7
8
?>

Dopo aver creato la classe, si può istanziare la classe e memorizzarla in una variabile usando la parola chiave new:

1
$obj = new MyClass;

Per vedere il contenuto della classe si usa var_dump():

1
var_dump($obj);

Provate questo procedimento mettendo il codice precedente in un nuovo file di nome test.php nella cartella [locale] di test:

1
<?php
2
3
class MyClass
4
{
5
  // Class properties and methods go here

6
}
7
8
$obj = new MyClass;
9
10
var_dump($obj);
11
12
?>

Caricate la pagina nel vostro browser all'URL http://localhost/test.php e si dovrebbe vedere quanto segue:

1
object(MyClass)#1 (0) { }

Nella sua forma più semplice, avete appena completato il vostro primo script OOP.

Definire le proprietà di una classe

Per aggiungere dati a una classe si usano le proprietà, o variabili specifiche della classe. Queste funzionano esattamente come le variabili normali, tranne per il fatto che sono legate all'oggetto e perciò vi si può accedere solo usando l'oggetto.

Per aggiungere una proprietà a MyClass aggiungete il seguente codice al vostro script:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
}
7
8
$obj = new MyClass;
9
10
var_dump($obj);
11
12
?>

La parola chiave public determina la visibilità della proprietà, sulla quale imparerete un po' più avanti in questo capitolo. In seguito si dà un nome alla proprietà usando la sintassi standard per le variabili e si assegna un valore (sebbene le proprietà delle classi non abbiano bisogno di un valore iniziale).

Per leggere questa proprietà e visualizzarla nel browser si fa riferimento all'oggetto da cui leggere e alla proprietà da leggere:

1
echo $obj->prop1;

Poiché possono esistere multiple istanze di una classe, se non si facesse riferimento al singolo oggetto lo script non sarebbe in grado di determinare da quale oggetto leggere. L'uso della freccia (->) è un costrutto OOP per accedere alle proprietà e ai metodi contenuti in un dato oggetto:

Modificate lo script in test.php per leggere la proprietà invece di visualizzare l'intera classe modificando il codice così:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
}
7
8
$obj = new MyClass;
9
10
echo $obj->prop1; // Output the property

11
12
?>

Se ricaricate il browser ora vedrete quanto segue:

1
I'm a class property!

Definire i metodi di una classe

I metodi sono funzioni specifiche della classe. Si definiscono all'interno della classe come metodi le singole azioni che un oggetto sarà in grado di eseguire.

Per esempio, per creare metodi che scrivono e leggono il valore della proprietà $prop1 della classe aggiungete al vostro codice quanto segue:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function setProperty($newval)
8
  {
9
      $this->prop1 = $newval;
10
  }
11
12
  public function getProperty()
13
  {
14
      return $this->prop1 . "<br />";
15
  }
16
}
17
18
$obj = new MyClass;
19
20
echo $obj->prop1;
21
22
?>

Nota — l'OOP permette agli oggetti di far riferimento a loro stessi usando $this. Quando si lavora all'interno di un metodo si usa $this come si usarebbe il nome dell'oggetto al di fuori della classe.

Per usare questi metodi li si chiamano come funzioni normali, ma prima si fa riferimento all'oggetto al quale appartengono. Leggete la proprietà da MyClass, modificate il suo valore e leggetela di nuovo facendo la modifica seguente:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function setProperty($newval)
8
  {
9
      $this->prop1 = $newval;
10
  }
11
12
  public function getProperty()
13
  {
14
      return $this->prop1 . "<br />";
15
  }
16
}
17
18
$obj = new MyClass;
19
20
echo $obj->getProperty(); // Get the property value

21
22
$obj->setProperty("I'm a new property value!"); // Set a new one

23
24
echo $obj->getProperty(); // Read it out again to show the change

25
26
?>

Ricaricate il browser e vedrete quanto segue:

1
I'm a class property!

2
I'm a new property value!

"Il potere dell'OOP si nota quando si usano istanze multiple della
stessa classe".

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function setProperty($newval)
8
  {
9
      $this->prop1 = $newval;
10
  }
11
12
  public function getProperty()
13
  {
14
      return $this->prop1 . "<br />";
15
  }
16
}
17
18
// Create two objects

19
$obj = new MyClass;
20
$obj2 = new MyClass;
21
22
// Get the value of $prop1 from both objects

23
echo $obj->getProperty();
24
echo $obj2->getProperty();
25
26
// Set new values for both objects

27
$obj->setProperty("I'm a new property value!");
28
$obj2->setProperty("I belong to the second instance!");
29
30
// Output both objects' $prop1 value

31
echo $obj->getProperty();
32
echo $obj2->getProperty();
33
34
?>

Quando ricaricate il risultato nel browser appare quello che segue:

1
I'm a class property!

2
I'm a class property!
3
I'm a new property value!
4
I belong to the second instance!

Come potete vedere, l'OOP considera gli oggetti entità separate, e ciò permette una facile separazione dei vari pezzi di codice in piccoli gruppi separati.

Metodi magici nell'OOP

Per rendere più facile l'uso degli oggetti il PHP fornisce anche un certo numero di metodi magici, o metodi speciali che vengono chiamati quando occorrono determinate azioni comuni all'interno degli oggetti. Questo permette agli sviluppatori di eseguire diversi compiti utili con una certa facilità.

Usare i costruttori e i distruttori

Quando viene istanziato un oggetto spesso si desidera impostare alcune cose subito dall'inizio. Per gestire questo il PHP fornisce il metodo magico __construct(), che viene chiamato automaticamente ogni volta che si crea un nuovo
oggetto.

Al fine di illustrare il concetto dei costruttori, aggiungiamo a MyClass un costruttore che restituirà un messaggio ogni volta che si crea una nuova istanza della classe:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function setProperty($newval)
13
  {
14
      $this->prop1 = $newval;
15
  }
16
17
  public function getProperty()
18
  {
19
      return $this->prop1 . "<br />";
20
  }
21
}
22
23
// Create a new object

24
$obj = new MyClass;
25
26
// Get the value of $prop1

27
echo $obj->getProperty();
28
29
// Output a message at the end of the file

30
echo "End of file.<br />";
31
32
?>

Nota __CLASS__ restituisce il nome della classe nella quale è chiamato, per questo è noto come costante magica. Ci sono varie costanti magiche disponibili, potete leggere di più a questo proposito nel manuale PHP.

Ricaricare il file nel browser produrrà il seguente risultato:

1
The class "MyClass" was initiated!
2
I'm a class property!
3
End of file.

Per chiamare una funzione quando l'oggetto viene distrutto è disponibile il metodo magico __destruct(). Questo è utile per fare pulizia nelle classi (per esempio per chiudere una connessione a un database).

Visualizzate un messaggio quando viene distrutto l'oggetto definendo il metodo magico
__destruct() in MyClass:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function setProperty($newval)
18
  {
19
      $this->prop1 = $newval;
20
  }
21
22
  public function getProperty()
23
  {
24
      return $this->prop1 . "<br />";
25
  }
26
}
27
28
// Create a new object

29
$obj = new MyClass;
30
31
// Get the value of $prop1

32
echo $obj->getProperty();
33
34
// Output a message at the end of the file

35
echo "End of file.<br />";
36
37
?>

Con un distruttore definito, ricaricare il file di test darà il seguente risultato:

1
The class "MyClass" was initiated!
2
I'm a class property!
3
End of file.
4
The class "MyClass" was destroyed.

"Quando si raggiunge la fine di un file il PHP libera automaticamente tutte le risorse".

Per attivare esplicitamente il distruttore si può distruggere l'oggetto usando la
funzione unset():

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function setProperty($newval)
18
  {
19
      $this->prop1 = $newval;
20
  }
21
22
  public function getProperty()
23
  {
24
      return $this->prop1 . "<br />";
25
  }
26
}
27
28
// Create a new object

29
$obj = new MyClass;
30
31
// Get the value of $prop1

32
echo $obj->getProperty();
33
34
// Destroy the object

35
unset($obj);
36
37
// Output a message at the end of the file

38
echo "End of file.<br />";
39
40
?>

Ora il risultato cambia come segue se caricato nel browser:

1
The class "MyClass" was initiated!
2
I'm a class property!
3
The class "MyClass" was destroyed.
4
End of file.

Convertire in stringa

Per evitare un errore se uno script cerca di visualizzare MyClass come stringa si usa un altro metodo magico di nome __toString().

Senza __toString() un tentativo di visualizzare l'oggetto come stringa darebbe un errore fatale. Tentativo di usare echo per visualizzare l'oggetto senza che ci sia un metodo magico:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function setProperty($newval)
18
  {
19
      $this->prop1 = $newval;
20
  }
21
22
  public function getProperty()
23
  {
24
      return $this->prop1 . "<br />";
25
  }
26
}
27
28
// Create a new object

29
$obj = new MyClass;
30
31
// Output the object as a string

32
echo $obj;
33
34
// Destroy the object

35
unset($obj);
36
37
// Output a message at the end of the file

38
echo "End of file.<br />";
39
40
?>

Questo dà il seguente risultato:

1
The class "MyClass" was initiated!
2
3
Catchable fatal error: Object of class MyClass could not be converted to string in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 40

Per evitare questo errore aggiungiamo un metodo __toString():

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  public function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
// Create a new object

35
$obj = new MyClass;
36
37
// Output the object as a string

38
echo $obj;
39
40
// Destroy the object

41
unset($obj);
42
43
// Output a message at the end of the file

44
echo "End of file.<br />";
45
46
?>

In questo caso un tentativo di convertire l'oggetto in stringa dà come risultato una chiamata al metodo getProperty(). Caricate lo script nel browser per vedere il risultato:

1
The class "MyClass" was initiated!
2
Using the toString method: I'm a class property!
3
The class "MyClass" was destroyed.
4
End of file.

 Consiglio — In aggiunta ai metodi magici discussi in questa sezione, ne sono disponibili altri. Per una lista completa dei metodi magici si veda la pagina del manuale PHP.

Usare l'ereditarietà

Le classi possono ereditare i metodi e le proprietà di un'altra classe usando la parola chiave extends. Per esempio, per creare una seconda classe che estende MyClass e aggiunge un metodo si può aggiungere il seguente codice al file:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  public function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function newMethod()
37
  {
38
      echo "From a new method in " . __CLASS__ . ".<br />";
39
  }
40
}
41
42
// Create a new object

43
$newobj = new MyOtherClass;
44
45
// Output the object as a string

46
echo $newobj->newMethod();
47
48
// Use a method from the parent class

49
echo $newobj->getProperty();
50
51
?>

Ricaricando il file nel browser si ha il seguente risultato:

1
The class "MyClass" was initiated!
2
From a new method in MyOtherClass.
3
I'm a class property!
4
The class "MyClass" was destroyed.

Sovrascrivere proprietà e metodi ereditati

Per modificare il comportamento di una proprietà o di un metodo esistente nella nuova classe si può semplicemente sovrascriverlo dichiarandolo di nuovo nella nuova classe:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  public function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function __construct()
37
  {
38
      echo "A new constructor in " . __CLASS__ . ".<br />";
39
  }
40
41
  public function newMethod()
42
  {
43
      echo "From a new method in " . __CLASS__ . ".<br />";
44
  }
45
}
46
47
// Create a new object

48
$newobj = new MyOtherClass;
49
50
// Output the object as a string

51
echo $newobj->newMethod();
52
53
// Use a method from the parent class

54
echo $newobj->getProperty();
55
56
?>

Questo cambia il risultato nel browser:

1
A new constructor in MyOtherClass.
2
From a new method in MyOtherClass.
3
I'm a class property!
4
The class "MyClass" was destroyed.

Conservare la funzionalità originale del metodo quando si sovrascrivono i metodi

Per aggiungere una nuova funzionalità a un metodo ereditato conservando intatto il metodo originale si usa la parola chiave parent con l'operatore di risoluzione di visibilità (::):

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  public function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function __construct()
37
  {
38
      parent::__construct(); // Call the parent class's constructor

39
      echo "A new constructor in " . __CLASS__ . ".<br />";
40
  }
41
42
  public function newMethod()
43
  {
44
      echo "From a new method in " . __CLASS__ . ".<br />";
45
  }
46
}
47
48
// Create a new object

49
$newobj = new MyOtherClass;
50
51
// Output the object as a string

52
echo $newobj->newMethod();
53
54
// Use a method from the parent class

55
echo $newobj->getProperty();
56
57
?>

Questo restituisce il risultato sia del costruttore padre che del costruttore della nuova classe:

1
The class "MyClass" was initiated!
2
A new constructor in MyOtherClass.
3
From a new method in MyOtherClass.
4
I'm a class property!
5
The class "MyClass" was destroyed.

Assegnare la visibilità alle proprietà e ai metodi

Per un maggior controllo sugli oggetti si assegna una visibilità ai metodi e alle proprietà. Questo controlla come e da dove si può accedere a metodi e proprietà. Ci sono tre parole chiave di visibilità: public, protected e private. Oltre alla visibilità, una proprietà o un metodo può essere dichiarato static, che permette l'accesso senza un'istanziazione della classe.

"Per un maggior controllo sugli oggetti si assegna una visibilità ai metodi e alle proprietà".

Nota — La visibilità è una nuova funzionalità dal PHP 5 in poi. Per informazioni sulla compatibilità OOP con il PHP 4 si veda la pagina del manuale PHP.

Proprietà e metodi pubblici

Tutte le proprietà e tutti i metodi usati finora sono stati pubblici. Questo significa che vi si può accedere ovunque, sia dentro la classe che fuori.

Proprietà e metodi protetti

Quando una proprietà o un metodo è dichiarato protected vi si può accedere solo all'interno della classe stessa o delle sue classi discendenti (classi che estendono la classe che contiene il metodo protetto).

Dichiarate il metodo getProperty() protetto in MyClass e provate ad accedere ad esso direttamente al di fuori della classe:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  protected function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function __construct()
37
  {
38
      parent::__construct();
39
echo "A new constructor in " . __CLASS__ . ".<br />";
40
  }
41
42
  public function newMethod()
43
  {
44
      echo "From a new method in " . __CLASS__ . ".<br />";
45
  }
46
}
47
48
// Create a new object

49
$newobj = new MyOtherClass;
50
51
// Attempt to call a protected method

52
echo $newobj->getProperty();
53
54
?>

Provando a eseguire questo script appare il seguente errore:

1
The class "MyClass" was initiated!
2
A new constructor in MyOtherClass.
3
4
Fatal error: Call to protected method MyClass::getProperty() from context '' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55

Ora create un metodo in MyOtherClass che chiami il metodo getProperty():

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  protected function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function __construct()
37
  {
38
      parent::__construct();
39
echo "A new constructor in " . __CLASS__ . ".<br />";
40
  }
41
42
  public function newMethod()
43
  {
44
      echo "From a new method in " . __CLASS__ . ".<br />";
45
  }
46
47
  public function callProtected()
48
  {
49
      return $this->getProperty();
50
  }
51
}
52
53
// Create a new object

54
$newobj = new MyOtherClass;
55
56
// Call the protected method from within a public method

57
echo $newobj->callProtected();
58
59
?>

Questo genera il risultato desiderato:

1
The class "MyClass" was initiated!
2
A new constructor in MyOtherClass.
3
I'm a class property!
4
The class "MyClass" was destroyed.

Proprietà e metodi privati

Una proprietà o un metodo dichiarato private è accessibile solo all'interno della classe che lo definisce. Questo vuol dire che anche se una nuova classe estende la classe che definisce una proprietà privata, quella proprietà o quel metodo non sarà affatto disponibile all'interno della classe figlia.

Per dimostrare questo, dichiarate privato getProperty() in MyClass e provate a chiamare callProtected() da
MyOtherClass:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public function __construct()
8
  {
9
      echo 'The class "', __CLASS__, '" was initiated!<br />';
10
  }
11
12
  public function __destruct()
13
  {
14
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
15
  }
16
17
  public function __toString()
18
  {
19
      echo "Using the toString method: ";
20
      return $this->getProperty();
21
  }
22
23
  public function setProperty($newval)
24
  {
25
      $this->prop1 = $newval;
26
  }
27
28
  private function getProperty()
29
  {
30
      return $this->prop1 . "<br />";
31
  }
32
}
33
34
class MyOtherClass extends MyClass
35
{
36
  public function __construct()
37
  {
38
      parent::__construct();
39
      echo "A new constructor in " . __CLASS__ . ".<br />";
40
  }
41
42
  public function newMethod()
43
  {
44
      echo "From a new method in " . __CLASS__ . ".<br />";
45
  }
46
47
  public function callProtected()
48
  {
49
      return $this->getProperty();
50
  }
51
}
52
53
// Create a new object

54
$newobj = new MyOtherClass;
55
56
// Use a method from the parent class

57
echo $newobj->callProtected();
58
59
?>

Ricaricate il browser e apparirà il seguente errore:

1
The class "MyClass" was initiated!
2
A new constructor in MyOtherClass.
3
4
Fatal error: Call to private method MyClass::getProperty() from context 'MyOtherClass' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49

Proprietà e metodi statici

A una proprietà o a un metodo dichiarato static si può accedere senza prima istanziare la classe; si scrive semplicemente il nome della classe, l'operatore di risoluzione di visibilità e il nome della proprietà o del metodo.

"Uno dei vantaggi maggiori di usare proprietà statiche è che mantengono in memoria il loro valore per tutta la durata dello script".

Per dimostrare questo aggiungete una proprietà statica di nome $count e un metodo statico di nome plusOne() a MyClass. Poi impostate un ciclo do...while per visualizzare il valore incrementato di $count finché il valore è minore di 10:

1
<?php
2
3
class MyClass
4
{
5
  public $prop1 = "I'm a class property!";
6
7
  public static $count = 0;
8
9
  public function __construct()
10
  {
11
      echo 'The class "', __CLASS__, '" was initiated!<br />';
12
  }
13
14
  public function __destruct()
15
  {
16
      echo 'The class "', __CLASS__, '" was destroyed.<br />';
17
  }
18
19
  public function __toString()
20
  {
21
      echo "Using the toString method: ";
22
      return $this->getProperty();
23
  }
24
25
  public function setProperty($newval)
26
  {
27
      $this->prop1 = $newval;
28
  }
29
30
  private function getProperty()
31
  {
32
      return $this->prop1 . "<br />";
33
  }
34
35
  public static function plusOne()
36
  {
37
      return "The count is " . ++self::$count . ".<br />";
38
  }
39
}
40
41
class MyOtherClass extends MyClass
42
{
43
  public function __construct()
44
  {
45
      parent::__construct();
46
      echo "A new constructor in " . __CLASS__ . ".<br />";
47
  }
48
49
  public function newMethod()
50
  {
51
      echo "From a new method in " . __CLASS__ . ".<br />";
52
  }
53
54
  public function callProtected()
55
  {
56
      return $this->getProperty();
57
  }
58
}
59
60
do
61
{
62
  // Call plusOne without instantiating MyClass

63
  echo MyClass::plusOne();
64
} while ( MyClass::$count < 10 );
65
66
?>

Nota — Quando si accede a proprietà statiche, il segno del dollaro
($) viene dopo l'operatore di risoluzione di visibilità.

Quando si carica lo script nel browser il risultato è il seguente:

1
The count is 1.
2
The count is 2.
3
The count is 3.
4
The count is 4.
5
The count is 5.
6
The count is 6.
7
The count is 7.
8
The count is 8.
9
The count is 9.
10
The count is 10.

Commentare con i DocBlock

"Lo stile di commento DocBlock è un metodo
ampiamente accettato per documentare le classi".

Sebbene non faccia ufficialmente parte dell'OOP, lo stile di commento DocBlock è un metodo ampiamente accettato per documentare le classi. Oltre a fornire uno standard che gli
sviluppatori usano quando scrivono codice, è anche stato adottato da molti dei più popolari strumenti di sviluppo software, come Eclipse e NetBeans, e sarà usato per usare suggerimenti di codice.

Si definisce un DocBlock usando un commento a blocco che inizia con un asterisco in più:

1
/**

2
 * This is a very basic DocBlock

3
 */

Il vero potere del DocBlock sta nella capacità di usare i tag, che iniziano con una chiocciola (@) immediatamente seguita dal nome del tag e dal suo valore. I tag dei DocBlock permettono agli sviluppatori di definire gli autori di un file, la licenza per una classe, informazioni su proprietà e metodi e altre informazioni utili.

Seguono i tag più comunemente usati:

  • @author: Con questo tag si elenca l'autore dell'elemento corrente (che può essere una classe, un file, un metodo o un qualunque pezzo di codice). Si possono usare nello stesso DocBlock più tag author se si vuole dar credito a più di un autore. Il formato per il nome di un autore è Mario Rossi <mario.rossi@email.com>.
  • @copyright: Questo significa l'anno di copyright e il nome del detentore del copyright per l'elemento corrente. Il formato è 2010 Detentore Del Copyright.
  • @licenza: Questo è un link alla licenza per l'elemento corrente. Il formato per le informazioni sulla licenza è
    http://www.esempio.com/percorso/alla/licenza.txt Nome Della Licenza.
  • @var: Contiente il tipo e la descrizione di una variabile o proprietà della classe. Il formato è tipo descrizione dell'elemento.
  • @param: Questo tag mostra il tipo e la descrizione del parametro di una funzione o metodo. Il formato è tipo $nome_elemento descrizione dell'elemento
  • @return: Questo tag fornisce il tipo e la descrizione del valore restituito da una funzione o metodo. Il formato è tipo descrizione dell'elemento restituito.

Un esempio di classe commentata con i DocBlock ha questo aspetto:

1
<?php
2
3
/**

4
 * A simple class

5
 *

6
 * This is the long description for this class,

7
 * which can span as many lines as needed. It is

8
 * not required, whereas the short description is

9
 * necessary.

10
 *

11
 * It can also span multiple paragraphs if the

12
 * description merits that much verbiage.

13
 *

14
 * @author Jason Lengstorf <jason.lengstorf@ennuidesign.com>

15
 * @copyright 2010 Ennui Design

16
 * @license http://www.php.net/license/3_01.txt PHP License 3.01

17
 */
18
class SimpleClass
19
{
20
  /**

21
   * A public variable

22
   *

23
   * @var string stores data for the class

24
   */
25
  public $foo;
26
27
  /**

28
   * Sets $foo to a new value upon class instantiation

29
   *

30
   * @param string $val a value required for the class

31
   * @return void

32
   */
33
  public function __construct($val)
34
  {
35
      $this->foo = $val;
36
  }
37
38
  /**

39
   * Multiplies two integers

40
   *

41
   * Accepts a pair of integers and returns the

42
   * product of the two.

43
   *

44
   * @param int $bat a number to be multiplied

45
   * @param int $baz a number to be multiplied

46
   * @return int the product of the two parameters

47
   */
48
  public function bar($bat, $baz)
49
  {
50
      return $bat * $baz;
51
  }
52
}
53
54
?>

Se guardate bene la classe precedente, i vantaggi dei DocBlock sono chiari: tutto è ben definito in modo che il prossimo sviluppatore possa prendere il codice e non si debba mai chiedere cosa fa o cosa dovrebbe contenere un pezzo di codice.

Confronto tra codice orientato agli oggetti e procedurale

Non esiste un modo giusto o un modo sbagliato di scrivere codice. Detto questo, questa sezione delinea una forte argomentazione per adottare un approccio orientato agli oggetti nello sviluppo di software, specialmente in grandi progetti.

Ragione 1: Facilità di implementazione

"Anche se può intimorire all'inizio, l'OOP in realtà fornisce un approccio più facile per gestire i dati".

Anche se può intimorire all'inizio, l'OOP in realtà fornisce un approccio più facile per gestire i dati. Poiché un oggetto può memorizzare dati al suo interno, non è necessario passare variabili da una funzione all'altra perché tutto funzioni correttamente.

Inoltre, poiché possono esistere contemporaneamente più istanze della stessa classe, è infinitamente più facile gestire grandi insiemi di dati. Per esempio, supponiamo di dover elaborare le informazioni di due persone in un file. Ci servono i nomi, le occupazioni e le età.

L'approccio procedurale

Questo è l'approccio procedurale al nostro esempio:

1
<?php
2
3
function changeJob($person, $newjob)
4
{
5
  $person['job'] = $newjob; // Change the person's job

6
  return $person;
7
}
8
9
function happyBirthday($person)
10
{
11
  ++$person['age']; // Add 1 to the person's age

12
  return $person;
13
}
14
15
$person1 = array(
16
  'name' => 'Tom',
17
  'job' => 'Button-Pusher',
18
  'age' => 34
19
);
20
21
$person2 = array(
22
  'name' => 'John',
23
  'job' => 'Lever-Puller',
24
  'age' => 41
25
);
26
27
// Output the starting values for the people

28
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
29
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";
30
31
// Tom got a promotion and had a birthday

32
$person1 = changeJob($person1, 'Box-Mover');
33
$person1 = happyBirthday($person1);
34
35
// John just had a birthday

36
$person2 = happyBirthday($person2);
37
38
// Output the new values for the people

39
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
40
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";
41
42
?>

Quando si esegue, il codice restituisce quanto segue:

1
Person 1: Array
2
(
3
  [name] => Tom
4
  [job] => Button-Pusher
5
  [age] => 34
6
)
7
Person 2: Array
8
(
9
  [name] => John
10
  [job] => Lever-Puller
11
  [age] => 41
12
)
13
Person 1: Array
14
(
15
  [name] => Tom
16
  [job] => Box-Mover
17
  [age] => 35
18
)
19
Person 2: Array
20
(
21
  [name] => John
22
  [job] => Lever-Puller
23
  [age] => 42
24
)

Sebbene il codice non sia necessariamente pessimo, c'è molto da tener presente quando si scrive. L'array con gli attributi della persona in questione deve essere passato e restituito da ogni chiamata di funzione, cosa che lascia un margine di errore.

Per rendere l'esempio più pulito, sarebbe desiderabile lasciare allo sviluppatore meno cose possibili. Alle funzioni dovrebbe essere necessario passare solo le informazioni assolutamente essenziali per l'operazione corrente.

È qui che arriva l'OOP ad aiutare a rendere le cose più pulite.

L'approccio OOP

Ecco l'approccio OOP al nostro esempio:

1
<?php
2
3
class Person
4
{
5
  private $_name;
6
  private $_job;
7
  private $_age;
8
9
  public function __construct($name, $job, $age)
10
  {
11
      $this->_name = $name;
12
      $this->_job = $job;
13
      $this->_age = $age;
14
  }
15
16
  public function changeJob($newjob)
17
  {
18
      $this->_job = $newjob;
19
  }
20
21
  public function happyBirthday()
22
  {
23
      ++$this->_age;
24
  }
25
}
26
27
// Create two new people

28
$person1 = new Person("Tom", "Button-Pusher", 34);
29
$person2 = new Person("John", "Lever Puller", 41);
30
31
// Output their starting point

32
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
33
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";
34
35
// Give Tom a promotion and a birthday

36
$person1->changeJob("Box-Mover");
37
$person1->happyBirthday();
38
39
// John just gets a year older

40
$person2->happyBirthday();
41
42
// Output the ending values

43
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
44
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";
45
46
?>

Questo restituisce il seguente risultato nel browser:

1
Person 1: Person Object
2
(
3
  [_name:private] => Tom
4
  [_job:private] => Button-Pusher
5
  [_age:private] => 34
6
)
7
8
Person 2: Person Object
9
(
10
  [_name:private] => John
11
  [_job:private] => Lever Puller
12
  [_age:private] => 41
13
)
14
15
Person 1: Person Object
16
(
17
  [_name:private] => Tom
18
  [_job:private] => Box-Mover
19
  [_age:private] => 35
20
)
21
22
Person 2: Person Object
23
(
24
  [_name:private] => John
25
  [_job:private] => Lever Puller
26
  [_age:private] => 42
27
)

C'è un po' più di lavoro iniziale per realizzare l'approccio orientato agli oggetti, ma una volta che la classe è definita, creare e modificare una persona è un attimo; le informazioni di una persona non devono essere passate o restituite dai metodi, e ad ogni metodo si passano solo le informazioni assolutamente essenziali.

"L'OOP ridurrà significativamente il vostro carico di lavoro se implementata correttamente".

Su una piccola scala questa differenza potrà non sembrare granché, ma come la vostra applicazione crescerà di dimensione l'OOP ridurrà significativamente il vostro carico di lavoro se implementata correttamente.

Consiglio Non tutto deve essere orientato agli oggetti. Una funzione rapida che gestisce qualcosa di piccolo in un posto solo all'interno dell'applicazione non deve necessariamente far parte di una qualche classe. Usate il vostro giudizio quando dovete decidere tra un approccio procedurale e uno orientato agli oggetti.

Ragione 2: Migliore organizzazione

Un altro vantaggio dell'OOP è come si presta bene ad essere facilmente impacchettata e catalogata. Ogni classe si può generalmente tenere in un suo file separato, e se si una una convenzione uniforme per i nomi accedere alle classi è estremamente semplice.

Supponiamo di avere un'applicazione con 150 classi che sono chiamate dinamicamente da un file di controllo alla radice del filesystem della nostra applicazione. Tutte le 150 classi seguono la convenzione per i nomi class.nomeclasse.inc.php e risiedono nella cartella inc della nostra applicazione.

Il file di controllo può implementare la funzione __autoload() del PHP per caricare dinamicamente solo le classi necessarie quando vengono chiamate, piuttosto che includerle tutte e 150 nel file di controllo nel caso che servano o trovare qualche modo abile per includere i file nel nostro codice:

1
<?php
2
  function __autoload($class_name)
3
  {
4
      include_once 'inc/class.' . $class_name . '.inc.php';
5
  }
6
?>

Avere ogni classe in un file separato rende anche il codice più portabile e più facile da riutilizzare in nuove applicazioni senza troppo copia e incolla.

Ragione 3: Manutenzione più facile

A causa della natura più compatta dell'OOP quando è fatta correttamente, le modifiche nel codice sono di solito molto più facili da individuare e da fare che in una lunga implementazione procedurale a spaghetti.

Se un array particolare deve avere un nuovo attributo, un pezzo di software procedurale può richiedere (nel caso peggiore) che il nuovo attributo sia aggiunto a ogni funzione che usa l'array.

Un'applicazione OOP potrebbe potenzialmente essere aggiornata facilmente aggiungendo la nuova proprietà e poi aggiungendo i metodi che gestiscono quella proprietà.

Molti dei vantaggi coperti in questa sezione sono il prodotto della OOP in combinazione con le pratice di programmazione DRY: È decisamente possibile creare codice procedurale facile da manutenere che non causi incubi, ed è altrettanto possibile creare pessimo codice orientato agli oggetti. [Pro PHP and jQuery] cercherà di dimostrare che una combinazione di buone abitudini quando si scrive codice insieme all'OOP genera codice che è facile da leggere e da manutenere.

Riassunto

A questo punto dovreste sentirvi a vostro agio con lo stile di programmazione orientato agli oggetti. Imparare l'OOP è un buon modo per portare la vostra programmazione al livello successivo. Se implementata correttamente, la OOP vi aiuterà a produrre codice facile da leggere, facile da manutenere, portabile e che risparmierà (a voi e agli sviluppatori che lavorano con voi) ore di lavoro in più. Siete bloccati su qualcosa che non è stato trattato in questo articolo? State già usando l'OOP e avete consigli per i principianti? Commentate!

Nota dell'autore — Questo tutorial era un estratto da Pro PHP and jQuery (Apress, 2010).

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.