Sei un recruiter? Clicca qui!
Programmazione a oggetti in PHP guida semplice per iniziare davvero

Programmazione a oggetti in PHP: guida semplice per iniziare davvero

Scritto da Sergio Pinna il

13/07/2026

Quando si inizia a scrivere codice PHP, di solito si parte nel modo più diretto possibile: variabili, condizioni, cicli, funzioni e qualche file incluso qua e là. È normalissimo. Anzi, per imparare è quasi sempre la strada migliore.

Poi però arriva un momento in cui il codice cresce.

Magari all’inizio hai solo poche righe per gestire un modulo di contatto, stampare una lista di prodotti, recuperare dati da un database o creare una piccola funzionalità per WordPress. Dopo qualche modifica, però, il file inizia a diventare lungo, alcune funzioni si assomigliano, certe variabili vengono usate in più punti e ogni volta che devi cambiare qualcosa hai paura di rompere il resto.

È proprio qui che entra in gioco la programmazione a oggetti, spesso abbreviata in OOP, dall’inglese Object Oriented Programming.

Detta in modo semplice, la programmazione a oggetti è un modo di organizzare il codice. Non serve solo a “scrivere codice più elegante”, ma soprattutto a creare strutture più ordinate, riutilizzabili e facili da mantenere nel tempo.

In PHP, la programmazione a oggetti è usatissima: nei framework come Laravel e Symfony, nei plugin WordPress più strutturati, nei sistemi e-commerce, nei gestionali, nelle API e in moltissimi progetti professionali.

L’obiettivo di questo articolo non è farti imparare tutta la teoria in modo accademico. L’obiettivo è molto più pratico: capire che cos’è una classe, che cos’è un oggetto, come si usano proprietà e metodi, e perché tutto questo può rendere il tuo codice PHP più pulito e professionale.

Prima di tutto: cosa significa “programmazione a oggetti”?

Immagina di dover rappresentare un’automobile nel codice.

Un’automobile ha delle caratteristiche:

$marca = 'Fiat';
$modello = 'Panda';
$colore = 'Rosso';
$velocita = 0;

E può compiere delle azioni:

accelera();
frena();
accendi();
spegni();

Nella programmazione tradizionale potresti gestire tutto con variabili e funzioni separate. Funziona, ma più il progetto cresce, più il codice rischia di diventare sparso.

Con la programmazione a oggetti, invece, puoi racchiudere dati e comportamenti dentro una struttura chiamata classe.

La classe è come un modello, uno stampo, una definizione.

L’oggetto, invece, è l’elemento concreto creato a partire da quello stampo.

Esempio molto semplice:

class Automobile {
public $marca;
public $modello;
public $colore;
public $velocita = 0;

public function accelera() {
$this->velocita += 10;
}

public function frena() {
$this->velocita -= 10;
}
}

Questa è una classe.

Da questa classe posso creare una o più automobili reali, cioè degli oggetti:

$auto1 = new Automobile();
$auto1->marca = 'Fiat';
$auto1->modello = 'Panda';
$auto1->colore = 'Rosso';

$auto2 = new Automobile();
$auto2->marca = 'Volkswagen';
$auto2->modello = 'Golf';
$auto2->colore = 'Nero';

Qui abbiamo creato due oggetti diversi, ma entrambi basati sulla stessa classe Automobile.

La classe è una specie di progetto tecnico. Gli oggetti sono le istanze reali create da quel progetto.

Classe e oggetto: la differenza spiegata facile

Questa distinzione è fondamentale.

Una classe è una definizione.

Un oggetto è qualcosa che nasce da quella definizione.

Per fare un paragone molto concreto:

  • la classe è il progetto di una casa;
  • l’oggetto è la casa costruita;
  • la classe è lo stampo per fare biscotti;
  • l’oggetto è il biscotto vero;
  • la classe è il modello “Prodotto”;
  • l’oggetto è un prodotto specifico, ad esempio “T-shirt bianca taglia M”.

In PHP si crea una classe con la parola chiave class.

class Prodotto {
public $nome;
public $prezzo;
}

Poi si crea un oggetto con la parola chiave new.

$prodotto = new Prodotto();
$prodotto->nome = 'T-shirt bianca';
$prodotto->prezzo = 29.90;

A questo punto $prodotto è un oggetto.

E posso accedere alle sue proprietà usando la freccia ->.

echo $prodotto->nome;
echo $prodotto->prezzo;

La freccia -> è uno degli elementi che inizialmente può sembrare strano, ma in realtà significa semplicemente: “prendi questa proprietà o questo metodo dentro questo oggetto”.

Le proprietà: i dati dell’oggetto

Le proprietà sono le informazioni che appartengono a un oggetto.

Nel caso di un prodotto, le proprietà potrebbero essere:

class Prodotto {
public $nome;
public $prezzo;
public $disponibile;
}

Poi posso creare un prodotto concreto:

$prodotto = new Prodotto();

$prodotto->nome = 'Felpa con cappuccio';
$prodotto->prezzo = 49.90;
$prodotto->disponibile = true;

In questo esempio:

$prodotto->nome

contiene il nome del prodotto.

$prodotto->prezzo

contiene il prezzo.

$prodotto->disponibile

contiene un valore booleano, cioè true o false.

Le proprietà sono quindi un modo ordinato per raggruppare informazioni collegate tra loro.

Senza oggetti, potresti avere qualcosa del genere:

$nomeProdotto = 'Felpa con cappuccio';
$prezzoProdotto = 49.90;
$disponibileProdotto = true;

Con gli oggetti, invece, tutto è raccolto dentro un’unica entità:

$prodotto->nome;
$prodotto->prezzo;
$prodotto->disponibile;

È più leggibile, più ordinato e più facile da gestire.

I metodi: le azioni dell’oggetto

I metodi sono funzioni definite dentro una classe.

Se le proprietà descrivono “com’è” un oggetto, i metodi descrivono “cosa può fare”.

Torniamo al prodotto.

Un prodotto potrebbe avere un metodo per restituire il prezzo formattato:

class Prodotto {
public $nome;
public $prezzo;

public function prezzoFormattato() {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Ora posso fare così:

$prodotto = new Prodotto();

$prodotto->nome = 'Felpa con cappuccio';
$prodotto->prezzo = 49.9;

echo $prodotto->prezzoFormattato();

Il risultato sarà:

49,90 €

Qui succedono alcune cose importanti.

Dentro il metodo prezzoFormattato() compare questa parola:

$this

$this significa: “questo oggetto”.

Quindi:

$this->prezzo

vuol dire: “la proprietà prezzo di questo specifico oggetto”.

Se hai più prodotti, ogni oggetto avrà il proprio prezzo.

$prodotto1 = new Prodotto();
$prodotto1->nome = 'Felpa';
$prodotto1->prezzo = 49.90;

$prodotto2 = new Prodotto();
$prodotto2->nome = 'T-shirt';
$prodotto2->prezzo = 19.90;

echo $prodotto1->prezzoFormattato(); // 49,90 €
echo $prodotto2->prezzoFormattato(); // 19,90 €

Il metodo è lo stesso, ma lavora sui dati dell’oggetto specifico.

Questa è una delle cose più potenti della programmazione a oggetti.

Il costruttore: inizializzare subito un oggetto

Negli esempi precedenti abbiamo creato un oggetto e poi abbiamo assegnato le proprietà una per una:

$prodotto = new Prodotto();

$prodotto->nome = 'Felpa';
$prodotto->prezzo = 49.90;

Funziona, ma non è comodissimo.

PHP permette di usare un metodo speciale chiamato costruttore.

Il costruttore viene eseguito automaticamente quando crei un nuovo oggetto.

Si scrive così:

class Prodotto {
public $nome;
public $prezzo;

public function __construct($nome, $prezzo) {
$this->nome = $nome;
$this->prezzo = $prezzo;
}

public function prezzoFormattato() {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Ora posso creare il prodotto in modo molto più pulito:

$prodotto = new Prodotto('Felpa con cappuccio', 49.90);

echo $prodotto->nome;
echo $prodotto->prezzoFormattato();

Il metodo:

__construct()

viene chiamato automaticamente da PHP quando scrivi:

new Prodotto(...)

Questo rende il codice più compatto e riduce il rischio di dimenticare qualche proprietà importante.

Un esempio concreto: creare una scheda prodotto

Facciamo un esempio più vicino a un sito web.

Immagina di voler creare una piccola scheda prodotto in PHP.

Potresti scrivere una classe così:

class Prodotto {
public $nome;
public $prezzo;
public $descrizione;
public $disponibile;

public function __construct($nome, $prezzo, $descrizione, $disponibile = true) {
$this->nome = $nome;
$this->prezzo = $prezzo;
$this->descrizione = $descrizione;
$this->disponibile = $disponibile;
}

public function prezzoFormattato() {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}

public function statoDisponibilita() {
if ($this->disponibile) {
return 'Disponibile';
}

return 'Non disponibile';
}

public function stampaScheda() {
echo '<div class="prodotto">';
echo '<h2>' . htmlspecialchars($this->nome) . '</h2>';
echo '<p>' . htmlspecialchars($this->descrizione) . '</p>';
echo '<strong>' . $this->prezzoFormattato() . '</strong>';
echo '<span>' . $this->statoDisponibilita() . '</span>';
echo '</div>';
}
}

Poi puoi usarla così:

$prodotto = new Prodotto(
'Felpa con cappuccio',
49.90,
'Felpa morbida in cotone, ideale per il tempo libero.',
true
);

$prodotto->stampaScheda();

Questo codice crea un prodotto e stampa una piccola scheda HTML.

Il vantaggio è che la logica del prodotto è raccolta tutta dentro la classe Prodotto.

La formattazione del prezzo sta lì.

La disponibilità sta lì.

La stampa della scheda sta lì.

Se domani vuoi cambiare il modo in cui viene mostrato il prezzo, non devi cercarlo in dieci punti diversi del sito. Vai nel metodo prezzoFormattato() e modifichi solo quello.

La visibilità: public, private e protected

Finora abbiamo usato sempre public.

Esempio:

public $nome;
public $prezzo;

Oppure:

public function prezzoFormattato() {
// codice
}

In PHP, la visibilità stabilisce da dove puoi accedere a proprietà e metodi.

I tre livelli principali sono:

public
private
protected

Vediamoli in modo semplice.

public significa che una proprietà o un metodo è accessibile anche dall’esterno della classe.

$prodotto->nome = 'Felpa';
echo $prodotto->nome;

private significa che quella proprietà o quel metodo può essere usato solo dentro la classe.

class Prodotto {
private $prezzo;

public function __construct($prezzo) {
$this->prezzo = $prezzo;
}
}

In questo caso non puoi fare:

$prodotto->prezzo = 10;

Perché $prezzo è privato.

Allora come fai a leggere o modificare il prezzo?

Usi dei metodi pubblici.

class Prodotto {
private $nome;
private $prezzo;

public function __construct($nome, $prezzo) {
$this->nome = $nome;
$this->prezzo = $prezzo;
}

public function getNome() {
return $this->nome;
}

public function getPrezzo() {
return $this->prezzo;
}

public function setPrezzo($prezzo) {
if ($prezzo < 0) {
return;
}

$this->prezzo = $prezzo;
}
}

Ora il prezzo non viene modificato direttamente dall’esterno. Passa attraverso un metodo:

$prodotto = new Prodotto('Felpa', 49.90);

echo $prodotto->getPrezzo();

$prodotto->setPrezzo(39.90);

Questa cosa si chiama incapsulamento.

Non farti spaventare dal termine. Significa semplicemente che proteggi i dati interni dell’oggetto e decidi tu come possono essere letti o modificati.

È un po’ come dire: “Non toccare direttamente il motore dell’auto. Usa volante, pedali e cambio”.

Perché usare private invece di public?

All’inizio può sembrare una complicazione inutile.

Potresti pensare:

“Ma se metto tutto public faccio prima”.

Ed è vero: fai prima.

Però fai anche più danni, soprattutto in progetti grandi.

Guarda questo esempio:

$prodotto->prezzo = -100;

Se prezzo è pubblico, PHP accetta questa modifica.

Ma un prodotto con prezzo negativo non ha senso.

Se invece il prezzo è private, puoi obbligare chi usa la classe a passare da un metodo controllato:

public function setPrezzo($prezzo) {
if ($prezzo < 0) {
throw new InvalidArgumentException('Il prezzo non può essere negativo.');
}

$this->prezzo = $prezzo;
}

Così proteggi il tuo codice da valori sbagliati.

Ecco una versione più completa:

class Prodotto {
private $nome;
private $prezzo;

public function __construct($nome, $prezzo) {
$this->nome = $nome;
$this->setPrezzo($prezzo);
}

public function getNome() {
return $this->nome;
}

public function getPrezzo() {
return $this->prezzo;
}

public function setPrezzo($prezzo) {
if ($prezzo < 0) {
throw new InvalidArgumentException('Il prezzo non può essere negativo.');
}

$this->prezzo = $prezzo;
}

public function prezzoFormattato() {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Uso:

$prodotto = new Prodotto('T-shirt', 19.90);

echo $prodotto->prezzoFormattato();

Se provo a creare un prodotto con prezzo negativo:

$prodotto = new Prodotto('T-shirt', -10);

PHP solleverà un errore, perché il valore non è valido.

In un progetto vero, questa è una protezione molto utile.

Tipizzazione: rendere il codice più chiaro

Nelle versioni moderne di PHP puoi indicare il tipo dei dati che ti aspetti.

Esempio:

class Prodotto {
private string $nome;
private float $prezzo;

public function __construct(string $nome, float $prezzo) {
$this->nome = $nome;
$this->prezzo = $prezzo;
}

public function getNome(): string {
return $this->nome;
}

public function getPrezzo(): float {
return $this->prezzo;
}

public function prezzoFormattato(): string {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Qui stiamo dicendo a PHP:

private string $nome;

La proprietà $nome deve contenere una stringa.

private float $prezzo;

La proprietà $prezzo deve contenere un numero decimale.

public function getNome(): string

Questo metodo deve restituire una stringa.

public function getPrezzo(): float

Questo metodo deve restituire un numero decimale.

Per un principiante può sembrare più rigido, ma in realtà aiuta tantissimo.

Il codice diventa più leggibile, più prevedibile e meno soggetto a errori strani.

Ereditarietà: creare classi più specifiche

Un altro concetto importante della programmazione a oggetti è l’ereditarietà.

L’idea è questa: puoi creare una classe generica e poi creare classi più specifiche che ereditano da quella classe.

Esempio.

Hai una classe generica Utente:

class Utente {
protected string $nome;
protected string $email;

public function __construct(string $nome, string $email) {
$this->nome = $nome;
$this->email = $email;
}

public function getNome(): string {
return $this->nome;
}

public function getEmail(): string {
return $this->email;
}
}

Poi vuoi creare un utente amministratore.

Un amministratore è comunque un utente, ma con qualcosa in più.

class Amministratore extends Utente {
public function puoGestireSito(): bool {
return true;
}
}

Ora puoi fare:

$admin = new Amministratore('Sergio', 'sergio@example.com');

echo $admin->getNome();
echo $admin->getEmail();

if ($admin->puoGestireSito()) {
echo 'Questo utente può gestire il sito.';
}

La classe Amministratore eredita proprietà e metodi dalla classe Utente.

La parola chiave è:

extends

Significa: “questa classe estende un’altra classe”.

Quando usare l’ereditarietà?

L’ereditarietà è utile quando esiste davvero un rapporto “è un”.

Per esempio:

Amministratore è un Utente

Oppure:

ProdottoDigitale è un Prodotto

Oppure:

ArticoloBlog è un Contenuto

Facciamo un esempio con i prodotti.

class Prodotto {
protected string $nome;
protected float $prezzo;

public function __construct(string $nome, float $prezzo) {
$this->nome = $nome;
$this->prezzo = $prezzo;
}

public function getNome(): string {
return $this->nome;
}

public function prezzoFormattato(): string {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Ora creo un prodotto digitale:

class ProdottoDigitale extends Prodotto {
private string $linkDownload;

public function __construct(string $nome, float $prezzo, string $linkDownload) {
parent::__construct($nome, $prezzo);
$this->linkDownload = $linkDownload;
}

public function getLinkDownload(): string {
return $this->linkDownload;
}
}

Uso:

$ebook = new ProdottoDigitale(
'Guida PHP per principianti',
14.90,
'https://esempio.it/download/guida-php.pdf'
);

echo $ebook->getNome();
echo $ebook->prezzoFormattato();
echo $ebook->getLinkDownload();

Qui compare una cosa nuova:

parent::__construct($nome, $prezzo);

Significa: “richiama il costruttore della classe genitore”.

La classe ProdottoDigitale eredita da Prodotto, quindi può riutilizzare il costruttore della classe principale e aggiungere solo quello che le serve in più.

protected: una via di mezzo tra public e private

Prima abbiamo visto public e private.

Ora compare protected.

protected string $nome;
protected float $prezzo;

protected significa che la proprietà può essere usata dentro la classe e dentro le classi figlie.

Quindi:

  • public: accessibile da fuori;
  • private: accessibile solo nella classe dove viene dichiarata;
  • protected: accessibile nella classe e nelle classi che la estendono.

Nel dubbio, per iniziare puoi ragionare così:

  • usa private per proteggere i dati interni;
  • usa public per i metodi che devono essere usati dall’esterno;
  • usa protected quando stai lavorando con ereditarietà e vuoi permettere alle classi figlie di accedere a certe proprietà o metodi.

Polimorfismo: stesso metodo, comportamenti diversi

Altro termine che sembra complicato: polimorfismo.

In realtà il concetto è abbastanza semplice.

Significa che classi diverse possono avere lo stesso metodo, ma comportarsi in modo diverso.

Esempio.

Hai una classe base:

class MetodoPagamento {
public function paga(float $importo): string {
return 'Pagamento generico di ' . $importo . ' €';
}
}

Poi hai più metodi di pagamento specifici:

class CartaDiCredito extends MetodoPagamento {
public function paga(float $importo): string {
return 'Pagamento di ' . $importo . ' € effettuato con carta di credito.';
}
}

class BonificoBancario extends MetodoPagamento {
public function paga(float $importo): string {
return 'Ordine registrato. Attesa bonifico di ' . $importo . ' €.';
}
}

class PayPal extends MetodoPagamento {
public function paga(float $importo): string {
return 'Pagamento di ' . $importo . ' € effettuato tramite PayPal.';
}
}

Uso:

$pagamento = new CartaDiCredito();
echo $pagamento->paga(99.90);

$pagamento = new BonificoBancario();
echo $pagamento->paga(99.90);

$pagamento = new PayPal();
echo $pagamento->paga(99.90);

Il metodo si chiama sempre:

paga()

Ma il comportamento cambia in base all’oggetto usato.

Questo è utilissimo nei progetti reali, perché ti permette di scrivere codice flessibile.

Per esempio, in un e-commerce potresti gestire diversi sistemi di pagamento senza riscrivere tutta la logica dell’ordine.

Interfacce: stabilire un contratto

Le interfacce sono un altro concetto molto usato nella programmazione a oggetti.

Un’interfaccia stabilisce quali metodi una classe deve avere, senza dire esattamente come devono funzionare.

Esempio:

interface MetodoPagamentoInterface {
public function paga(float $importo): string;
}

Questa interfaccia dice:

“Qualunque classe voglia essere considerata un metodo di pagamento deve avere un metodo paga()”.

Ora creo alcune classi che implementano questa interfaccia:

class CartaDiCredito implements MetodoPagamentoInterface {
public function paga(float $importo): string {
return 'Pagamento con carta di credito: ' . $importo . ' €';
}
}

class BonificoBancario implements MetodoPagamentoInterface {
public function paga(float $importo): string {
return 'Pagamento tramite bonifico: ' . $importo . ' €';
}
}

La parola chiave è:

implements

Significa che la classe si impegna a rispettare il contratto definito dall’interfaccia.

A cosa serve tutto questo?

Serve a rendere il codice più sicuro e prevedibile.

Se una classe implementa MetodoPagamentoInterface, sai con certezza che avrà il metodo paga().

Puoi quindi scrivere una funzione così:

function completaOrdine(MetodoPagamentoInterface $metodoPagamento, float $totale): string {
return $metodoPagamento->paga($totale);
}

E usarla con diversi metodi di pagamento:

$carta = new CartaDiCredito();
echo completaOrdine($carta, 99.90);

$bonifico = new BonificoBancario();
echo completaOrdine($bonifico, 99.90);

La funzione completaOrdine() non deve sapere se stai usando carta, bonifico o altro. Le interessa solo che l’oggetto ricevuto rispetti l’interfaccia.

Questo è un modo molto pulito di progettare il codice.

Classi astratte: una base non utilizzabile direttamente

Una classe astratta è una classe pensata per essere estesa, ma non per essere usata direttamente.

Esempio:

abstract class Contenuto {
protected string $titolo;

public function __construct(string $titolo) {
$this->titolo = $titolo;
}

public function getTitolo(): string {
return $this->titolo;
}

abstract public function pubblica(): string;
}

Questa classe contiene una proprietà $titolo e un metodo concreto getTitolo().

Però contiene anche un metodo astratto:

abstract public function pubblica(): string;

Questo metodo non ha corpo. Dice solo che le classi figlie dovranno implementarlo.

Ora creo una classe Articolo:

class Articolo extends Contenuto {
public function pubblica(): string {
return 'Articolo pubblicato: ' . $this->titolo;
}
}

E una classe Pagina:

class Pagina extends Contenuto {
public function pubblica(): string {
return 'Pagina pubblicata: ' . $this->titolo;
}
}

Uso:

$articolo = new Articolo('Programmazione a oggetti in PHP');
echo $articolo->pubblica();

$pagina = new Pagina('Chi siamo');
echo $pagina->pubblica();

Non puoi fare:

$contenuto = new Contenuto('Titolo');

Perché Contenuto è astratta.

La classe astratta serve come base comune, non come oggetto concreto.

Static: proprietà e metodi senza creare oggetti

In PHP puoi creare anche metodi statici.

Un metodo statico può essere chiamato senza creare un oggetto.

Esempio:

class FormattatorePrezzo {
public static function euro(float $prezzo): string {
return number_format($prezzo, 2, ',', '.') . ' €';
}
}

Uso:

echo FormattatorePrezzo::euro(49.90);

Qui non ho fatto:

$formattatore = new FormattatorePrezzo();

Ho chiamato direttamente il metodo usando:

FormattatorePrezzo::euro()

La doppia coppia di due punti:

::

serve per accedere a metodi o proprietà statiche.

I metodi statici possono essere comodi per piccole utility, helper o funzioni di formattazione.

Però attenzione: non bisogna abusarne. Se usi tutto statico, rischi di perdere molti vantaggi della programmazione a oggetti.

Un esempio pratico completo: piccolo carrello in PHP

Adesso mettiamo insieme un po’ di concetti con un esempio più concreto: un piccolo carrello.

Creiamo prima la classe Prodotto.

class Prodotto {
private string $nome;
private float $prezzo;

public function __construct(string $nome, float $prezzo) {
$this->nome = $nome;
$this->setPrezzo($prezzo);
}

public function getNome(): string {
return $this->nome;
}

public function getPrezzo(): float {
return $this->prezzo;
}

public function setPrezzo(float $prezzo): void {
if ($prezzo < 0) {
throw new InvalidArgumentException('Il prezzo non può essere negativo.');
}

$this->prezzo = $prezzo;
}

public function prezzoFormattato(): string {
return number_format($this->prezzo, 2, ',', '.') . ' €';
}
}

Ora creiamo la classe Carrello.

class Carrello {
private array $prodotti = [];

public function aggiungiProdotto(Prodotto $prodotto): void {
$this->prodotti[] = $prodotto;
}

public function getProdotti(): array {
return $this->prodotti;
}

public function totale(): float {
$totale = 0;

foreach ($this->prodotti as $prodotto) {
$totale += $prodotto->getPrezzo();
}

return $totale;
}

public function totaleFormattato(): string {
return number_format($this->totale(), 2, ',', '.') . ' €';
}

public function stampaCarrello(): void {
echo '<ul>';

foreach ($this->prodotti as $prodotto) {
echo '<li>';
echo htmlspecialchars($prodotto->getNome()) . ' - ';
echo $prodotto->prezzoFormattato();
echo '</li>';
}

echo '</ul>';

echo '<strong>Totale: ' . $this->totaleFormattato() . '</strong>';
}
}

Ora usiamo le due classi:

$prodotto1 = new Prodotto('T-shirt', 19.90);
$prodotto2 = new Prodotto('Felpa', 49.90);
$prodotto3 = new Prodotto('Cappellino', 14.90);

$carrello = new Carrello();

$carrello->aggiungiProdotto($prodotto1);
$carrello->aggiungiProdotto($prodotto2);
$carrello->aggiungiProdotto($prodotto3);

$carrello->stampaCarrello();

Questo è un esempio molto semplice, ma già mostra bene il senso della programmazione a oggetti.

Abbiamo una classe Prodotto, che si occupa dei dati e dei comportamenti del prodotto.

Abbiamo una classe Carrello, che si occupa di gestire una lista di prodotti e calcolare il totale.

Ogni classe ha una responsabilità precisa.

Questo è molto importante.

Una buona classe non dovrebbe fare tutto. Dovrebbe occuparsi bene di una cosa.

Programmazione procedurale e programmazione a oggetti: qual è la differenza?

Per capire meglio la OOP, può essere utile confrontarla con la programmazione procedurale.

Con un approccio procedurale potresti scrivere:

$prodotti = [
[
'nome' => 'T-shirt',
'prezzo' => 19.90,
],
[
'nome' => 'Felpa',
'prezzo' => 49.90,
],
];

function calcolaTotale($prodotti) {
$totale = 0;

foreach ($prodotti as $prodotto) {
$totale += $prodotto['prezzo'];
}

return $totale;
}

echo calcolaTotale($prodotti);

Questo codice va benissimo.

La programmazione procedurale non è sbagliata.

Per piccoli script, snippet semplici o funzionalità rapide, spesso è anche più immediata.

Con la programmazione a oggetti, però, puoi strutturare meglio il codice quando il progetto cresce.

Invece di avere dati e funzioni separati, hai oggetti che contengono sia i dati sia i comportamenti.

Procedurale:

$prodotto['prezzo'];
calcolaPrezzo($prodotto);

A oggetti:

$prodotto->getPrezzo();
$prodotto->prezzoFormattato();

La differenza non è solo estetica. È organizzativa.

Quando conviene usare la programmazione a oggetti?

Conviene usare la programmazione a oggetti quando:

hai entità chiare da rappresentare, come utenti, prodotti, ordini, articoli, pagamenti, prenotazioni;

il codice inizia a crescere e vuoi tenerlo ordinato;

devi riutilizzare la stessa logica in più punti;

vuoi separare meglio le responsabilità;

vuoi rendere il codice più facile da testare;

stai lavorando con framework o librerie moderne;

vuoi costruire plugin o applicazioni più strutturate.

Per esempio, in un sito potresti avere classi come:

class Utente {}
class Prodotto {}
class Ordine {}
class Carrello {}
class MetodoPagamento {}
class Newsletter {}
class ModuloContatto {}
class ArticoloBlog {}

Ogni classe rappresenta un concetto del progetto.

Questo rende il codice più vicino al modo in cui ragioniamo nella realtà.

Quando invece può essere eccessiva?

Non tutto deve essere per forza a oggetti.

Se devi scrivere uno snippet molto semplice, per esempio una piccola funzione WordPress per aggiungere uno shortcode, puoi tranquillamente usare una funzione procedurale.

Esempio:

add_shortcode('anno_corrente', function () {
return date('Y');
});

Qui creare una classe sarebbe probabilmente eccessivo.

La programmazione a oggetti diventa davvero utile quando la logica cresce.

Il punto non è usare le classi sempre e comunque. Il punto è usarle quando ti aiutano a scrivere codice più chiaro, modulare e mantenibile.

Programmazione a oggetti e WordPress

WordPress nasce con una forte impronta procedurale. Moltissime funzioni di WordPress sono funzioni globali:

get_the_title();
get_permalink();
add_action();
add_filter();
wp_enqueue_script();

Però questo non significa che in WordPress non si usi la programmazione a oggetti.

Anzi, molti plugin professionali usano classi per organizzare meglio il codice.

Un esempio semplice può essere una classe che registra uno shortcode.

class SergioAnnoCorrenteShortcode {
public function __construct() {
add_shortcode('anno_corrente', [$this, 'render']);
}

public function render(): string {
return date('Y');
}
}

new SergioAnnoCorrenteShortcode();

Qui la classe viene inizializzata con:

new SergioAnnoCorrenteShortcode();

Nel costruttore viene registrato lo shortcode:

add_shortcode('anno_corrente', [$this, 'render']);

Questa parte:

[$this, 'render']

significa: “usa il metodo render di questo oggetto”.

Lo shortcode poi restituisce l’anno corrente:

public function render(): string {
return date('Y');
}

Per uno shortcode così semplice, una classe non è obbligatoria. Però se lo shortcode diventasse più complesso, con CSS, JavaScript, impostazioni, controlli e opzioni, una classe potrebbe aiutare molto.

Esempio WordPress più concreto: box autore

Immagina di voler creare uno shortcode per stampare un box autore nei tuoi articoli.

Con una classe potresti scrivere:

class SergioBoxAutore {
public function __construct() {
add_shortcode('sergio_box_autore', [$this, 'render']);
}

public function render(): string {
$nome = get_the_author();
$descrizione = get_the_author_meta('description');

if (empty($nome)) {
return '';
}

ob_start();
?>

<div class="sergio-box-autore">
<h3>Articolo scritto da <?php echo esc_html($nome); ?></h3>

<?php if (!empty($descrizione)) : ?>
<p><?php echo esc_html($descrizione); ?></p>
<?php endif; ?>
</div>

<?php
return ob_get_clean();
}
}

new SergioBoxAutore();

Poi nell’articolo potresti usare:

[sergio_box_autore]

Questo esempio è interessante perché fa vedere una cosa pratica: la classe non serve solo per esempi astratti. Può aiutarti anche nella gestione di piccole funzionalità WordPress.

Organizzare meglio il codice: una classe, una responsabilità

Uno degli errori più comuni quando si inizia con la programmazione a oggetti è creare classi enormi che fanno tutto.

Per esempio una classe del genere sarebbe poco sana:

class Sito {
public function gestisciUtenti() {}
public function gestisciProdotti() {}
public function inviaEmail() {}
public function generaFatture() {}
public function stampaArticoli() {}
public function creaBackup() {}
}

Questa classe si occupa di troppe cose.

Meglio dividere:

class GestoreUtenti {}
class GestoreProdotti {}
class ServizioEmail {}
class GeneratoreFatture {}
class GestoreArticoli {}
class SistemaBackup {}

Il principio è semplice: ogni classe dovrebbe avere una responsabilità chiara.

Se il nome della classe è troppo generico, spesso è un segnale d’allarme.

Una classe chiamata Prodotto è chiara.

Una classe chiamata Carrello è chiara.

Una classe chiamata EmailSender è chiara.

Una classe chiamata UtilityGeneralePerTutto probabilmente no.

Nomi chiari: una buona abitudine da subito

Quando lavori con la programmazione a oggetti, i nomi diventano importantissimi.

Una classe dovrebbe avere un nome chiaro, scritto di solito in PascalCase:

class Prodotto {}
class Carrello {}
class MetodoPagamento {}
class GestoreNewsletter {}

I metodi invece di solito usano il camelCase:

public function getNome() {}
public function prezzoFormattato() {}
public function aggiungiProdotto() {}
public function calcolaTotale() {}

Le proprietà seguono spesso la stessa logica:

private string $nomeProdotto;
private float $prezzoFinale;
private bool $disponibile;

Un buon nome riduce la necessità di commenti.

Questo:

$carrello->aggiungiProdotto($prodotto);

è molto più chiaro di:

$cart->add($p);

Soprattutto se il codice deve essere letto da qualcun altro, o da te stesso dopo tre mesi.

Un errore comune: confondere classe e oggetto

All’inizio è facile confondersi.

Guarda questo codice:

class Prodotto {
public string $nome;
}

Questa è la classe.

Non è ancora un prodotto reale.

Il prodotto reale nasce qui:

$prodotto = new Prodotto();

Poi posso assegnare il nome:

$prodotto->nome = 'T-shirt';

Quindi:

Prodotto

è la classe.

$prodotto

è l’oggetto.

Se creo due oggetti:

$prodotto1 = new Prodotto();
$prodotto1->nome = 'T-shirt';

$prodotto2 = new Prodotto();
$prodotto2->nome = 'Felpa';

Ho due oggetti diversi, creati dalla stessa classe.

Un altro errore comune: usare $this fuori dalla classe

$this si usa solo dentro una classe, quando vuoi riferirti all’oggetto corrente.

Questo è corretto:

class Prodotto {
private string $nome;

public function getNome(): string {
return $this->nome;
}
}

Questo invece non va bene:

echo $this->nome;

se lo scrivi fuori da una classe.

Fuori dalla classe devi usare la variabile dell’oggetto:

$prodotto = new Prodotto('T-shirt');

echo $prodotto->getNome();

Questa distinzione è importante.

Dentro la classe usi:

$this

Fuori dalla classe usi:

$nomeOggetto

Un esempio finale: gestione semplice di articoli blog

Facciamo un ultimo esempio, più vicino a chi lavora con contenuti web.

Vogliamo rappresentare un articolo blog.

class ArticoloBlog {
private string $titolo;
private string $autore;
private string $contenuto;
private DateTime $dataPubblicazione;

public function __construct(
string $titolo,
string $autore,
string $contenuto,
DateTime $dataPubblicazione
) {
$this->titolo = $titolo;
$this->autore = $autore;
$this->contenuto = $contenuto;
$this->dataPubblicazione = $dataPubblicazione;
}

public function getTitolo(): string {
return $this->titolo;
}

public function getAutore(): string {
return $this->autore;
}

public function getEstratto(int $lunghezza = 120): string {
if (strlen($this->contenuto) <= $lunghezza) {
return $this->contenuto;
}

return substr($this->contenuto, 0, $lunghezza) . '...';
}

public function getDataFormattata(): string {
return $this->dataPubblicazione->format('d/m/Y');
}

public function stampaAnteprima(): void {
echo '<article class="articolo-card">';
echo '<h2>' . htmlspecialchars($this->titolo) . '</h2>';
echo '<p class="meta">Scritto da ' . htmlspecialchars($this->autore) . ' il ' . $this->getDataFormattata() . '</p>';
echo '<p>' . htmlspecialchars($this->getEstratto()) . '</p>';
echo '</article>';
}
}

Uso:

$articolo = new ArticoloBlog(
'Programmazione a oggetti in PHP',
'Sergio Pinna',
'La programmazione a oggetti è un modo per organizzare meglio il codice PHP...',
new DateTime('2026-05-02')
);

$articolo->stampaAnteprima();

Questo esempio fa capire bene una cosa: una classe può rappresentare un elemento reale del progetto.

Un articolo ha:

$titolo
$autore
$contenuto
$dataPubblicazione

E può fare alcune cose:

getEstratto()
getDataFormattata()
stampaAnteprima()

Il codice diventa più leggibile perché rispecchia meglio la realtà del progetto.

I vantaggi principali della programmazione a oggetti in PHP

Arrivati a questo punto, possiamo riassumere i vantaggi principali.

Il primo vantaggio è l’ordine.

Una classe ti permette di raggruppare dati e funzioni che appartengono allo stesso concetto. Invece di avere funzioni sparse, hai blocchi più coerenti.

Il secondo vantaggio è il riutilizzo.

Una volta creata una classe Prodotto, puoi creare tanti prodotti diversi senza riscrivere ogni volta la stessa logica.

Il terzo vantaggio è la manutenzione.

Se una logica è centralizzata dentro una classe, modificarla diventa più semplice.

Il quarto vantaggio è la protezione dei dati.

Con private, protected, getter, setter e metodi controllati, puoi evitare modifiche sbagliate o incoerenti.

Il quinto vantaggio è la scalabilità.

Quando un progetto cresce, la programmazione a oggetti ti permette di aggiungere nuove funzionalità in modo più ordinato.

La programmazione a oggetti è obbligatoria?

No.

E questa è una cosa importante da dire.

Non devi usare la programmazione a oggetti per forza.

PHP ti permette di scrivere codice procedurale, codice a oggetti o anche una combinazione dei due.

In molti progetti reali succede proprio questo: alcune parti sono procedurali, altre sono organizzate in classi.

L’importante è scegliere lo strumento giusto per il problema giusto.

Se devi scrivere uno snippet di cinque righe, una funzione semplice può bastare.

Se stai creando un plugin, un gestionale, una libreria, un sistema e-commerce o una funzionalità articolata, allora la programmazione a oggetti può fare davvero la differenza.

Come iniziare senza bloccarsi

Il consiglio migliore è non partire subito da concetti troppo avanzati.

Non serve iniziare da design pattern, dependency injection, service container o architetture complesse.

Per iniziare bene, concentrati su questi elementi:

capire la differenza tra classe e oggetto;

creare proprietà e metodi;

usare il costruttore;

capire $this;

usare public e private;

creare piccoli esempi concreti;

riscrivere qualche funzione semplice sotto forma di classe.

Un buon esercizio è prendere qualcosa che conosci già e trasformarlo in una classe.

Per esempio:

class Cliente {}
class Preventivo {}
class ProgettoWeb {}
class ModuloContatto {}
class Articolo {}
class Categoria {}

Poi chiediti:

“Quali dati ha questo elemento?”

E quelli diventano proprietà.

Poi chiediti:

“Quali azioni può fare?”

E quelle diventano metodi.

Per esempio, un preventivo potrebbe avere:

private string $cliente;
private float $importo;
private bool $approvato;

E metodi come:

approva()
rifiuta()
getImportoFormattato()
stampaRiepilogo()

Questo modo di ragionare è la base della programmazione a oggetti.

Conclusione

La programmazione a oggetti in PHP può sembrare inizialmente più complessa rispetto al classico codice fatto di variabili e funzioni. In realtà, una volta capito il meccanismo, diventa un modo molto naturale per organizzare il codice.

Una classe è uno stampo.

Un oggetto è qualcosa di concreto creato da quello stampo.

Le proprietà sono i dati dell’oggetto.

I metodi sono le azioni che l’oggetto può compiere.

Il costruttore serve a inizializzare subito l’oggetto.

La visibilità permette di proteggere i dati.

L’ereditarietà consente di creare classi più specifiche partendo da classi più generiche.

Le interfacce aiutano a stabilire regole comuni tra classi diverse.

Non serve imparare tutto in un giorno. Il modo migliore per prendere confidenza con la OOP è partire da esempi piccoli e concreti: un prodotto, un carrello, un articolo, un utente, un modulo di contatto.

Quando inizi a vedere il codice non più come una sequenza di istruzioni, ma come un insieme di elementi che collaborano tra loro, la programmazione a oggetti comincia davvero ad avere senso.

E da quel momento il PHP smette di essere solo un linguaggio per “stampare pagine dinamiche” e diventa uno strumento molto più potente per costruire applicazioni, plugin, sistemi e funzionalità web ordinate, riutilizzabili e professionali.

Potrebbe interessarti anche...

CSS cross-browser per Chrome e Safari: come rendere il tuo sito più compatibile con i browser moderni

CSS cross-browser per Chrome e Safari: come rendere il tuo sito più compatibile con i browser moderni

06/07/2026

Guida pratica alla creazione di una patch CSS cross-browser per rendere un sito più compatibile con Google Chrome, Safari e i browser moderni. L’articolo spiega come migliorare la stabilità del layout, gestire prefissi come -webkit-, aggiungere fallback per proprietà avanzate come backdrop-filter, rendere più coerenti form e animazioni, ottimizzare gli effetti su mobile e rispettare le preferenze di accessibilità degli utenti.

Leggi l'articolo
Come creare una lente di ricerca in WordPress con mini pop-up senza plugin di ricerca

Come creare una lente di ricerca in WordPress con mini pop-up senza plugin di ricerca

29/06/2026

In questa guida vediamo come creare in WordPress una lente di ricerca cliccabile che apre un piccolo pop-up con una form di ricerca, senza usare plugin aggiuntivi. Lo snippet permette di inserire la lente tramite shortcode, collegarla alla ricerca nativa di WordPress e mostrare i risultati nelle pagine costruite con GeneratePress e GenerateBlocks, gestendo anche il caso in cui non vengano trovati risultati.

Leggi l'articolo
Come creare un sommario apribile nei singoli articoli WordPress senza plugin

Come creare un sommario apribile nei singoli articoli WordPress senza plugin

22/06/2026

In questa guida vediamo come creare un sommario apribile nei singoli articoli WordPress senza usare plugin. Attraverso uno snippet PHP, CSS e JavaScript, realizziamo un box responsive con link di ancoraggio, apertura e chiusura morbida e scroll fluido verso le sezioni del contenuto.

Leggi l'articolo

Hai una domanda su questo articolo?

Scrivimi pure nei commenti: ti risponderò il prima possibile con un suggerimento pratico.

...aspetta un attimo!

Hai un’idea per un sito, una pagina da migliorare o un progetto ancora da mettere a fuoco? Raccontamelo: possiamo capire insieme da dove partire.

Parlami della tua idea