logo oujood
🔍

Cas d'utilisation de set_local_infile_handler() en PHP MySQLi

set_local_infile_handler() permet d'intercepter chaque ligne lue par LOAD DATA LOCAL INFILE avant qu'elle n'atteigne MySQL. Ce guide montre comment s'en servir pour transformer, filtrer ou archiver des données à l'importation.

OUJOOD.COM

Pourquoi utiliser set_local_infile_handler() ?

Quand vous importez un fichier texte avec LOAD DATA LOCAL INFILE, MySQL lit et insère les lignes sans que vous puissiez intervenir. mysqli_set_local_infile_handler() ouvre une brèche dans ce processus : vous enregistrez une fonction de rappel (callback) que PHP appelle ligne par ligne pendant la lecture. C'est là que vous transformez, filtrez ou journalisez les données — avant qu'elles n'arrivent en base.

Cette approche reste utile en 2026 pour les pipelines d'importation sur mesure, surtout quand les fichiers sources sont mal formatés ou soumis à des règles métier que LOAD DATA ne peut pas appliquer seul. Elle fonctionne aussi bien en style orienté objet qu'en style procédural.

Cas d'utilisation 1 : transformer les données avant l'importation

Imaginons un fichier CSV où les noms sont en minuscules, alors que la base attend des majuscules. Plutôt que de pré-traiter le fichier à part, on délègue la transformation au callback. Voici les deux approches PHP.

Approche orientée objet

La fonction transformerDonnees lit chaque ligne du flux, met le nom en majuscules, puis l'insère dans la table personne via une requête préparée.

  📋 Copier le code

<?php

// Connexion MySQLi en mode objet
$mysqli = new mysqli('localhost', 'utilisateur', 'motdepasse', 'ma_base');

// Le callback reçoit le flux de données ligne par ligne
function transformerDonnees($flux, $ligne, $fin) {
    // Lire la ligne courante depuis le flux
    $donnee = fgets($flux);

    if ($donnee === false) {
        return false; // Fin du fichier ou erreur de lecture
    }

    // Découper la ligne CSV en champs
    $champs = str_getcsv(trim($donnee));

    if (count($champs) === 4) {
        [$nom, $prenom, $email, $age] = $champs;

        // Normalisation : nom en majuscules
        $nom = strtoupper($nom);

        // Requête préparée pour éviter les injections SQL
        global $mysqli;
        $stmt = $mysqli->prepare(
            "INSERT INTO personne (nom, prenom, email, age) VALUES (?, ?, ?, ?)"
        );
        $stmt->bind_param('sssi', $nom, $prenom, $email, $age);
        $stmt->execute();
        $stmt->close();

        return true;
    }

    return false; // Ligne ignorée si le format est incorrect
}

// Enregistrer le callback avant d'exécuter LOAD DATA LOCAL INFILE
$mysqli->set_local_infile_handler('transformerDonnees');

// MySQL lit personnes.csv et appelle notre callback pour chaque ligne
$mysqli->query("LOAD DATA LOCAL INFILE 'personnes.csv' INTO TABLE personne FIELDS TERMINATED BY ','");
?>

Approche procédurale

Le comportement est identique. La différence : mysqli_set_local_infile_handler() prend la connexion en premier argument, contrairement à la méthode objet.

  📋 Copier le code

<?php

// Connexion MySQLi en mode procédural
$lien = mysqli_connect('localhost', 'utilisateur', 'motdepasse', 'ma_base');

function transformerDonnees($flux, $ligne, $fin) {
    $donnee = fgets($flux);

    if ($donnee === false) {
        return false;
    }

    $champs = str_getcsv(trim($donnee));

    if (count($champs) === 4) {
        [$nom, $prenom, $email, $age] = $champs;

        // Mise en majuscules avant insertion
        $nom = strtoupper($nom);

        global $lien;
        $stmt = mysqli_prepare(
            $lien,
            "INSERT INTO personne (nom, prenom, email, age) VALUES (?, ?, ?, ?)"
        );
        mysqli_stmt_bind_param($stmt, 'sssi', $nom, $prenom, $email, $age);
        mysqli_stmt_execute($stmt);
        mysqli_stmt_close($stmt);

        return true;
    }

    return false;
}

// Premier argument : la connexion MySQLi
mysqli_set_local_infile_handler($lien, 'transformerDonnees');

mysqli_query($lien, "LOAD DATA LOCAL INFILE 'personnes.csv' INTO TABLE personne FIELDS TERMINATED BY ','");
?>

Cas d'utilisation 2 : archiver les données dans un fichier local

Le callback ne sert pas uniquement à insérer en base. On peut aussi l'utiliser pour écrire chaque ligne dans un fichier texte — journal d'importation, sauvegarde intermédiaire, trace de débogage. Voici comment archiver les lignes dans archive.txt au fil de la lecture.

Approche orientée objet

Le callback écrit chaque ligne en mode ajout (FILE_APPEND) sans écraser les entrées précédentes.

  📋 Copier le code

<?php

$mysqli = new mysqli('localhost', 'utilisateur', 'motdepasse', 'ma_base');

// Chemin de l'archive — à adapter selon l'environnement
define('FICHIER_ARCHIVE', __DIR__ . '/archive.txt');

function sauvegarderLigne($flux, $ligne, $fin) {
    $donnee = fgets($flux);

    if ($donnee === false) {
        return false;
    }

    // Écriture en mode ajout avec verrouillage
    file_put_contents(FICHIER_ARCHIVE, trim($donnee) . PHP_EOL, FILE_APPEND | LOCK_EX);

    return true;
}

$mysqli->set_local_infile_handler('sauvegarderLigne');

$mysqli->query("LOAD DATA LOCAL INFILE 'personnes.csv' INTO TABLE personne FIELDS TERMINATED BY ','");

echo "Importation terminée. Données archivées dans : " . FICHIER_ARCHIVE;
?>

Approche procédurale

Le drapeau LOCK_EX dans file_put_contents évite les écritures concurrentes si le script tourne en parallèle.

  📋 Copier le code

<?php

$lien = mysqli_connect('localhost', 'utilisateur', 'motdepasse', 'ma_base');

define('FICHIER_ARCHIVE', __DIR__ . '/archive.txt');

function sauvegarderLigne($flux, $ligne, $fin) {
    $donnee = fgets($flux);

    if ($donnee === false) {
        return false;
    }

    file_put_contents(FICHIER_ARCHIVE, trim($donnee) . PHP_EOL, FILE_APPEND | LOCK_EX);

    return true;
}

mysqli_set_local_infile_handler($lien, 'sauvegarderLigne');

mysqli_query($lien, "LOAD DATA LOCAL INFILE 'personnes.csv' INTO TABLE personne FIELDS TERMINATED BY ','");

echo "Importation terminée. Données archivées dans : " . FICHIER_ARCHIVE;
?>

Autres cas d'utilisation

Dès qu'on a besoin d'intervenir sur les données pendant leur lecture — pas avant, pas après —, set_local_infile_handler() est la bonne entrée. Trois scénarios reviennent régulièrement en production.

Filtrer les lignes invalides

Les fichiers CSV réels contiennent souvent des lignes vides, des doublons ou des valeurs hors plage. Le callback permet de ne laisser passer que les lignes valides, sans toucher à la base. L'exemple ci-dessous vérifie le format de l'email et que l'âge est un entier entre 0 et 120 — les lignes qui ne passent pas sont simplement ignorées.

  📋 Copier le code

<?php

$mysqli = new mysqli('localhost', 'utilisateur', 'motdepasse', 'ma_base');

function filtrerLignesInvalides($flux, $ligne, $fin) {
    $donnee = fgets($flux);

    if ($donnee === false) {
        return false;
    }

    $champs = str_getcsv(trim($donnee));

    if (count($champs) !== 4) {
        return false; // Structure incorrecte
    }

    [$nom, $prenom, $email, $age] = $champs;

    // Validation de l'email avec le filtre PHP natif
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return false;
    }

    // L'âge doit être un entier entre 0 et 120
    $age = (int) $age;
    if ($age < 0 || $age > 120) {
        return false;
    }

    $stmt = $mysqli->prepare(
        "INSERT INTO personne (nom, prenom, email, age) VALUES (?, ?, ?, ?)"
    );
    $stmt->bind_param('sssi', $nom, $prenom, $email, $age);
    $stmt->execute();
    $stmt->close();

    return true;
}

$mysqli->set_local_infile_handler('filtrerLignesInvalides');
$mysqli->query("LOAD DATA LOCAL INFILE 'personnes.csv' INTO TABLE personne FIELDS TERMINATED BY ','");
?>

Appliquer des règles métier

Certaines contraintes ne peuvent pas s'exprimer dans le schéma MySQL : un client ne peut pas avoir deux comptes avec le même email, un produit ne peut pas avoir un stock négatif. Le callback est l'endroit pour les vérifier avant toute insertion, sans mélanger cette logique avec le reste de l'application.

Journaliser les rejets

Les lignes ignorées disparaissent en silence par défaut. En les écrivant dans un fichier de log avec la raison du rejet, on peut auditer l'importation après coup et corriger les données sources. Combiné au filtrage, cela donne un pipeline d'importation traçable.

Voir aussi : référence complète de mysqli_set_local_infile_handler().


Par carabde | Mis à jour le 16 mai 2026