Un fournisseur de pièces détachées automobile confie à une agence un fichier CSV de cent mille références à importer dans un CPT piece_detachee, avec pour chaque ligne une référence, une désignation, un prix et une compatibilité véhicule. Le fichier pèse plus de 40 Mo. Un script d’import classique, déclenché depuis l’administration via un appel AJAX, se heurte immédiatement à la limite de temps d’exécution du serveur bien avant d’avoir traité l’ensemble des lignes. La solution retenue est une commande WP-CLI dédiée, exécutée directement en ligne de commande, sans les contraintes de temps imposées à une requête HTTP classique.
Ne jamais charger tout le fichier en mémoire
La première erreur à éviter, même en ligne de commande où les limites de temps sont différentes, consiste à charger l’intégralité du fichier avec une fonction comme file() qui retourne un tableau contenant chaque ligne. Sur cent mille lignes, cela reste gérable, mais l’habitude est dangereuse : sur un fichier dix fois plus gros fourni plus tard par le même client, la consommation mémoire deviendrait problématique. La bonne pratique consiste à lire le fichier ligne par ligne avec un pointeur de fichier, quelle que soit sa taille.
La commande complète

class Import_Pieces_Command {
public function importer( $args, $assoc_args ) {
$chemin = $args[0];
if ( ! file_exists( $chemin ) ) {
WP_CLI::error( "Fichier introuvable : {$chemin}" );
}
$handle = fopen( $chemin, 'r' );
$entetes = fgetcsv( $handle, 0, ';' );
$total_lignes = $this->compter_lignes( $chemin ) - 1;
$barre = WP_CLI\Utils\make_progress_bar( 'Import des pièces', $total_lignes );
$lot = array();
$erreurs = 0;
$traites = 0;
while ( ( $ligne = fgetcsv( $handle, 0, ';' ) ) !== false ) {
$donnee = array_combine( $entetes, $ligne );
if ( empty( $donnee['reference'] ) || empty( $donnee['prix'] ) ) {
WP_CLI::log( "Ligne ignorée, référence ou prix manquant : " . implode( ',', $ligne ) );
$erreurs++;
$barre->tick();
continue;
}
$lot[] = $donnee;
if ( count( $lot ) >= 200 ) {
$this->traiter_lot( $lot );
$traites += count( $lot );
$lot = array();
}
$barre->tick();
}
if ( ! empty( $lot ) ) {
$this->traiter_lot( $lot );
$traites += count( $lot );
}
$barre->finish();
fclose( $handle );
WP_CLI::success( "{$traites} pièces importées, {$erreurs} lignes ignorées." );
}
private function traiter_lot( array $lot ) {
foreach ( $lot as $donnee ) {
$post_id = wp_insert_post( array(
'post_type' => 'piece_detachee',
'post_title' => sanitize_text_field( $donnee['designation'] ),
'post_status' => 'publish',
) );
if ( is_wp_error( $post_id ) ) {
WP_CLI::log( "Échec insertion pour {$donnee['reference']} : " . $post_id->get_error_message() );
continue;
}
update_post_meta( $post_id, 'reference', sanitize_text_field( $donnee['reference'] ) );
update_post_meta( $post_id, 'prix', (float) $donnee['prix'] );
}
}
private function compter_lignes( string $chemin ): int {
$handle = fopen( $chemin, 'r' );
$compte = 0;
while ( fgets( $handle ) !== false ) {
$compte++;
}
fclose( $handle );
return $compte;
}
}
WP_CLI::add_command( 'agence importer-pieces', array( new Import_Pieces_Command(), 'importer' ) );
Exécutée avec wp agence importer-pieces catalogue.csv, cette commande affiche une barre de progression en temps réel dans le terminal, journalise chaque ligne malformée sans interrompre le traitement des lignes suivantes, et regroupe les insertions par lots de deux cents pour limiter la fréquence des appels à des fonctions coûteuses comme le déclenchement des hooks associés à wp_insert_post().
Pourquoi ce découpage en lots de 200
Ce chiffre n’est pas arbitraire dans l’absolu, mais il constitue un bon point de départ observé en pratique sur ce type d’import : suffisamment grand pour amortir le coût fixe de chaque itération de boucle, suffisamment petit pour rester lisible dans les journaux et pour permettre, en cas d’erreur fatale imprévue en cours de traitement, de ne perdre au maximum que la progression d’un seul lot plutôt que l’ensemble de l’import.
Reprendre un import interrompu
Pour un import qui pourrait être interrompu, par exemple par une coupure réseau sur une connexion SSH distante, il est utile d’ajouter un paramètre optionnel permettant de reprendre à partir d’une ligne donnée :
wp agence importer-pieces catalogue.csv --reprendre-a=45000
Cette option, lue via WP_CLI\Utils\get_flag_value( $assoc_args, 'reprendre-a', 0 ), permet de sauter les lignes déjà traitées lors d’une exécution précédente sans avoir à retraiter l’ensemble du fichier depuis le début.
En résumé
WP-CLI, combiné à une lecture ligne par ligne du fichier source et à un découpage en lots raisonnables, permet d’importer des volumes de données largement hors de portée d’un traitement AJAX classique limité par le temps d’exécution d’une requête HTTP. La barre de progression et la journalisation des erreurs ligne par ligne, loin d’être un simple confort, deviennent indispensables dès que le volume dépasse ce qu’un humain peut surveiller à l’œil sans repère de progression.