1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
| define('RESUME_TEXTE_TYPE_HTML', 1);
define('RESUME_TEXTE_TYPE_XHTML', 2);
class ResumeTexte
{
private $texte;
private $taille;
private $htmlType;
private $listeTag = array('p', 'span', 'b', 'strong', 'em', 'u', 'br', 'img', 'hr'); // Liste blanche par défaut des balises à conserver
private $simpleTag = array('img', 'br', 'hr'); // Liste des balises simples à conserver par défaut
private $deleteBloc = array('ul', 'ol', 'table', 'dl'); // Bloc html complexe à supprimer du texte
private $listeTagAttributConserve; // Liste des balises dont les attributs doivent-être conservés
private $listeTagAttributSupprime; // Liste ds balises dont les attributs doivent-être supprimés
private $attributConserve = true; // Conservation des attributs html
private $entiteTransforme = false; // Retour du texte par htmlspecialchars
/***
Constructeur
@param $texte : le texte
@param $htmlType : défini si le texte contient du html ou xhtml doit avoir une valeur d'une constante parmi:
RESUME_TEXTE_TYPE_XHTML (par défaut)
RESUME_TEXTE_TYPE_HTML
**/
function __construct($texte, $taille=200, $htmlType = RESUME_TEXTE_TYPE_XHTML)
{
$this->texte = $texte . ' ';
$this->taille = (int)$taille;
$this->htmlType = $htmlType & RESUME_TEXTE_TYPE_XHTML ? $htmlType : RESUME_TEXTE_TYPE_HTML;
}
/***
Sert à valider les caractères contenus dans les balises définies via array_filter
**/
private function valideTag($tags, $function)
{
if( array_filter($tags, 'ctype_alnum') != $tags )
{
trigger_error(__CLASS__ . '::' . $function . ' : les balises fournies doivent-être de type alphanumériques', E_USER_WARNING);
return false;
}
return true;
}
/***
Permet de charger une liste blanche de balise depuis un fichier structuré sous la forme 1 ligne = 1 balise
@param $fichier : chemin vers le fichier
**/
public function loadTag($fichier)
{
if( !is_file($fichier) )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : le fichier "' . $fichier . '" est introuvable', E_USER_WARNING);
else
{
$lignes = file($fichier);
$tags = array_filter(array_map('trim', $lignes));
if( $this->valideTag($tags, __FUNCTION__) )
$this->listeTag = $tags;
}
}
/***
Permet de charger une liste blanche de balise via un array
@param $listeTag : array des balises
**/
public function setTag($listeTag)
{
if( $this->valideTag($listeTag, __FUNCTION__) )
$this->listeTag = $listeTag;
}
/***
Permet de définir si le texte à retourner doit-être passer par htmlspecialchars
@param $bool : true ou false
**/
public function setEntite($bool)
{
$this->entiteTransform = (bool)$bool;
}
/***
Permet de définir si les attributs des balises html doivent-être conservés
@param $bool : true ou false
NOTE :
- Si valeur passée à true, $this->listeTagAttributConserve ne doit pas être défini
- Si passée à false, $this->listeTagAttributSupprime ne doit pas être défini
**/
public function setAttributConserve($bool)
{
settype($bool, 'bool');
if( $bool === true && isset($this->listeTagAttributConserve) )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : vous avez défini la conservation des attributs à "true" alors qu\'une liste de balises dont les attributs doivent-être conservés a été définie', E_USER_NOTICE);
elseif( $bool === false && isset($this->listeTagAttributSupprime) )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : vous avez défini la conservation des attributs à "false" alors qu\'une liste de balises dont les attributs doivent-être supprimés a été définie', E_USER_NOTICE);
$this->attributConserve = $bool;
}
/***
Permet de définir quelles balises DOIVENT conserver leurs attributs
@param $listeTag : liste des balises dont les attributs doivent-être conservés
NOTE :
- les balises doivent-être contenues IMPÉRATIVEMENT dans $this->listeTag
- $this->attributConserve ne doit pas être à true
- $this->listeTagAttributSuprime ne doit pas être défini
**/
public function setListeTagAttributConserve($listeTag)
{
settype($listeTag, 'array');
if( $this->valideTag($listeTag, __FUNCTION__) )
{
if( array_diff($listeTag, $this->listeTag) )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : la liste des balises fournies ne concorde pas avec la liste des balises à conserver', E_USER_WARNING);
else
{
if( $this->attributConserve === true )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : vous avez défini la conservation des attributs à "true"', E_USER_NOTICE);
$this->listeTagAttributConserve = $listeTag;
}
}
}
/***
Permet de définir quelles balises NE DOIVENT PAS conserver leurs attributs
@param $listeTag : liste des balises dont les attributs ne doivent pas être conservés
NOTE :
- les balises doivent-être contenues IMPÉRATIVEMENT dans $this->listeTag
- $this->attributConserve ne doit pas être à false
- listeTagAttributConserve ne doit pas être défini
**/
public function setListeTagAttributSupprime($listeTag)
{
settype($listeTag, 'array');
if( $this->valideTag($listeTag, __FUNCTION__) )
{
if( array_diff($listeTag, $this->listeTag) )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : la liste des balises fournies ne concorde pas avec la liste des balises à conserver', E_USER_WARNING);
else
{
if( $this->attributConserve === false )
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' : vous avez défini la conservation des attributs à "false"', E_USER_NOTICE);
$this->listeTagAttributSupprime = $listeTag;
}
}
}
/***
Permet d'ajouter les blocs html à supprimer ou de redéfinir la liste
@param $bloc : une balise ou un array de balises html
@param $initialise : true ou false (si true la liste existante est vidée)
**/
public function setDeleteBloc($bloc, $initialise = false)
{
if( !is_array($bloc) )
$bloc = array($bloc);
$initialise === false ? $this->deleteBloc = array_merge($this->deleteBloc, $bloc) : $this->deleteBloc = $bloc;
}
/***
Fonction de rappel nécessaire à la suppression des balises non-autorisées
@param $array : array de capture de la chaine par preg_replace_callback
**/
private function callbackSupprimeTag($array)
{
return (in_array($array[1], $this->listeTag) ? $array[0] : '');
}
/***
Sert à préparer le texte en fonction des éléments fournis
**/
private function textePrepare()
{
// Supression des blocs de balises complexe du texte (balises et tout leur contenu)
$this->texte = preg_replace('#<(' . implode('|',$this->deleteBloc) . ')( +[a-zA-Z]+="[^"]*")*>.+</\1>#Us', '', $this->texte);
// Suppression des balises de début et fin autres que celles autorisées
$this->texte = preg_replace_callback('#</?([a-zA-Z1-6]+)( +[a-zA-Z]+="[^"]*")*( ?/)?>#', array($this, 'callbackSupprimeTag'), $this->texte);
// Ajout d'un / de fin sur les balises simples si texte en html (pour facilité de travail par la suite)
if( $this->htmlType & RESUME_TEXTE_TYPE_HTML )
$this->texte = preg_replace('#(<(?:' . implode('|', $this->simpleTag) . ')(?: +[a-zA-Z]+="[^"]*")*)>#', '$1 />', $this->texte);
// Option de supprimer/conserver tous/en partie les attributs des balises
if( $this->attributConserve == false )
{
// Si une liste de balises dont les attributs sont à conserver existe
if( isset($this->listeTagAttributConserve) )
{
$listeDiff = array_diff($this->listeTag, $this->listeTagAttributConserve);
$this->texte = preg_replace('#(<(' . implode('|', $listeDiff) . ')(?: +[a-zA-Z]+="[^"]*")+)( ?/)?>#', '<$1$2>', $this->texte);
}
else
$this->texte = preg_replace('#(<(' . implode('|', $this->listeTag) . ')(?: +[a-zA-Z]+="[^"]*")+)( ?/)?>#', '<$1$2>', $this->texte);
}
// Conservation des attributs
else
{
// Si une liste de balises dont les attributs sont à supprimer existe
if( isset($this->listeTagAttributSupprime) )
$this->texte = preg_replace('#<(' . implode('|', $this->listeTagAttributSupprime) . ')(?: +[a-zA-Z]+="[^"]*")+( ?/)?>#', '<$1$2>', $this->texte);
}
}
/***
Function servant à césurer le texte du nombre de caractères souhaités.
Le principe est de :
- Récupérer d'un côté tous les morceaux de texte et leur offset, et de l'autre toutes les balises html et leur offset
- De calculer la longueur des morceaux mit bout à bout, et de trouver la position de césure en fonction de leurs longueurs
- De couper le texte sur un espace suivant la position de césure trouvée
- Et enfin de compléter le texte par les balises html de fin manquantes
**/
private function generation()
{
$masqueExpressionSplit = '#</?([a-zA-Z1-6]+)(?: +[a-zA-Z]+="[^"]*")*( ?/)?>#';
$masqueExpressionMatch = '#<(?:/([a-zA-Z1-6]+)|([a-zA-Z1-6]+)(?: +[a-zA-Z]+="[^"]*")*( ?/)?)>#';
$morceauTexte = preg_split($masqueExpressionSplit, $this->texte, -1, PREG_SPLIT_OFFSET_CAPTURE | PREG_SPLIT_NO_EMPTY);
$nombreMorceau = count($morceauTexte);
if( $nombreMorceau == 1 )
{
$longueur = mb_strlen($this->texte);
// pour ne pas couper un mot, on va a l espace suivant
$this->texte = substr($texte, 0, strpos($this->texte, ' ', $longueur > $nombreMorceau ? $nombreMorceau : $longueur));
}
else
{
// Variable contenant la longueur des bouts de texte
$longueur = 0;
// (position du dernier élément du tableau $chaines)
$indexDernierElementMorceau = $nombreMorceau - 1;
// Position par défaut de la césure au cas ou la longueur du texte serait inférieure au nombre de caratères à sélectionner
// La position de la césure est égal à sa position [1] + la longueur du bout de texte [0] - 1 (dernier caractère)
$position = $morceauTexte[$indexDernierElementMorceau][1] + mb_strlen($morceauTexte[$indexDernierElementMorceau][0]) - 1;
// Boucle parcourant l'array et ayant pour fonction d'incrémenter au fur et à mesure la longueur des morceaux de texte,
// et de calculer la position de césure de l'extrait dans le texte
$indexMorceau = $indexDernierElementMorceau;
$rechercheEspace = true;
foreach( $morceauTexte as $index => $morceau )
{
$longueur += mb_strlen($morceau[0]);
// Si la longueur désirée de l'extrait à obtenir est atteinte
if( $longueur >= $this->taille )
{
// On calcule la position de césure du texte (position de chaîne + sa longueur -1 )
$positionFinMorceau = $morceau[1] + mb_strlen($morceau[0]) - 1;
// calcul de la position de césure
$position = $positionFinMorceau - ($longueur - $this->taille);
// On regarde si un espace est présent après la position dans le bout de texte
if( ($positionEspace = strpos($morceau[0], ' ', $position - $morceau[1])) !== false )
{
// Un espace est détecte dans le bout de texte APRÈS la position
$position = $morceau[1] + $positionEspace;
$rechercheEspace = false;
}
// Si on ne se trouve pas sur le dernier élément
if( $index != $indexDernierElementMorceau )
$indexMorceau = $index + 1;
break;
}
}
// Donc il n'y avait pas d'espace dans le bout de texte où la position de césure sert de référence
if( $rechercheEspace === true )
{
// Recherche d'un espace dans les bouts de texte suivants
for( $i=$indexMorceau; $i<=$indexDernierElementMorceau; $i++ )
{
$position = $morceauTexte[$i][1];
if( ($positionEspace = strpos($morceauTexte[$i][0], ' ')) !== false )
{
$position += $positionEspace;
break;
}
}
}
// On effectue la césure sur le texte suivant la position calculée
$this->texte = substr($this->texte, 0, $position);
// Récupération de toutes les balises du texte et de leur position (PREG_OFFSET_CAPTURE)
preg_match_all($masqueExpressionMatch, $this->texte, $retour, PREG_OFFSET_CAPTURE);
// Array destiné à enregistrer les noms de balises ouvrantes
$tags = array();
foreach( $retour[0] as $index => $tag )
{
// Si on se trouve sur une balise unique, on passe au tour suivant
if( isset($retour[3][$index][0]) )
continue;
// Si le caractère slash n'est pas détecté en seconde position dans la balise entière, on est sur une balise ouvrante
if( $retour[0][$index][0][1] != '/' )
// On empile l'élément en début de l'array
array_unshift($tags, $retour[2][$index][0]);
// Donc balise fermante
else
// suppression du premier élément de l'array
array_shift($tags);
}
// réparation du code si necessaire (balises ouvertes, mais non-fermées)
if( !empty($tags) ) {
foreach( $tags as $tag ) {
$this->texte .= '</' . $tag . '>';
}
}
}
}
/***
Retournera le texte césuré
**/
public function resume()
{
$this->textePrepare();
$this->generation();
// Suppression du " /" de fin sur les balises uniques si HTML
if( $this->htmlType & RESUME_TEXTE_TYPE_HTML )
$this->texte = preg_replace('#<(' . implode('|', $this->listeTag) . ')( +[a-zA-Z]+="[^"]*")*(:? ?/)?>#', '<$1$2>', $this->texte);
return $this->texte;
}
}
header('Content-type: text/html');
$texte = <<<TeXt
<div id="a">
<p class="class1">Loin, très loin, <b>au delà des monts Mots</b>, à mille lieues des pays
<a href="http://vasvoirlabas.com">Voyellie et Consonnia</a>, demeurent les <span style="color: blue">Bolos Bolos</span>. Ils vivent en retrait, à <a href="http://petaouchnock.org">Bourg-en-Lettres</a>, sur les côtes de la Sémantique, <img src="un vaste océan de langues" />.</p>
<ul>
<li>one</li>
<li>dos</li>
</ul>
<p class="class1">Un petit ruisseau, du nom de <a href="http://zoralarousse.net">Larousse</a>, coule en leur lieu <span style="font-style: oblique">et les approvisionne en <strong>règlalades</strong> nécessaires en tout genre</span>; un pays paradisiagmatique, dans lequel des pans entiers de phrases prémâchées vous volent litéralement tout cuit dans la bouche.
<hr class="oops" />Pas même la toute puissante Ponctuation ne régit les Bolos Bolos - une vie on ne peut moins orthodoxographique. <a href="http://unjourmonprince.net">Un jour pourtant</a>, une petite ligne de Bolo Bolo du nom de Lorem Ipsum décida de s'aventurer dans la vaste Grammaire.
</p>
</div>
TeXt;
$Resume = new ResumeTexte($texte, 250);
$Resume->setTag(array('a', 'span', 'p', 'em', 'u', 'b', 'strong', 'img'));
$Resume->setListeTagAttributSupprime('p');
echo $Resume->resume(); |
Partager