PHP, PDO e Database: usare i placeholder nominativi in modo efficace

by theArchitect
SHARE
PHP, PDO e Database: usare i placeholder nominativi in modo efficace
© Guida-HTML5.it

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.

SHARE