Une page blanche, un import qui s’arrête au milieu, et ce message dans les logs : « Allowed memory size of 134217728 bytes exhausted ». Augmenter la limite fait parfois disparaître l’erreur… jusqu’à la prochaine fois. Dans ce guide, tu comprends ce que signifie vraiment l’erreur Allowed memory size exhausted en PHP, comment trouver la ligne qui consomme la mémoire, et comment la corriger durablement.

🐘 PHP 8💾 memory_limit🔍 Diagnostic🧩 WordPress & Composer⏱️ 18 min de lecture

Le message complet ressemble à ceci :

Allowed memory size exhausted : « Allowed memory size exhausted » en PHP : causes et solutions
« Allowed memory size exhausted » en PHP : causes et solutions
PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted
(tried to allocate 20480 bytes) in /var/www/html/import.php on line 42
Texte

L’erreur Allowed memory size exhausted est l’une des erreurs fatales les plus courantes en PHP. Elle surprend parce que le script fonctionnait très bien… avec moins de données. Elle apparaît typiquement lors d’un import CSV, d’un export, du traitement d’images, dans WordPress après l’installation d’une extension, ou pendant un composer update.

La tentation est grande de simplement augmenter la limite. Parfois c’est la bonne solution, souvent c’est un pansement. Voyons comment faire la différence.

Allowed memory size exhausted : les causes fréquentes
Les causes les plus fréquentes de l’erreur Allowed memory size exhausted.
🎯
Ce que tu vas apprendre
  • Lire et comprendre chaque partie du message d’erreur.
  • Trouver la limite actuelle et la modifier au bon endroit.
  • Mesurer la consommation de mémoire de ton script.
  • Corriger les causes réelles : gros tableaux, fichiers, requêtes SQL, boucles infinies.
  • Régler les cas particuliers de WordPress et de Composer.

Que signifie l’erreur Allowed memory size exhausted ?

Chaque script PHP dispose d’une limite de mémoire, définie par la directive memory_limit du php.ini. C’est un garde-fou : sans elle, un seul script mal écrit pourrait consommer toute la mémoire du serveur et faire tomber tous les sites qu’il héberge.

Quand ton script essaie d’utiliser plus de mémoire que la limite, PHP l’arrête immédiatement avec une erreur fatale. Décortiquons le message :

Partie du messageSignification
Allowed memory size of 134217728 bytesLa limite actuelle : 134 217 728 octets = 128 Mo
exhaustedLa limite est atteinte
tried to allocate 20480 bytesLa dernière demande de mémoire, celle qui a fait déborder le vase (ici 20 Ko)
in /var/www/html/import.php on line 42Le fichier et la ligne où la limite a été dépassée
💡
La ligne indiquée n’est pas forcément le coupable

Le message indique où la mémoire a débordé, pas forcément où elle a été consommée. Si un tableau grossit pendant toute une boucle, la limite peut être atteinte sur n’importe quelle ligne anodine. La petite valeur « tried to allocate 20480 bytes » est un indice : c’est l’accumulation qui pose problème, pas cette allocation-là.

Quelques conversions utiles pour lire les messages :

Octets dans le messageLimite
3355443232 Mo
6710886464 Mo
134217728128 Mo (valeur par défaut de PHP)
268435456256 Mo
536870912512 Mo

Étape 1 : connaître la limite actuelle

Avant de modifier quoi que ce soit, vérifie la valeur réellement appliquée :

# En ligne de commande
php -r "echo ini_get('memory_limit'), PHP_EOL;"
php --ini   # affiche le php.ini utilisé
Bash
<?php
// Dans une page web (la configuration peut être différente de la ligne de commande !)
echo 'Limite : ' . ini_get('memory_limit');
PHP
⚠️
La ligne de commande et le serveur web n’utilisent pas forcément le même php.ini

Sur beaucoup de serveurs Linux, il existe un php.ini pour la ligne de commande (/etc/php/8.x/cli/php.ini) et un autre pour PHP-FPM ou Apache (/etc/php/8.x/fpm/php.ini). Modifier le mauvais est l’erreur la plus fréquente : vérifie toujours la valeur depuis le contexte où l’erreur se produit.

Étape 2 : mesurer la consommation de mémoire

Pour savoir si ton script a besoin de 130 Mo ou de 3 Go (ce qui indiquerait un vrai problème), mesure-le. PHP fournit deux fonctions :

<?php
function memoire(string $etape): void
{
    printf(
        "%-25s actuelle : %6.1f Mo | pic : %6.1f Mo\n",
        $etape,
        memory_get_usage(true) / 1048576,
        memory_get_peak_usage(true) / 1048576
    );
}

memoire('Début');
$lignes = file('clients.csv');          // charge tout le fichier en mémoire
memoire('Après lecture du CSV');
$donnees = array_map('str_getcsv', $lignes);
memoire('Après analyse');
PHP
Début                     actuelle :    2.0 Mo | pic :    2.0 Mo
Après lecture du CSV      actuelle :   86.0 Mo | pic :   86.0 Mo
Après analyse             actuelle :  412.0 Mo | pic :  414.0 Mo
Texte

Le coupable est immédiatement visible : transformer chaque ligne en tableau multiplie la mémoire par cinq. Un fichier CSV de 40 Mo peut facilement occuper plusieurs centaines de Mo une fois chargé dans des tableaux PHP, car chaque valeur et chaque élément de tableau a un coût supplémentaire.

💡
Pour aller plus loin : le profilage

Sur un gros projet, l’extension Xdebug (mode profile) ou des outils comme Blackfire et SPX montrent la consommation de mémoire de chaque fonction. Indispensable quand la fuite est cachée dans le code d’une librairie.

Étape 3 : corriger la cause réelle

Voici les causes les plus fréquentes de l’erreur Allowed memory size exhausted, et la bonne correction pour chacune.

Cause n°1 : charger un gros fichier d’un seul coup

file(), file_get_contents() ou json_decode() sur un gros fichier chargent tout en mémoire. Lis plutôt le fichier ligne par ligne :

<?php
// ❌ Tout le fichier en mémoire
$lignes = file('clients.csv');
foreach ($lignes as $ligne) {
    traiter(str_getcsv($ligne));
}

// ✅ Une seule ligne en mémoire à la fois
$fichier = new SplFileObject('clients.csv');
$fichier->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);
foreach ($fichier as $colonnes) {
    traiter($colonnes);
}
PHP

Avec la seconde version, un fichier de 10 Go consomme autant de mémoire qu’un fichier de 10 Ko.

Cause n°2 : récupérer tous les résultats SQL avec fetchAll()

fetchAll() place tous les résultats dans un tableau. Sur une table d’un million de lignes, c’est l’erreur assurée.

<?php
// ❌ Un million de lignes dans un tableau
$clients = $pdo->query('SELECT * FROM clients')->fetchAll();

// ✅ Une ligne à la fois
$stmt = $pdo->query('SELECT id, email FROM clients');
while ($client = $stmt->fetch(PDO::FETCH_ASSOC)) {
    traiter($client);
}
PHP

Attention : avec MySQL, PDO récupère par défaut tout le résultat côté client (requête bufferisée), même avec fetch(). Pour les très gros volumes, désactive ce mode ou traite par lots :

<?php
// Option A : requête non bufferisée (une seule requête à la fois sur cette connexion)
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

// Option B : traitement par lots de 1 000 lignes, basé sur l'identifiant
$dernierId = 0;
do {
    $stmt = $pdo->prepare('SELECT id, email FROM clients WHERE id > ? ORDER BY id LIMIT 1000');
    $stmt->execute([$dernierId]);
    $lot = $stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($lot as $client) {
        traiter($client);
        $dernierId = $client['id'];
    }
} while (count($lot) === 1000);
PHP
✅
Ne sélectionne que les colonnes nécessaires

SELECT * ramène aussi les colonnes de texte long (descriptions, contenus HTML) dont tu n’as pas besoin. Lister les colonnes réduit la mémoire et accélère la requête.

Cause n°3 : accumuler des données dans une boucle

<?php
// ❌ Le tableau $resultats grossit à chaque tour
$resultats = [];
foreach (lireCommandes() as $commande) {
    $resultats[] = calculerFacture($commande);
}
genererPdf($resultats);

// ✅ Un générateur : chaque élément est produit, utilisé, puis libéré
function factures(iterable $commandes): Generator
{
    foreach ($commandes as $commande) {
        yield calculerFacture($commande);
    }
}

foreach (factures(lireCommandes()) as $facture) {
    ecrireDansExport($facture);
}
PHP

Les générateurs (mot-clé yield) sont l’outil le plus puissant contre l’erreur de mémoire : au lieu de construire un énorme tableau, ils produisent les valeurs une par une, à la demande.

Cause n°4 : une boucle ou une récursion infinie

Si ton script consomme toujours toute la mémoire, quelle que soit la limite (128 Mo, 512 Mo, 2 Go…), ce n’est pas un problème de taille : c’est presque toujours une boucle infinie qui remplit un tableau, ou une récursion sans fin.

<?php
// ❌ Récursion infinie : une catégorie qui est sa propre parente
function cheminCategorie(array $categorie, array $toutes): array
{
    $parent = $toutes[$categorie['parent_id']] ?? null;
    return $parent ? [...cheminCategorie($parent, $toutes), $categorie['nom']] : [$categorie['nom']];
}

// ✅ On garde la trace des éléments déjà visités
function cheminCategorieSur(array $categorie, array $toutes, array $vus = []): array
{
    if (isset($vus[$categorie['id']])) {
        throw new RuntimeException('Boucle détectée dans les catégories');
    }
    $vus[$categorie['id']] = true;
    $parent = $toutes[$categorie['parent_id']] ?? null;
    return $parent
        ? [...cheminCategorieSur($parent, $toutes, $vus), $categorie['nom']]
        : [$categorie['nom']];
}
PHP
🚫
Augmenter la limite ne corrigera jamais une boucle infinie

Avec memory_limit = -1 (aucune limite), une boucle infinie consommera toute la mémoire du serveur, ralentira ou plantera les autres sites, et finira tuée par le système. La limite de mémoire est ton alliée : elle t’alerte vite.

Cause n°5 : le traitement d’images

Une photo JPEG de 4 Mo peut sembler légère, mais pour la redimensionner, GD la décompresse entièrement. Une image de 6 000 × 4 000 pixels occupe alors environ 6 000 × 4 000 × 4 octets, soit 96 Mo, et le double si tu crées une copie redimensionnée.

<?php
// Estimer la mémoire nécessaire avant de charger l'image
[$largeur, $hauteur] = getimagesize($chemin);
$memoireNecessaire = $largeur * $hauteur * 4 * 1.8; // marge pour la copie
if ($memoireNecessaire > 200 * 1048576) {
    throw new RuntimeException('Image trop grande pour être traitée.');
}

$source = imagecreatefromjpeg($chemin);
$miniature = imagescale($source, 800);
imagejpeg($miniature, $destination, 85);

// Libérer la mémoire immédiatement (important dans une boucle)
imagedestroy($source);
imagedestroy($miniature);
PHP

Limite aussi la taille des images acceptées à l’envoi, ou utilise l’extension Imagick, plus économe sur les gros fichiers.

Cause n°6 : les scripts longs et les fuites de mémoire

Dans un script qui tourne longtemps (import nocturne, consommateur de file d’attente, while (true)), les petites accumulations finissent par compter : journaux en mémoire, cache d’un ORM, références circulaires entre objets.

<?php
foreach ($lots as $i => $lot) {
    traiterLot($lot);

    unset($lot);            // libère la variable
    if ($i % 100 === 0) {
        gc_collect_cycles(); // force le nettoyage des références circulaires
        $entityManager?->clear(); // vide le cache de Doctrine si tu l'utilises
    }
}
PHP
💡
Le bon réflexe pour les tâches longues

Plutôt que de faire tourner un processus PHP pendant des heures, traite un nombre limité d’éléments puis termine le script : un gestionnaire comme Supervisor ou systemd le relance automatiquement, avec une mémoire toute neuve. C’est ce que font les workers de Laravel (--max-jobs) ou Symfony Messenger (--memory-limit).

Étape 4 : augmenter memory_limit, quand c’est justifié

Si tes mesures montrent qu’un traitement légitime a besoin de 200 Mo (une génération de PDF complexe, un gros export ponctuel), augmenter la limite est la bonne solution. Voici où le faire, du plus global au plus local :

MéthodePortéeExemple
php.iniTout le serveur (ou toute la version de PHP)memory_limit = 256M
Pool PHP-FPMUn site précisphp_admin_value[memory_limit] = 256M
.htaccess (Apache + mod_php)Un dossierphp_value memory_limit 256M
.user.ini (PHP-FPM, CGI)Un dossiermemory_limit = 256M
ini_set() dans le codeUn script précisini_set('memory_limit', '512M');
Ligne de commandeUne exécutionphp -d memory_limit=1G import.php

La méthode la plus fine est souvent la meilleure : augmente la limite uniquement pour le script qui en a besoin.

<?php
// export-annuel.php : ce script précis a besoin de plus de mémoire
ini_set('memory_limit', '512M');
PHP
⚠️
Après une modification du php.ini, redémarre le service

Les modifications du php.ini ne sont prises en compte qu’au redémarrage de PHP-FPM ou d’Apache (sudo systemctl restart php8.4-fpm). Le fichier .user.ini, lui, est relu automatiquement, mais avec un délai (5 minutes par défaut). Sur un hébergement mutualisé, la limite se règle souvent dans le panneau de contrôle et ne peut pas dépasser le maximum de ton offre.

Le cas de WordPress

Dans WordPress, l’erreur apparaît souvent après l’installation d’une extension ou d’un thème lourd, ou dans l’administration. WordPress a sa propre limite, définie dans wp-config.php (avant la ligne « C’est tout, ne touchez pas à ce qui suit ! ») :

<?php
// wp-config.php
define('WP_MEMORY_LIMIT', '256M');     // partie publique du site
define('WP_MAX_MEMORY_LIMIT', '512M'); // administration (imports, mises à jour…)
PHP

Ces valeurs ne peuvent pas dépasser le memory_limit du serveur. Si l’erreur persiste après une augmentation raisonnable, désactive les extensions une par une (ou renomme temporairement le dossier wp-content/plugins via FTP) pour trouver la responsable : une extension qui consomme 400 Mo a un problème, ce n’est pas à ton serveur de compenser.

Le cas de Composer

composer update peut consommer beaucoup de mémoire pour résoudre les dépendances d’un gros projet. Composer 2 est bien plus économe que Composer 1, mais tu peux lever sa limite pour une exécution :

# Linux / macOS
COMPOSER_MEMORY_LIMIT=-1 composer update

# Windows (PowerShell)
$env:COMPOSER_MEMORY_LIMIT = "-1"; composer update
Bash

Ici, lever la limite est acceptable : c’est un outil de développement lancé ponctuellement, pas un script exposé aux visiteurs. Si tu utilises encore Composer 1, mets-le à jour avec composer self-update --2.

Récapitulatif : quelle solution pour quel cas ?

SituationBonne solution
Import ou lecture d’un gros fichierLecture ligne par ligne (SplFileObject, fgets, fgetcsv)
Requête SQL avec beaucoup de lignesfetch() en boucle, requête non bufferisée ou lots
Construction d’un gros tableauGénérateur (yield)
La mémoire explose quelle que soit la limiteChercher une boucle ou une récursion infinie
Redimensionnement d’imagesLimiter la taille, imagedestroy(), Imagick
Script qui tourne longtempsunset(), gc_collect_cycles(), redémarrage régulier
Besoin légitime et mesuréAugmenter memory_limit pour ce script uniquement
WordPressWP_MEMORY_LIMIT et recherche de l’extension gourmande
ComposerCOMPOSER_MEMORY_LIMIT=-1 et Composer 2
Allowed memory size exhausted : les points essentiels à retenir
Les points essentiels à retenir de ce guide.

FAQ

Quelle valeur de memory_limit choisir ?

La valeur par défaut de PHP (128 Mo) suffit à la grande majorité des sites. 256 Mo est courant pour WordPress avec plusieurs extensions ou pour les frameworks. Au-delà de 512 Mo pour une page web, c’est le signe d’un code à optimiser plutôt que d’une limite trop basse.

Peut-on mettre memory_limit = -1 ?

Pour un outil en ligne de commande lancé ponctuellement (Composer, un script de migration surveillé), oui. Pour un site web, jamais : un seul script défaillant pourrait consommer toute la mémoire du serveur et faire tomber tous les sites qu’il héberge.

Quelle différence avec l’erreur « Out of memory » ?

« Allowed memory size exhausted » signifie que PHP a atteint sa propre limite (memory_limit). « Out of memory » signifie que le système n’a plus de mémoire à donner à PHP : le serveur lui-même est saturé, et c’est un problème d’infrastructure (trop de processus, pas assez de RAM).

L’erreur apparaît seulement en production, pourquoi ?

Soit la limite y est plus basse (vérifie avec ini_get('memory_limit') dans une page), soit il y a beaucoup plus de données qu’en local. Teste ton code avec un volume de données réaliste avant de le mettre en ligne.

Conclusion

L’erreur Allowed memory size exhausted n’est pas une fatalité, et augmenter la limite n’est qu’une solution parmi d’autres. La bonne démarche : lire le message, mesurer la consommation avec memory_get_peak_usage(), identifier la cause (fichier chargé d’un coup, fetchAll(), tableau qui grossit, boucle infinie, image géante), puis traiter les données en flux plutôt que tout en mémoire. Augmente memory_limit seulement quand un besoin légitime est mesuré, et seulement là où c’est nécessaire.

Pour tester tes scripts dans un environnement proche de la production, notre guide Docker pour développeur web te permet de reproduire la configuration de ton serveur sur ton poste. La documentation de memory_limit sur php.net détaille la directive et ses unités.

Comments

No comments yet. Why don’t you start the discussion?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *