Mise à jour 4.4.6 plugins/dist et plugins/auto, bug dans referers

Titre : [statistiques] table_objet_sql() reçoit un array dans referenceurs.php → fatal PHP 8

Contexte

  • SPIP : x.y.z (prod)
  • PHP : 8.x
  • Plugins noyau : statistiques (version livrée avec x.y.z)
  • Plugins : statsobjets 2.1.0, referer_spam 1.2.1
  • Hébergeur/OS : …

Reproduction

  1. Activer Statistiques et StatsObjets.
  2. Aller dans : Activités → Statistiques → Liens entrants.
  3. Avec certains objets passés par l’interface, l’erreur survient.

Résultat obtenu

table_objet_sql(): Argument #1 ($type) must be of type string, array given
…/ecrire/base/objets.php:1074
appelé depuis …/plugins-dist/statistiques/inc/referenceurs.php:191

Résultat attendu
Affichage normal des référents.

Analyse
referenceurs.php::referes() peut recevoir $objets sous forme de tableau (extraction depuis spip_referers_objets ou appels externes). La boucle foreach ($objets as $objet) envoie ensuite un élément potentiellement tableau à table_objet_sql($objet), qui attend une chaîne.

Correctif proposé (défensif)

  • Extraire proprement la colonne objet depuis sql_allfetsel.
  • Aplatir/normaliser $objets en tableau de chaînes.
  • Passer chaque $objet par objet_type() avant table_objet_sql().

Diff minimal sur plugins-dist/statistiques/inc/referenceurs.php :

--- a/plugins-dist/statistiques/inc/referenceurs.php
+++ b/plugins-dist/statistiques/inc/referenceurs.php
@@ function referes(string $referermd5, $objets = null, string $serveur = ''): string {
-       if ($stats_objets) {
-               if ($objets = sql_allfetsel('DISTINCT objet', 'spip_referers_objets')) {
-                       $objets_par_defaut = array_values($objets);
-               }
-       }
+       if ($stats_objets) {
+               if ($tmp = sql_allfetsel('DISTINCT objet', 'spip_referers_objets')) {
+                       // extraire colonne 'objet', nettoyer et dédupliquer
+                       $liste = array_column($tmp, 'objet');
+                       $liste = array_filter(array_map('strval', $liste));
+                       $liste = array_values(array_unique($liste));
+                       $objets_par_defaut = $liste;
+               }
+       }
        if (sql_fetsel('*', 'spip_visites_articles', '', '', '', '0,1')) {
                $objets_par_defaut[] = 'article';
-               // (pas de déduplication ici)
+               $objets_par_defaut = array_values(array_unique($objets_par_defaut));
        }
@@
- elseif (is_array($objets)) {
-       // laisser tel quel
- }
+ elseif (is_array($objets)) {
+       // aplatir d’éventuels sous-tableaux
+       $flat = [];
+       foreach ($objets as $o) {
+               $flat[] = is_array($o) ? reset($o) : $o;
+       }
+       $objets = array_values(array_unique(array_filter(array_map('strval', $flat))));
+ }
@@
- foreach ($objets as $objet) {
-       $table_objet_sql = table_objet_sql($objet);
+ foreach ($objets as $objet) {
+       if (is_array($objet)) {
+               $objet = reset($objet);
+       }
+       $objet = objet_type($objet);
+       $table_objet_sql = table_objet_sql($objet);
        $id_table_objet = id_table_objet($objet);

Remarque front/squelettes (optionnel)
Dans prive/squelettes/contenu/stats_referers.html, on peut aussi normaliser côté gabarit pour éviter de passer un tableau :

#SET{objet_norm,#ENV{objet}|table_valeur{0,#ENV{objet}}}
… utiliser #GET{objet_norm} à la place de #ENV{objet} …

Mais le correctif robuste est côté PHP.

Merci.

Merci !

Je l’ai indiqué dans le ticket : Erreur table_objet_sql (#11) · Issues · spip-contrib-extensions / statistiques_objets · GitLab

ok je n’avais pas vu ce ticket

Le mer. 15 oct. 2025 à 11:00, RealET via Discuter de SPIP <noreply@discuter.spip.net> a écrit :

RealET
Octobre 15

Merci !

Je l’ai indiqué dans le ticket : Erreur table_objet_sql (#11) · Issues · spip-contrib-extensions / statistiques_objets · GitLab


Voir le sujet ou répondre à cet e-mail pour répondre.

Pour vous désabonner de ces e-mails, cliquez ici.

J’ai essayé de tester ton patch, mais il y a des parties de code que je ne trouve pas dans l’original :
// (pas de déduplication ici) par exemple

Est-ce que tu pourrais faire une PR sur : spip / stats · GitLab ?

désolé mais je n’arrive pas à me connecter sur Gitlab voici le code nettoyé :


/**
* Recherche des objets pointés par un referer
*
* @param string $referermd5
* @param array|string $objets
* @param string $serveur
* @return string
*/
function referes(string $referermd5, $objets = null, string $serveur = ''): string {
static $objets_par_defaut;

include_spip('base/objets'); // au cas où
$trouver_table = charger_fonction('trouver_table', 'base');

// si aucun type d'objet n'est donné en paramètre,
// on va chercher tous les types d'objets référencés dans les tables des referers
if (!$objets) {
if (null === $objets_par_defaut) {
$objets_par_defaut = [];
// plugin stats objets ?
$stats_objets = $trouver_table('spip_referers_objets');
if ($stats_objets) {
if ($tmp = sql_allfetsel('DISTINCT objet', 'spip_referers_objets')) {
$liste = array_column($tmp, 'objet');
$liste = array_values(array_unique(array_filter(array_map('strval', $liste))));
$objets_par_defaut = $liste;
}
}
if (sql_fetsel('*', 'spip_visites_articles', '', '', '', '0,1')) {
$objets_par_defaut[] = 'article';
$objets_par_defaut = array_values(array_unique($objets_par_defaut));
}
}
$objets = $objets_par_defaut;
}
// sinon on s'assure d'avoir un array propre
elseif (is_string($objets)) {
$objets = [$objets];
} elseif (is_array($objets)) {
$flat = [];
foreach ($objets as $o) {
$flat[] = is_array($o) ? reset($o) : $o;
}
$objets = array_values(array_unique(array_filter(array_map('strval', $flat))));
}

$retours = [];
$res_objets = [];
foreach ($objets as $objet) {
if (is_array($objet)) {
$objet = reset($objet);
}
$objet = objet_type($objet);

$table_objet_sql = table_objet_sql($objet); // spip_articles
$id_table_objet = id_table_objet($objet); // id_article
$desc = $trouver_table($table_objet_sql);
$champ_titre = $desc['titre'] ?? 'titre'; // titre, nom...

$table_referers = ($objet === 'article') ? 'spip_referers_articles' : 'spip_referers_objets';
$on = ($objet === 'article')
? 'J1.id_article = J2.id_article'
: '(J1.objet=' . sql_quote($objet) . " AND J1.id_objet = J2.$id_table_objet)";

if (
$res = sql_allfetsel(
"J2.$id_table_objet, J2.$champ_titre",
"$table_referers AS J1 LEFT JOIN $table_objet_sql AS J2 ON $on",
"(referer_md5='$referermd5' AND J1.maj>=DATE_SUB(" . sql_quote(date('Y-m-d H:i:s')) . ', INTERVAL 2 DAY))',
'',
'titre',
'',
'',
$serveur
)
) {
$res_objets[$objet] = $res;
}
}

foreach ($res_objets as $objet => $res) {
$id_table_objet = id_table_objet($objet);
foreach ($res as $k => $ligne) {
$titre = typo($ligne['titre']);
$url = generer_objet_url($ligne[$id_table_objet], $objet, '', '', true);
$retours[$k] = "<a href='$url'><i>$titre</i></a>";
}
}

if (count($retours) > 1) {
return '&rarr; ' . join(',<br />&rarr; ', $retours);
}
if (count($retours) == 1) {
return '&rarr; ' . array_shift($retours);
}

return '';
}

Le mer. 15 oct. 2025 à 11:37, RealET via Discuter de SPIP <noreply@discuter.spip.net> a écrit :

RealET
Octobre 15

J’ai essayé de tester ton patch, mais il y a des parties de code que je ne trouve pas dans l’original :
// (pas de déduplication ici) par exemple

Est-ce que tu pourrais faire une PR sur : spip / stats · GitLab ?


Voir le sujet ou répondre à cet e-mail pour répondre.

Pour vous désabonner de ces e-mails, cliquez ici.

Il faudrait que tu modifie ton message en tourant le code avec le bouton
image

Là, c’est inutilisable.

Salut,

Je l’ai fais pour lui

un modo

1 « J'aime »

Mais les Tabs ont été perdus :frowning:

ça je les vois pas, @pb38550 je te laisse modifier ou refaire un message avec les cote pour la mise en page

Est-ce que tu es bien inscrit sur la forge ? Partie « Contribuer au développement », dans cette page

Salut, je viens de valider ton compte, tu devrais pouvoir t’identifier ou lancer un rappel de mot de passe.

Gitlab ne me renvoie pas de mail pour réactiver mon compte alors j’écris ici dans la discussion. mon fichier qui fonctionne pour /inc/referers.php Il y a 3 modifs :

173: 				if ($objets = sql_allfetsel('DISTINCT objet', 'spip_referers_objets')) {
174: 					$objets_par_defaut = array_values($objets); remplacé par 173: 				// Correction PHP 8+ : sql_allfetsel() renvoie un tableau de lignes.
174: 				// On extrait proprement la colonne 'objet' pour obtenir une liste de chaînes.
175: 				if ($tmp = sql_allfetsel('DISTINCT objet', 'spip_referers_objets', '', '', '', '', '', $serveur)) {
176: 					$liste = array_column($tmp, 'objet');
177: 					$liste = array_values(array_unique(array_filter(array_map('strval', $liste))));
178: 					$objets_par_defaut = $liste; puis 183: 	// sinon on s'assure d'avoir un array
184: 	elseif (is_string($objets)) {
185: 		$objets = [$objets];
186: 	} par 191: 	// Robustesse : si on reçoit un tableau de sous-tableaux, on l'aplatit en chaînes.
192: 	elseif (is_array($objets)) {
193: 		$flat = [];
194: 		foreach ($objets as $o) {
195: 			$flat[] = is_array($o) ? reset($o) : $o;
196: 		}
197: 		$objets = array_values(array_unique(array_filter(array_map('strval', $flat))));
198: 	} et enfin 190: 	foreach ($objets as $objet) {
191: 		$table_objet_sql = table_objet_sql($objet); // spip_articles
192: 		$id_table_objet = id_table_objet($objet); // id_article
193: 		$desc = $trouver_table($table_objet_sql); par 203: 		// Sécurité PHP 8+ : $objet doit être une chaîne (pas un sous-tableau) et normalisé.
204: 		if (is_array($objet)) {
205: 			$objet = reset($objet);
206: 		}
207: 		$objet = objet_type((string)$objet);
208: 		$table_objet_sql = table_objet_sql($objet); // spip_articles Ce qui correspond à :  Extraction correcte de la liste d'objets puis si c'est un sous tableau qui arrive au lieu d'une string, l'aplatir, puis sécuriser que c'est bien une chaine à la fin.

Tu as 3 comptes :

  • 1 @proton.me du 18 janvier 2025,
  • 1 @pm.me du 28 aout 2025,
  • et 1 @gmail.com du 2 février 2024 (importé lors de la migration vers gitlab, avec lequel tu ne t’étais jamais connecté sur l’ancienne plate-forme gitea, manifestement)

avec lequel essaies-tu de te connecter ?

J’ai essayé avec chaque :slight_smile: mais la gmail m’irait très bien.

Normalement, y a un mail de reset de mot de passe qui devrait arriver :wink:

1 « J'aime »

merci :slight_smile:

et du coup, tu utilises le @pm.me finalement ?

Concernant le bug en lui meme, j’ai fait une MR reprennant le de Patrick mais en plus simple

Il n’y a pas besoin de travailler autant sur le tableau, vu qu’on recoit des choses deja propres de mysql. Quant à la correction de ce qui est recu comme paramètre, à mon avis cela n’a pas lieu d’être dans la fonction appelée : c’est à la fonction appelante d’être propre.

1 « J'aime »