Introduzione
Quando si lavora con PHP e un database tramite PDO, uno degli aspetti più utili da padroneggiare è l’uso dei placeholder nominativi nelle query preparate. Si tratta di una tecnica semplice ma molto potente, che rende il codice più leggibile, più sicuro e più facile da mantenere rispetto all’inserimento diretto dei valori nella stringa SQL.
Questo tutorial si concentra su un sotto-argomento pratico e spesso sottovalutato: come usare i placeholder nominativi con PDO per costruire query chiare e riutilizzabili. È un tema diverso dalla semplice prevenzione dell’SQL injection, perché qui il focus è anche su manutenibilità, leggibilità e organizzazione del codice.
Vedremo un esempio reale: un piccolo sistema per cercare utenti in un database con filtri opzionali, usando una query preparata con parametri nominativi. È un caso molto comune in applicazioni CRUD, pannelli admin e sistemi di ricerca avanzata.
Codice completo
<?php
declare(strict_types=1);
// Configurazione connessione
$dsn = ´mysql:host=localhost;dbname=demo_pdo;charset=utf8mb4´;
$user = ´root´;
$pass = ´´;
try {
// Connessione PDO con impostazioni consigliate
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
// Valori ricevuti, ad esempio da un form di ricerca
$nome = $_GET[´nome´] ?? ´´;
$ruolo = $_GET[´ruolo´] ?? ´´;
$citta = $_GET[´citta´] ?? ´´;
// Costruiamo dinamicamente i filtri
$sql = "SELECT id, nome, email, ruolo, citta
FROM utenti
WHERE 1=1";
$params = [];
if ($nome !== ´´) {
$sql .= " AND nome LIKE :nome";
$params[´:nome´] = ´%´ . $nome . ´%´;
}
if ($ruolo !== ´´) {
$sql .= " AND ruolo = :ruolo";
$params[´:ruolo´] = $ruolo;
}
if ($citta !== ´´) {
$sql .= " AND citta = :citta";
$params[´:citta´] = $citta;
}
$sql .= " ORDER BY nome ASC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$utenti = $stmt->fetchAll();
} catch (PDOException $e) {
die(´Errore database: ´ . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Ricerca utenti</title>
</head>
<body>
<h1>Risultati ricerca utenti</h1>
<form method="get">
<label>Nome:
<input type="text" name="nome" value="<?= htmlspecialchars($nome) ?>">
</label>
<br><br>
<label>Ruolo:
<input type="text" name="ruolo" value="<?= htmlspecialchars($ruolo) ?>">
</label>
<br><br>
<label>Città:
<input type="text" name="citta" value="<?= htmlspecialchars($citta) ?>">
</label>
<br><br>
<button type="submit">Cerca</button>
</form>
<hr>
<?php if (!empty($utenti)): ?>
<ul>
<?php foreach ($utenti as $utente): ?>
<li>
<strong><?= htmlspecialchars($utente[´nome´]) ?></strong>
- <?= htmlspecialchars($utente[´email´]) ?>
- <?= htmlspecialchars($utente[´ruolo´]) ?>
- <?= htmlspecialchars($utente[´citta´]) ?>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p>Nessun utente trovato.</p>
<?php endif; ?>
</body>
</html> Spiegazione
Il cuore dell’esempio è la costruzione della query in modo progressivo. Invece di scrivere una query diversa per ogni combinazione di filtri, partiamo da una base comune:
WHERE 1=1 è un trucco pratico che semplifica l’aggiunta di condizioni con AND. In questo modo non dobbiamo preoccuparci di capire se una condizione è la prima della lista o meno.
1. Preparazione della connessione
La connessione PDO viene creata con alcune impostazioni importanti:
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION: gli errori diventano eccezioni, quindi sono più facili da gestire.
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC: i risultati vengono restituiti come array associativi.
- PDO::ATTR_EMULATE_PREPARES => false: si preferiscono i prepared statements nativi, quando supportati dal driver.
2. Placeholder nominativi
Nel codice troviamo parametri come :nome, :ruolo e :citta. Sono chiamati placeholder nominativi perché hanno un nome esplicito, invece di usare posizioni numeriche come nei placeholder anonimi ?.
Questo approccio è utile perché rende immediata la comprensione della query. Se leggi :citta, capisci subito a quale dato si riferisce. In progetti reali, soprattutto quando le query diventano lunghe, questa caratteristica migliora molto la manutenzione.
3. Array dei parametri
Il secondo elemento fondamentale è l’array $params. Ogni volta che aggiungiamo una condizione SQL, inseriamo anche il valore corrispondente nell’array:
- $params[´:nome´] contiene il testo cercato con i wildcard per LIKE.
- $params[´:ruolo´] contiene il ruolo esatto da confrontare.
- $params[´:citta´] contiene la città da filtrare.
Quando eseguiamo $stmt->execute($params), PDO associa automaticamente ogni valore al suo placeholder.
4. Perché è meglio dell’interpolazione manuale
Un errore comune è costruire query così:
$sql = "SELECT * FROM utenti WHERE nome = ´$nome´"; Questa pratica è fragile, difficile da leggere e rischiosa. Con i placeholder nominativi, invece, il codice è separato dai dati. Questo migliora sia la sicurezza sia la struttura generale del progetto.
5. Gestione di filtri opzionali
Uno dei vantaggi più interessanti di questo approccio è la possibilità di gestire filtri opzionali. Se l’utente compila solo il campo nome, la query includerà solo quella condizione. Se compila tutti i campi, verranno aggiunti tutti i filtri. Se non compila nulla, la query restituirà tutti i record.
Questa tecnica è molto utile in:
- motori di ricerca interni;
- pannelli amministrativi;
- liste filtrabili;
- report personalizzati.
Best practice
- Usa sempre prepared statements quando inserisci dati esterni nella query.
- Preferisci i placeholder nominativi quando la query ha molti parametri: il codice risulta più leggibile.
- Non concatenare direttamente input utente nella stringa SQL.
- Valida i dati prima della query, soprattutto se provengono da form o URL.
- Usa htmlspecialchars in output quando mostri dati in HTML, per evitare problemi di escaping lato frontend.
- Imposta PDO::ATTR_ERRMODE su EXCEPTION per gestire gli errori in modo pulito.
- Separa la logica dal markup quando il progetto cresce: il codice diventa più facile da testare e mantenere.
Attenzione ai LIKE
Quando usi LIKE, aggiungi i wildcard nel valore del parametro e non nella stringa SQL. Per esempio:
$params[´:nome´] = ´%´ . $nome . ´%´; Questo mantiene la query chiara e il parametro ben definito.
Attenzione ai valori vuoti
Se un campo è vuoto, conviene non aggiungere la condizione alla query. In questo modo eviti confronti inutili e mantieni il comportamento della ricerca intuitivo per l’utente.
Riepilogo
I placeholder nominativi in PDO sono uno strumento semplice ma molto utile per scrivere query più chiare e strutturate. In questo tutorial abbiamo visto come:
- costruire una query con filtri opzionali;
- associare i valori con un array di parametri;
- usare LIKE e confronti esatti in modo ordinato;
- migliorare la leggibilità del codice rispetto alla concatenazione manuale.
Se stai sviluppando applicazioni PHP con database, imparare a gestire bene i placeholder nominativi ti aiuterà a scrivere codice più professionale, pulito e facile da estendere.
Approfondisci con risorse ufficiali
- Documentazione PHP su PDO: https://www.php.net/manual/it/book.pdo.php
- PDO::prepare: https://www.php.net/manual/it/pdo.prepare.php
- PDOStatement::execute: https://www.php.net/manual/it/pdostatement.execute.php
- PDO::setAttribute: https://www.php.net/manual/it/pdo.setattribute.php
- PDOStatement::fetchAll: https://www.php.net/manual/it/pdostatement.fetchall.php
Studiare la documentazione ufficiale è il modo migliore per capire come PDO si comporta con i diversi driver database, come MySQL, PostgreSQL e SQLite, e per adottare soluzioni davvero robuste nei tuoi progetti.
