Hlavní navigace

PHP okénko: Zkrácení textu s XHTML značkami

Jakub Vrána

Dnešní PHP okénko ukazuje, jak se dá v PHP zkrátit řetězec obsahující XHTML značky s využitím rozšíření Tidy i bez něj, a naznačuje, jak by se kód musel upravit pro zpracování tolerantnějšího HTML kódu.

Pokud chceme vypsat pouze začátek nějakého textu, není v PHP nic jednoduššího:

<?php
echo substr($text, 0, $limit);
?> 

Pokud text nechceme ukončovat uprostřed slova, také to není dvakrát velká věda:

<?php
if (strlen($text) <= $limit) {
    echo $text;
} else {
    $text = substr($text, 0, $limit+1);
    $pos = strrpos($text, " "); // v PHP 5 by se dal použít parametr offset
    echo substr($text, 0, ($pos ? $pos : -1)) . "...";
}
?> 

Funkce strrpos vrátí v případě nenalezení podřetězce false a v případě nalezení na samém začátku řetězce 0, mezi čímž je obvykle potřeba pomocí operátoru === rozlišovat, my ale oba případy ošetříme stejně – vypsáním řetězce zkráceného přesně na požadovanou délku.

Co ale dělat v případě, kdy $text obsahuje HTML značky? Pokud nám na nich nezáleží, dá se jednoduše použít funkce strip_tags. Pokud je ale v textu chceme zachovat, musíme si napsat funkci vlastní. Mohla by být založena na regulárních výrazech, nejjednodušší ale bude použít staré dobré procházení po znacích:

<?php
/** Zkrácení textu s XHTML značkami
@param string $s zkracovaný řetězec bez komentářů a bloků skriptu
@param int $limit požadovaný počet vrácených znaků
@return string zkrácený řetězec se správně uzavřenými značkami
*/
function xhtml_cut_tidy($s, $limit)
{
    $length = 0;
    for ($i=0; $i < strlen($s) && $length < $limit; $i++) {
        switch ($s{$i}) {
        case '<':
            while ($i < strlen($s) && $s{$i} != '>') {
                $i++;
            }
            break;
        case '&':
            $length++;
            while ($i < strlen($s) && $s{$i} != ';') {
                $i++;
            }
            break;
        default:
            $length++;
        }
    }
    $config = array('output-xhtml' => true, 'show-body-only' => true);
    return tidy_repair_string(substr($s, 0, $i), $config, 'raw');
}
?> 

Funkce prochází řetězec, značky a entity přeskakuje a započítává pouze zobrazované znaky. Zkrácený řetězec ošetří funkcí tidy_repair_strin­g, která doplní chybějící uzavírací značky. Pokud bychom ošetřovali HTML řetězec, museli bychom kromě nepředání parametru output-xhtml funkci tidy_repair_string upravit také zpracování entit, které v HTML není tak přísné.

Funkce tidy_repair_string nám ušetřila spoustu práce, jak ale postupovat v případě, že není k dispozici? Bude potřeba ukládat otevírané značky do zásobníku a na závěr je uzavřít:

<?php
/** Zkrácení textu s XHTML značkami
@param string $s zkracovaný řetězec bez komentářů a bloků skriptu
@param int $limit požadovaný počet vrácených znaků
@return string zkrácený řetězec se správně uzavřenými značkami
*/
function xhtml_cut($s, $limit)
{
    $length = 0;
    $tags = array(); // dosud neuzavřené značky
    for ($i=0; $i < strlen($s) && $length < $limit; $i++) {
        switch ($s{$i}) {

        case '<':
            // načtení značky
            $start = $i+1;
            while ($i < strlen($s) && $s{$i} != '>' && !ctype_space($s{$i})) {
                $i++;
            }
            $tag = substr($s, $start, $i - $start);
            // přeskočení případných atributů
            while ($i < strlen($s) && $s{$i} != '>') {
                $i++;
            }
            if ($s{$start} == '/') { // uzavírací značka
                array_shift($tags); // v XHTML dokumentu musí být vždy uzavřena poslední neuzavřená značka
            } elseif ($s{$i-1} != '/') { // otevírací značka
                array_unshift($tags, $tag);
            }
            break;

        case '&':
            $length++;
            while ($i < strlen($s) && $s{$i} != ';') {
                $i++;
            }
            break;

        default:
            $length++;

        }
    }
    $s = substr($s, 0, $i);
    if ($tags) {
        $s .= "</" . implode("></", $tags) . ">";
    }
    return $s;
}
?> 

Úprava na tolerantnější HTML verzi by v tomto případě byla o něco složitější, protože v HTML nemusí být nepárové značky označeny a navíc některé značky uzavřeny být mohou, ale nemusí.

Pokud si zadavatel usmyslí, že chce za všech okolností zobrazovat např. čtyři řádky, dá se postupovat tak, že se text zkrátí s dostatečnou rezervou a na čtyři řádky se seřízne u klienta pomocí stylu height: 5em; overflow: hidden; line-height: 1.25;, kde hodnota u height je požadovaný počet řádek vynásobený hodnotou line-height v jednotkách em.


Podobně laděné texty můžete najít i na autorově weblogu PHP triky.

Našli jste v článku chybu?

20. 6. 2005 9:42

Dobrá připomínka. Další možností je počkat na další velkou verzi PHP, která bude podporu Unicode obsahovat, takže $s{$i} bude fungovat i s UTF-8. Na tu si ale ještě nějaký ten měsíc nebo rok počkáme...
Vitalia.cz: Vychytané vály a válečky na vánoční cukroví

Vychytané vály a válečky na vánoční cukroví

Měšec.cz: Komu musí od ledna zvýšit mzdu?

Komu musí od ledna zvýšit mzdu?

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

Vitalia.cz: Potvrzeno: Pobyt v lese je skvělý na imunitu

Potvrzeno: Pobyt v lese je skvělý na imunitu

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

DigiZone.cz: Flix TV: dva set-top boxy za korunu

Flix TV: dva set-top boxy za korunu

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí