Valider a7f7a786 rédigé par Glop's avatar Glop Validation de cerdic
Parcourir les fichiers

fix: Filtre `|couper`, longueur du texte de suite et détection du dernier mot

Jusqu'à présent, le filtre supposait que le texte de suite faisait
exactement 5 caractères, indépendamment de sa véritable taille.
Qui plus est, le texte de suite par défaut ` (...)` fait 6 caractères
et non pas 5 (car il faut aussi compter l'espace insécable), ce qui
fait que la longueur de la chaîne retournée était parfois incorrecte.

Ainsi, par exemple :
```
[(#VAL{Un nouveau test du filtre |couper bla bli blu}|couper{42})]
```
renvoie
* jusqu'à eebacfbe inclus :
  `Un nouveau test du filtre |couper bla (...)` (43 caractères → KO) ;
* après ce correctif :
  `Un nouveau test du filtre |couper (...)` (39 caractères → OK).

De même, en spécifiant un autre texte de coupe :
```
[(#VAL{Un nouveau test du filtre |couper bla bli blu}|couper{42, (etc.)})]
```
renvoie
* jusqu'à eebacfbe inclus :
  `Un nouveau test du filtre |couper bla (etc.)` (44 caractères → KO) ;
* après ce correctif :
  `Un nouveau test du filtre |couper (etc.)` (40 caractères → OK).

Aussi, lorsque la coupe tombait juste après un mot, l'expression
régulière employée dans le `preg_replace()` suivant considérait ce mot
comme tronqué car non suivi d'une espace. Afin de pouvoir détecter les
coupes en fin de mot, il faut en fait couper avec un caractère de plus
et modifier l'expression régulière en conséquence :
* soit ce dernier caractère est une espace, auquel cas on n'a pas coupé
  au milieu d'un mot, et il faut supprimer uniquement cette espace
  (c'est le `[\s]` dans la seconde alternative de l'expression
  régulière) ;
* soit ce dernier caractère n'est pas une espace, auquel cas on a coupé
  au milieu de (ou juste avant) un mot, et il faut supprimer ce mot
  (c'est le `[^\s]+` dans la seconde alternative de l'expression
  régulière).

Enfin, il faut aussi ajouter le modifieur `D` (`PCRE_DOLLAR_ENDONLY`)
à l'expression régulière pour qu'elle ne traite pas différemment le `\n`
ajouté comme marqueur de fin de texte :
https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php

Refs: #5422
parent c934c0d1
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+13 −7
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -419,23 +419,29 @@ function couper($texte, $taille = 50, $suite = null) {
	$texte = trim(str_replace("\n", ' ', $texte));
	$texte .= "\n";  // marquer la fin

	// couper au mot precedent (ou au début de la chaîne si c'est le premier mot)
	$long = spip_substr($texte, 0, max($taille - 4, 1));
	$u = $GLOBALS['meta']['pcre_u'];
	$court = preg_replace("/(^|[^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
	// points de suite
	if (is_null($suite)) {
		$suite = (defined('_COUPER_SUITE') ? _COUPER_SUITE : ' (...)');
	}
	$taille_suite = spip_strlen(filtrer_entites($suite));

	// couper au mot precedent (ou au début de la chaîne si c'est le premier mot)
	// on coupe avec un caractère de plus que la taille demandée afin de pouvoir
	// détecter si le dernier mot du texte coupé est complet ou non. ce caractère
	// excédentaire est ensuite supprimé par l'appel à preg_replace()
	$long = spip_substr($texte, 0, max($taille + 1 - $taille_suite, 1));
	$u = $GLOBALS['meta']['pcre_u'];
	$court = preg_replace('/(^|[^\s][\s]+)([\s]|[^\s]+)$/D' . $u, "\\1", $long);
	$points = $suite;

	// trop court ? ne pas faire de (...)
	if (spip_strlen($court) < max(0.75 * $taille, 2)) {
		$points = '';
		$long = spip_substr($texte, 0, $taille);
		$texte = preg_replace("/(^|[^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
		$long = spip_substr($texte, 0, $taille + 1);
		$texte = preg_replace('/(^|[^\s][\s]+)([\s]|[^\s]+)$/D' . $u, "\\1", $long);
		// encore trop court ? couper au caractere
		if (spip_strlen($texte) < 0.75 * $taille) {
			$texte = $long;
			$texte = spip_substr($long, 0, $taille);
		}
	} else {
		$texte = $court;