Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, int given. Ce message, apparu du jour au lendemain après une montée de version PHP 7.4 vers 8.0, a mis à l’arrêt le script maison de génération de sitemap d’un site catalogue. Aucune modification de code n’avait pourtant été faite sur ce script depuis des mois.
Ce cas illustre un piège classique de la migration vers PHP 8 : du code qui fonctionnait « silencieusement mal » depuis longtemps devient soudain bloquant, parce que PHP a changé la sévérité d’une erreur qu’il tolérait auparavant. Voici comment diagnostiquer et corriger ce cas précis, sans toucher à la question de la limite des 50 000 URL par fichier, qui est un tout autre sujet.
Symptôme
Le script de sitemap, exécuté via une tâche planifiée wp_schedule_event, générait chaque nuit un fichier XML listant les produits du catalogue. Après la migration serveur vers PHP 8.0, l’exécution s’arrête net avec l’erreur ci-dessus, et aucun fichier sitemap.xml n’est produit cette nuit-là. Le fichier de la veille reste en place, ce qui masque le problème pendant plusieurs jours avant qu’il ne soit repéré.
Diagnostic

Le code fautif ressemblait à ceci :
function generer_urls_produits() {
$produits = get_transient( 'cache_produits_sitemap' );
// Le transient a expiré : $produits vaut false, pas un tableau
if ( count( $produits ) > 0 ) {
foreach ( $produits as $produit ) {
// construction du sitemap
}
}
}
Avant PHP 8, appeler count() sur une valeur qui n’est ni un tableau ni un objet implémentant l’interface Countable (comme false ou null) déclenchait un simple avertissement (Warning), et la fonction renvoyait 1. Le script continuait de s’exécuter, produisant potentiellement un sitemap incomplet, mais sans jamais s’arrêter. Depuis PHP 8.0, ce même appel déclenche une TypeError fatale qui interrompt immédiatement le script.
Correctif
La solution consiste à sécuriser le type de la variable avant tout appel à count(), en s’assurant qu’elle est bien un tableau :
function generer_urls_produits() {
$produits = get_transient( 'cache_produits_sitemap' );
if ( ! is_array( $produits ) ) {
$produits = array();
}
if ( count( $produits ) > 0 ) {
foreach ( $produits as $produit ) {
// construction du sitemap
}
}
}
Cette vérification explicite avec is_array() avant l’appel à count() élimine la cause profonde plutôt que de simplement supprimer l’erreur en désactivant l’affichage des erreurs PHP, ce qui aurait masqué le problème sans le résoudre.
Où chercher d’autres occurrences du même piège
- Toute variable issue d’une fonction pouvant renvoyer
falsecommeget_transient(),get_option()avec une valeur par défaut mal définie, ou un résultat de requête SQL vide - Les tableaux issus de
json_decode()sans vérification du résultat en cas de JSON invalide, qui renvoienull - Les valeurs issues de
$_GETou$_POSTpassées directement àcount()sans validation préalable
Prévention
Avant toute montée de version PHP majeure, un passage du code par WP-CLI avec la commande wp eval-file sur un environnement de préproduction permet de repérer une bonne partie de ces cas. Ajouter une vérification systématique de type avant chaque appel à une fonction comme count(), array_map() ou implode() évite ce genre de régression, qui touche particulièrement le code ancien écrit à une époque où PHP tolérait ces approximations.
La leçon retenue sur ce projet : une migration PHP majeure doit toujours s’accompagner d’une exécution complète des scripts planifiés en environnement de test, pas seulement d’un chargement des pages du site.
En résumé
PHP 8 n’a rien cassé qui fonctionnait correctement : il a simplement cessé de tolérer silencieusement un usage incorrect de count() qui produisait déjà, en creux, des sitemaps parfois incomplets sans que personne ne le remarque. Ce type de régression, purement lié à la sévérité des erreurs et non à une suppression de fonctionnalité, mérite une attention particulière à chaque montée de version PHP.