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_string, 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.