Обрезка текста и/или замена стандартной функции the_excerpt()
Функция the_excerpt() в WordPress работает неоптимально — она вызывает get_the_excerpt(), the_content() и применяет к ним множество хуков. В результате на обработку уходит больше ресурсов, чем это оправдано в большинстве случаев.
Лично мне для цитат достаточно просто вырезать фрагмент текста, кратко передающий суть статьи. Поэтому, когда появилась необходимость ограничить длину текста в символах, я решил написать собственную функцию для обрезки.
Результат замены the_excerpt() оказался заметным: время генерации страницы сократилось с 0,850 сек до 0,550 сек при 9 вызовах the_excerpt() (тест на локальном компьютере, на сервере показатели обычно лучше). Эти 9 вызовов приходились на посты в рубрике, каждому из которых применялся the_excerpt().
Итог — более быстрая загрузка страницы без потери функциональности.
Альтернатива этой функции, которая есть в движке WP: wp_trim_words(). Она работает очень быстро!
Ниже функция, которой можно заменить стандартную функцию WordPress the_excerpt().
GitHub<?php
/**
* Cuts the specified text up to specified number of characters.
* Strips any of shortcodes.
*
* !IMPORTANT: This function is NOT escaped the text.
* Assumed the text is already escaped with wp_kses() or similar function.
*
* @version 2.8.1
*
* Important changes:
* v2.8.0 - Improved logic to work with HTML tags in cutting text.
* v2.7.2 - Cuts direct URLs from content.
* v2.7.0 - `sanitize_callback` parameter.
* v2.6.5 - `ignore_more` parameter.
* v2.6.2 - Regular to remove blocky shortcodes like: [foo]some data[/foo].
* v2.6 - Removed the `save_format` parameter and replaced it with two parameters `autop` and `save_tags`.
*
* @param string|array $args {
* Optional. Arguments to customize output.
*
* @type int $maxchar Max number of characters.
* @type string $text The text to be cut. The default is `post_excerpt` if there is no `post_content`.
* If the text has `<!--more-->`, then `maxchar` is ignored and everything
* up to `<!--more-->` is taken including HTML.
* @type bool $autop Replace the line breaks with `<p>` and `<br>` or not?
* @type string $more_text The text of `Read more` link.
* @type string $save_tags Tags to be left in the text. For example `<strong><b><a>`.
* @type string $sanitize_callback Text cleaning function.
* @type bool $ignore_more Whether to ignore `<!--more-->` in the content.
* }
*
* @return string HTML
*/
function kama_excerpt( $args = '' ): string {
global $post;
if( is_string( $args ) ){
parse_str( $args, $args );
}
$rg = (object) array_merge( [
'maxchar' => 350,
'text' => '',
'autop' => true,
'more_text' => 'Reed more...',
'ignore_more' => false,
'save_tags' => '<strong><b><a><em><i><var><code><span>',
'sanitize_callback' => static function( string $text, object $rg ) {
return strip_tags( $text, $rg->save_tags );
},
], $args );
$rg = apply_filters( 'kama_excerpt_args', $rg );
if( ! $rg->text ){
$rg->text = $post->post_excerpt ?: $post->post_content;
}
$text = $rg->text;
// strip content shortcodes: [foo]some data[/foo]. Consider markdown
$text = preg_replace( '~\[([a-z0-9_-]+)[^\]]*\](?!\().*?\[/\1\]~is', '', $text );
// strip others shortcodes: [singlepic id=3]. Consider markdown
$text = preg_replace( '~\[/?[^\]]*\](?!\()~', '', $text );
// strip direct URLs
$text = preg_replace( '~(?<=\s)https?://.+\s~', '', $text );
$text = trim( $text );
// <!--more-->
if( ! $rg->ignore_more && strpos( $text, '<!--more-->' ) ){
preg_match( '/(.*)<!--more-->/s', $text, $mm );
$text = trim( $mm[1] );
$text_append = sprintf( ' <a href="%s#more-%d">%s</a>', get_permalink( $post ), $post->ID, $rg->more_text );
}
// text, excerpt, content
else {
$text = call_user_func( $rg->sanitize_callback, $text, $rg );
$has_tags = false !== strpos( $text, '<' );
// collect html tags
if( $has_tags ){
$tags_collection = [];
$nn = 0;
$text = preg_replace_callback( '/<[^>]+>/', static function( $match ) use ( & $tags_collection, & $nn ) {
$nn++;
$holder = "~$nn";
$tags_collection[ $holder ] = $match[0];
return $holder;
}, $text );
}
// cut text
$cuted_text = mb_substr( $text, 0, $rg->maxchar );
if( $text !== $cuted_text ){
// del last word, it not complate in 99%
$text = preg_replace( '/(.*)\s\S*$/s', '\\1...', trim( $cuted_text ) );
}
// bring html tags back
if( $has_tags ){
$text = strtr( $text, $tags_collection );
$text = force_balance_tags( $text );
}
}
// add <p> tags. Simple analog of wpautop()
if( $rg->autop ){
$text = preg_replace(
[ "/\r/", "/\n{2,}/", "/\n/" ],
[ '', '</p><p>', '<br />' ],
"<p>$text</p>"
);
}
$text = apply_filters( 'kama_excerpt', $text, $rg );
if( isset( $text_append ) ){
$text .= $text_append;
}
return $text;
}
Как работает функция
-
Обрезает до определенного количества символов. Указывается в параметре
maxchar. -
Понимает тег
<!--more-->в записи. Если он присутствует, желаемое количество выводимых символов игнорируется и выводится все что выше<!--more-->с сохранением HTML разметки. -
Можно указать сохранять переносы строк или писать весь текст в одну строку. По умолчанию переносы сохраняются, если нужен "сплошняк" ставим параметр autop = 0.
-
Можно указан какие HTML теги не нужно вырезать. Например мы хотим оставить теги
<strong>и<em>, тогда указываем их так:save_tags=<strong><em> - Функцию также можно использовать для обрезки любого текста, который ей будет передан. Для этого указываем текст в параметре
text.
Обрезка во всех случаях вычисляет количество символов, а затем убирает последние символы до пробела. Нужно это для того, чтобы в конце всегда оставалось законченное слово, а не кусок недописанного (думаю это некрасиво).
Использование
Вставляем указанный выше код в файл functions.php вашей темы. И где нужно вывести обрезанный текст вызываем функцию так:
<?php echo kama_excerpt( [ 'maxchar'=>100, 'text'=>'бла бла' ] ); ?>
Чтобы заменить стандартный the_excerpt() нужно просто заменить the_excerpt() на kama_excerpt(). Все это должно быть внутри цикла WordPress.
ВАЖНО: Параметр text при замене the_excerpt(); указывать не надо!
Пример использования функции для обрезки любого текста:
$str = "Функцию [foo]some text[/foo] [foo]some text[/foo] обрезки текста для Worpress, можно применять [foo url='bar'] и на других движках."; echo kama_excerpt([ 'text'=>$str, 'maxchar'=>70 ]);
Получим:
<p>Функцию обрезки текста для Worpress,<br /> можно применять и на других ...</p>
При обрезке передаваемого функции текста, срабатывает только та часть функции которая необходима (никаких лишних операций).
Очень простой пример обрезки текста
Если вы не хотите использовать функцию и вам нужно просто обрезать текст не сохранения html теги и прочее, то можно воспользоваться такой короткой строкой внутри цикла WordPress:
<?php $maxchar = 152; $text = strip_tags( get_the_content() ); echo mb_substr( $text, 0, $maxchar ); ?>
152 - это количество оставляемых символов.
Или
Используйте функцию wp_trim_words().