Функция cot_string_truncate в Cotonti
Функция cot_string_truncate выполняет обрезку текста, чтобы он не превышал заданную длину (включая многоточие или дополнительный текст). Она также корректно работает с HTML-кодом
Описание функции
Функция cot_string_truncate выполняет обрезку текста, чтобы он не превышал заданную длину (включая многоточие или дополнительный текст). Она также корректно работает с HTML-кодом, учитывая теги, и может обрезать текст, не разрывая слова.
Функция позволяет гибко обрезать текст с учетом HTML-тегов, длины и точности, что полезно для вывода ограниченного по длине контента на веб-страницах.
Давайте разберем функцию cot_string_truncate в PHP-фреймворке Cotonti и подробно проанализируем каждое условие и действие внутри функции.
/**
* Truncates text.
*
* Cuts a string to the length of $length
*
* @param string $text String to truncate.
* @param int $length Length of returned string, including ellipsis.
* @param bool $considerhtml If true, HTML tags would be handled correctly *
* @param bool $exact If false, $text will not be cut mid-word
* @param string $cuttext Adds text if truncated
* @return string trimmed string.
*/
function cot_string_truncate($text, $length = 100, $considerhtml = true, $exact = false, $cuttext = ''): string
{
if (empty($text)) {
return $text;
}
$length = (int) $length;
$truncated_by_space = false;
$plain_mode = false;
$truncate = '';
$open_tags = [];
$total_length = 0;
$plain_tag = false;
if ($considerhtml) {
// if the plain text is shorter than the maximum length, return the whole text
if (
!preg_match('/<\s*(pre|plaintext)/', $text)
&& mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length
) {
return $text;
}
// splits all html-tags to scanable lines
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
foreach ($lines as $line_matchings) {
// if there is any html-tag in this line, handle it and add it (uncounted) to the output
if (!empty($line_matchings[1])) {
// if it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>)
if (
preg_match(
'/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is',
$line_matchings[1]
)
) {
// do nothing
} elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
// if tag is a closing tag (f.e. </b>)
$tag = false;
if (strtolower($tag_matchings[1]) == $plain_mode) {
$plain_mode = false;
} else {
// delete tag from $open_tags list
$pos = array_search($tag_matchings[1], $open_tags);
if ($pos !== false) {
unset($open_tags[$pos]);
}
}
} elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
// if tag is an opening tag (f.e. <b>)
$tag = strtolower($tag_matchings[1]);
$plain_tag = in_array($tag, array('pre','plaintext')) ? $tag : false;
// add tag to the beginning of $open_tags list
if (!$plain_mode && !$plain_tag) {
array_unshift($open_tags, mb_strtolower($tag));
}
}
// add html-tag to $truncate'd text
if (!$plain_mode) {
$truncate .= $line_matchings[1];
}
}
// the number of characters which are left
$left = $length - $total_length;
if ($plain_mode || ($plain_tag && $tag)) {
// treats text as plain in <pre>, <plaintext> tags
$content = $plain_mode ? $line_matchings[0] : $line_matchings[2];
if (mb_strlen($content) <= $left) {
$truncate .= $content;
$total_length += mb_strlen($content);
} else {
$truncate .= mb_substr($content, 0, $left);
$total_length += $left;
}
if ($plain_tag && !$plain_mode) {
$plain_mode = $plain_tag;
}
} else {
// calculate the length of the plain text part of the line; handle entities as one character
$content_length = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};|[\r\n\s]{2,}/i', ' ', $line_matchings[2]));
if ($total_length+$content_length> $length) {
$entities_length = 0;
// search for html entities and spaces
if (
preg_match_all(
'/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};|[\r\n\s]{2,}/i',
$line_matchings[2],
$entities,
PREG_OFFSET_CAPTURE
)
) {
// calculate the real length of all entities in the legal range
foreach ($entities[0] as $entity) {
if ($entity[1]+1-$entities_length <= $left) {
$left--;
$entities_length += mb_strlen($entity[0]);
} else {
// no more characters left
break;
}
}
}
$truncate .= mb_substr($line_matchings[2], 0, $left+$entities_length);
// maximum lenght is reached, so get off the loop
$truncated_by_space = preg_match('/[\r\n\s]/', mb_substr($line_matchings[2], $left+$entities_length, 1));
break;
} else {
$truncate .= $line_matchings[2];
$total_length += $content_length;
}
}
// if the maximum length is reached, get off the loop
if ($total_length >= $length) {
break;
}
}
} else {
if (mb_strlen($text) <= $length) {
return $text;
} else {
$truncate = mb_substr($text, 0, $length);
}
}
if (!$exact && !$truncated_by_space && !$plain_mode) {
// ...search the last occurence of a space...
if (mb_strrpos($truncate, ' ') > 0) {
$pos1 = mb_strrpos($truncate, ' ');
$pos2 = mb_strrpos($truncate, '>');
$spos = ($pos2 < $pos1) ? $pos1 : ($pos2+1);
if (isset($spos)) {
// ...and cut the text in this position
$truncate = mb_substr($truncate, 0, $spos);
}
}
}
$truncate .= $cuttext;
if ($considerhtml) {
// close all unclosed html-tags
if ($plain_mode) {
$truncate .= '</' . $plain_mode . '>';
}
foreach ($open_tags as $tag) {
$truncate .= '</' . $tag . '>';
}
}
return $truncate;
}
Параметры функции
$text — строка, которую необходимо обрезать.
$length — максимальная длина возвращаемой строки, включая многоточие.
$considerhtml — если установлено в true, то HTML-теги будут учтены при обрезке.
$exact — если установлено в false, то текст не будет обрезаться на середине слова.
$cuttext — текст, который будет добавлен в конце обрезанного текста (например, многоточие).
Алгоритм работы функции
1. Проверка на пустой текст
if (empty($text)) {
return $text;
}
Если текст пустой, функция сразу возвращает его без изменений.
2. Приведение длины в целое число
$length = (int) $length;
Приводит значение параметра $length к целочисленному типу для безопасности и корректности работы.
3. Инициализация переменных
$truncated_by_space = false;
$plain_mode = false;
$truncate = '';
$open_tags = [];
$total_length = 0;
$plain_tag = false;
$truncated_by_space — флаг, указывающий, что текст был обрезан в середине пробела.
$plain_mode — флаг, показывающий, что текст находится в простом (неформатированном) режиме (например, в тегах <pre> или <plaintext>).
$truncate — строка, в которую будет накапливаться обрезанный текст.
$open_tags — массив, в который будут добавляться открытые HTML-теги.
$total_length — общий длина обрезанного текста.
$plain_tag — флаг для определения, является ли текущий тег "простым" (например, <pre>).
4. Обработка HTML
Если параметр $considerhtml равен true, то функция будет учитывать HTML-теги при обрезке текста.
Проверка на длину без учета HTML
if (!preg_match('/<\s*(pre|plaintext)/', $text) && mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
return $text;
}
Эта проверка необходима для того, чтобы убедиться, что текст без HTML-тегов не превышает максимальную длину. Если текст без тегов укладывается в заданную длину, то он сразу возвращается без изменений.
Также проверяется, не является ли текст в тегах <pre> или <plaintext>, так как эти теги предполагают отображение текста в исходном виде.
Разделение текста на теги и содержимое
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
Этот паттерн используется для разделения текста на HTML-теги и содержимое.
(<.+?>)? — захватывает HTML-теги.
([^<>]*) — захватывает текст между тегами.
Затем происходит обработка каждого элемента массива $lines:
foreach ($lines as $line_matchings) {
// обработка каждого тега и содержимого...
}
5. Обработка HTML-тегов
Для каждого тега и его содержимого выполняется соответствующая логика:
Если встречается открывающий тег, то он добавляется в список открытых тегов $open_tags.
Если встречается закрывающий тег, то он удаляется из списка открытых тегов.
Если тег является пустым элементом (например, <br/>), то он игнорируется.
Пример:
if (!empty($line_matchings[1])) {
if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
// Закрывающий тег
} elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
// Открывающий тег
}
}
6. Обработка содержимого
Затем происходит обработка текста (содержимого между тегами):
Если текущий тег является простым (например, текст внутри <pre>), то содержимое обрабатывается как обычный текст.
Для каждого фрагмента текста вычисляется длина, и если она превышает оставшееся количество символов, текст обрезается.
Все текстовые сущности (например, &, <) также учитываются как один символ, чтобы избежать ошибок при подсчете длины.
7. Обрезка по словам (если параметр $exact равен false)
if (!$exact && !$truncated_by_space && !$plain_mode) {
if (mb_strrpos($truncate, ' ') > 0) {
// обрезка по последнему пробелу
}
}
Если текст не должен обрезаться точно по длине, то производится обрезка по последнему пробелу, чтобы не разорвать слово. Это сделано для того, чтобы текст был более читаемым.
8. Добавление текста, если обрезано
$truncate .= $cuttext;
После обрезки текста добавляется дополнительный текст, например, многоточие (...) или другой текст, указанный в параметре $cuttext.
9. Закрытие незакрытых HTML-тегов
if ($considerhtml) {
// закрытие всех открытых тегов
}
Если $considerhtml равно true, то все открытые HTML-теги закрываются, чтобы текст не оставался с незакрытыми тегами.
10. Возврат результата
return $truncate;
Возвращается обрезанный текст.
Итоги
Обработка HTML-тегов: Если $considerhtml равно true, то текст делится на теги и текст, с учетом длины каждого фрагмента, чтобы не нарушить структуру HTML.
Обрезка по словам:
Если $exact равно false, то обрезка текста происходит по последнему пробелу, чтобы не разорвать слово.
Закрытие тегов:
Если были открыты теги, они будут корректно закрыты.
Многоточие:
При обрезке текста добавляется дополнительный текст (например, многоточие) в конце.
Пример использования функции cot_string_truncate в Cotonti
Давайте разберем код, который реально используется движком и проанализируем его шаг за шагом, в контексте PHP-фреймворка Cotonti.
Важно отметить, что здесь используется функция cot_string_truncate, о которой мы говорили ранее.
$rtopic['ft_preview'] = !empty($rmsg['fp_text']) ? cot_string_truncate($rmsg['fp_text'], 120) : '';
// If preview string is still too long, let's strip tags and try again
// Если строка предварительного просмотра все еще слишком длинная, давайте удалим теги и повторим попытку.
if (mb_strlen($rtopic['ft_preview']) > 128) {
$rtopic['ft_preview'] = cot_string_truncate(strip_tags($rmsg['fp_text']), 120, false);
}
Строки кода
$rtopic['ft_preview'] = !empty($rmsg['fp_text']) ? cot_string_truncate($rmsg['fp_text'], 120) : '';
Проверка на наличие текста:
!empty($rmsg['fp_text'])
Эта часть проверяет, не является ли переменная $rmsg['fp_text'] пустой. Если строка не пуста, будет выполнена дальнейшая обработка текста. Если же строка пуста (например, нет сообщения или текст не задан), то результатом будет пустая строка ''. Это условие защищает от обработки пустых данных.
Функция обрезки текста:
cot_string_truncate($rmsg['fp_text'], 120)
Если строка $rmsg['fp_text'] не пуста, то вызывается функция cot_string_truncate, которая обрезает текст до 120 символов, сохраняя его читаемость и корректно обрабатывая HTML-теги, если они присутствуют.
Если $rmsg['fp_text'] является текстом с HTML-разметкой, то функция cot_string_truncate обрежет текст так, чтобы не нарушалась структура HTML.
Если $rmsg['fp_text'] не содержит HTML, то просто обрезается строка до 120 символов.
Запись в массив: Результат выполнения функции cot_string_truncate записывается в элемент массива $rtopic['ft_preview'].
Если строка слишком длинная, убираем HTML-теги: После того как текст был обрезан, добавляется дополнительная проверка на длину:
if (mb_strlen($rtopic['ft_preview']) > 128) {
$rtopic['ft_preview'] = cot_string_truncate(strip_tags($rmsg['fp_text']), 120, false);
}
Проверка длины:
if (mb_strlen($rtopic['ft_preview']) > 128)
Здесь используется функция mb_strlen (многоязычная версия strlen), чтобы измерить длину строки $rtopic['ft_preview']. Если длина строки больше 128 символов, выполняется дальнейшая обработка текста.
Удаление HTML-тегов:
strip_tags($rmsg['fp_text'])
Функция strip_tags удаляет все HTML-теги из текста $rmsg['fp_text']. Это делается для того, чтобы в случае, если текст все еще слишком длинный после обработки, HTML-теги не мешали правильно обрезать строку.
Обрезка текста без HTML:
cot_string_truncate(strip_tags($rmsg['fp_text']), 120, false)
После удаления HTML-тегов текст снова передается в функцию cot_string_truncate, но с параметром false для $considerhtml. Это означает, что теперь текст обрабатывается как обычный, без учета HTML-тегов, и обрезается до 120 символов.
Запись результата в массив:
Обработанный и очищенный от тегов текст снова записывается в $rtopic['ft_preview'].
Общее описание логики
В первую очередь код проверяет, есть ли текст в переменной $rmsg['fp_text'].
Если текст есть, то он обрезается до 120 символов с помощью функции cot_string_truncate. При этом учитываются HTML-теги (если они присутствуют).
Затем выполняется дополнительная проверка, если строка все еще слишком длинная (больше 128 символов). В этом случае из текста удаляются все HTML-теги с помощью strip_tags, и текст снова обрезается до 120 символов, но без учета HTML-тегов.
В конце обрезанный текст сохраняется в массив $rtopic['ft_preview'], который, вероятно, будет использоваться для отображения превью сообщения или темы.
Пример
Предположим, что у нас есть следующее сообщение:
$rmsg['fp_text'] = "<b>Это длинный текст с <i>HTML</i> тегами</b>, который нужно обрезать.";
После первой обработки (с учетом HTML) текст будет выглядеть так:
"Это длинный текст с <i>HTML</i> тегами"
Он будет обрезан до 120 символов, сохраняя теги в нужных местах.
Если же после первой обработки текст оказывается длиннее 128 символов, то HTML-теги будут удалены, и текст снова обрежется:
"Это длинный текст с HTML тегами"
Теперь текст обрезается до 120 символов без тегов.
Итоги
Код отвечает за создание краткого превью текста из сообщения, обрезая его до определенной длины и обрабатывая HTML-теги.
В случае, если текст слишком длинный после первой обработки, удаляются HTML-теги, и текст обрезается заново.
Комментарии (0)
Автор контента
Оффлайн
Administrator
Последняя авторизация: 05.07.2026 18:32
- Страница размещена: 22.01.2025 21:41
- Последнее обновление: 22.01.2025 21:41