Du bist hier: StartPHPFunktionen › HTML-Code kürzenDieses Snippet kommentieren

HTML-Code kürzen

Diese Funktion schneidet HTML-Code nach n Zeichen ab. Dabei werden Entities als jeweils 1 Zeichen berechnet und nicht zerschnitten, ebenso zählen HTML-Tags nicht zur Länge und werden ebenfalls nicht zerstört. Auf Wunsch kann die Klasse nach dem Abschneiden die nun offenen Tags auch wieder schließen.

<?php

function shortenText($text, $length, $closeTags = false) {
    $text = trim($text);
    
    if ( strlen($text) <= $length ) return $text;
    
    // HTML-Tags extrahieren, speichern und im Text
    // durch Extra-Markierungen ersetzen
    
    $htmlTagMap = array();
    $counter    = 0;
    $shortened  = 0; // Die Länge, die schon rausgestrichen wurde, wird in jedem Durchlauf größer
    
    preg_match_all("#<((/?[a-z]+)[^>]*)>#s",$text,$matches, PREG_OFFSET_CAPTURE);
    
    if ( $matches ) {
        foreach ( $matches[1] as $matchNo => $match ) {
            $fullTag  = $match[0];
            $tagBegin = $matches[2][$matchNo][0];
            $offset   = $match[1];
            /*
            echo "Full: $fullTag\n";
            echo "TagBegin: $tagBegin\n";
            echo "offset: $offset\n";
            echo "length: ".strlen($fullTag)."\n";
            */
            $key = $counter; //$tagBegin.'_'.$counter;
            
            $htmlTagMap[$key] = array(
                'full'   => $fullTag,
                'offset' => $offset
            );
            
            $text       = substr_replace($text,"<$key>",$offset-$shortened-1,strlen($fullTag)+2);
            $shortened += strlen($fullTag) - 1;
            $counter++;
        }
    }
    
    // Jetzt suchen wir von der gewünschten Stelle
    // an die nächstmögliche Trennstelle nach links,
    // damit wir die Maximalgrenze nicht überschreiten.
    // Die Suche ist iterativ, wobei bei jedem
    // gefundenem Entity die Maximallänge um die
    // Länge des Entities erhöht wird.
    
    $reachedLength         = 0;
    $rightText             = $text;
    $summedLengthOfGarbage = 0;
    $left                  = "";
    
    do {
        preg_match('/^.*?(&[a-z]+;)/si', $rightText, $matches, PREG_OFFSET_CAPTURE);
        
        if ( $matches ) {
            $newText      = $matches[0][0];
            $tagLength    = 0;
            $entityLength = strlen($matches[1][0]);
            
            // Wie viele HTML-Tags sind enthalten und
            // wie lang sind sie?
            
            preg_match_all("/<\d+>/i", $newText, $tagMatch);
            
            if ( $tagMatch ) {
                foreach ( $tagMatch[0] as $tag ) $tagLength += strlen($tag);
            }
            
            $newLength = $reachedLength + strlen($matches[0][0]) - $entityLength + 1 - $tagLength;
            $left .= $matches[0][0];
            $reachedLength +=
                strlen($matches[0][0]) /* Eigentliche Länge des neuen Textes */
                - $entityLength /* minus der Länge des Entitys am Ende */
                + 1 /* + 1, weil das Entity ja für ein Zeichen steht */
                - $tagLength /* minus der Länge aller HTML-Tags */;
            
            $summedLengthOfGarbage += $entityLength - 1 + $tagLength;
            
            $rightText = substr($rightText,strlen($matches[0][0]));
            
            // Falls wir nun weit genug sind,
            // schneiden wir das Entity noch
            // ab und beenden die Schleife.
            if ( $newLength > $length ) {
                $summedLengthOfGarbage -= $entityLength - 1; // <- das schneiden wir ja gerade wieder ab
                $left = substr($left,0,-$entityLength);
                break;
            }
        } else {
            break;
        }
    } while ( $reachedLength < $length );
    
    // Falls kein Entity vorhanden ist, schneiden wir direkt vom
    // Gesamttext die Wörter ab.
    
    if ( $reachedLength == 0 ) {
        $left = $text;
    }
    
    if ( strlen($left) > $length ) {
        // Falls das Wort nun immer noch zu groß
        // ist, können wir problemlos vom Ende
        // ganze Wörter abschneiden, da es sich
        // nicht um Entities handeln kann und
        // auch HTML-Tags nun "Wörter" sind.
        
        while ( strlen($left) - $summedLengthOfGarbage > $length ) {
            $left = preg_replace('/(.*)[\b\s].+?$/si', "\\1", $left);
        }
    }
    
    // Nun können wir die HTML-Tags wiederherstellen
    
    preg_match_all("/<(\d+)>/s", $left, $matches);
    
    foreach ( $matches[1] as $match ) {
        $left = str_replace("<$match>",'<'.$htmlTagMap[$match]['full'].'>',$left);
    }
    
    // Durch das Abschneiden des Textes können nun
    // einige Tags nicht mehr geschlossen sein.
    // Diese müssen wir, wenn gewünscht, finden
    // und manuell schließen.
    
    if ( $closeTags ) {
        preg_match_all("#<(/?[a-z]+)[^>]*>#i",$left,$matches);
        
        if ( $matches ) {
            $open = array();
            
            foreach ( $matches[1] as $match ) {
                if ( $match[0] == "/" ) {
                    $index = getLastIndex($open,substr($match,1));
                    
                    if ( $index != -1 ) {
                        unset($open[$index]);
                    }
                } else {
                    $open[] = $match;
                }
            }
            
            $open = array_reverse($open);
            
            foreach ( $open as $tag ) {
                $left .= "</$tag>";
            }
        }
    }
    
    return $left;
}

// Hilfsfunktion für shortenText()
function getLastIndex($array,$needle) {
    $index = -1;
    
    foreach ( $array as $idx => $element ) {
        if ( $element == $needle ) $index = $idx;
    }
    
    return $index;
}

?>

Kommentar verfassen

Fehler gefunden? Doofer Code? Ein kleines "Danke!"? Hinterlasse einfach einen Kommentar.

(muss sein)
(muss nicht sein, wird nicht angezeigt)

Dein Kommentar wird erst nach einer manuellen Prüfung angezeigt.