WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Обрезка текста и/или замена стандартной функции the_excerpt()

Обратил внимание, что функция WordPress the_excerpt() громоздкая. На её выполнение уходит много времени и ресурсов. Потому что она вызывает get_the_excerpt(), the_content() и ко всем ним, включая сам the_excerpt(), применяются различные хуки. В итоге получается немало операций - это нужно далеко не всегда. Я например, к цитатам отношусь просто - коротко сказать о чем статья, вырезав небольшой её кусок - достаточно просто текста.

С этим недостатком я мерился, до того момента, пока мне не понадобилась обрезать текст до определенного количества символов. Тогда то я и решил написать функцию обрезающую текст.

Результат замены the_excerpt() меня порадовал: генерация страницы уменьшилась в среднем с 0,850 сек до 0,550 сек, при 9 вызовах the_excerpt() (это время на компьютере, на сервере обычно оно меньше). 9 вызовов - это количество выводимых постов в рубрике, к каждому из которых применялся the_excerpt().

Ниже функция, которой можно заменить стандартную функцию WordPress the_excerpt().

/**
 * Обрезка текста (excerpt). Шоткоды вырезаются. Минимальное значение maxchar может быть 22.
 *
 * @param string/array $args Параметры.
 *
 * @return string HTML
 *
 * @ver 2.6.4
 */
function kama_excerpt( $args = '' ){
	global $post;

	if( is_string($args) )
		parse_str( $args, $args );

	$rg = (object) array_merge( array(
		'maxchar'   => 350,   // Макс. количество символов.
		'text'      => '',    // Какой текст обрезать (по умолчанию post_excerpt, если нет post_content.
							  // Если в тексте есть `<!--more-->`, то `maxchar` игнорируется и берется все до <!--more--> вместе с HTML.
		'autop'     => true,  // Заменить переносы строк на <p> и <br> или нет?
		'save_tags' => '',    // Теги, которые нужно оставить в тексте, например '<strong><b><a>'.
		'more_text' => 'Читать дальше...', // Текст ссылки `Читать дальше`.
	), $args );

	$rg = apply_filters( 'kama_excerpt_args', $rg );

	if( ! $rg->text )
		$rg->text = $post->post_excerpt ?: $post->post_content;

	$text = $rg->text;
	$text = preg_replace( '~\[([a-z0-9_-]+)[^\]]*\](?!\().*?\[/\1\]~is', '', $text ); // убираем блочные шорткоды: [foo]some data[/foo]. Учитывает markdown
	$text = preg_replace( '~\[/?[^\]]*\](?!\()~', '', $text ); // убираем шоткоды: [singlepic id=3]. Учитывает markdown
	$text = trim( $text );

	// <!--more-->
	if( strpos( $text, '<!--more-->') ){
		preg_match('/(.*)<!--more-->/s', $text, $mm );

		$text = trim( $mm[1] );

		$text_append = ' <a href="'. get_permalink( $post ) .'#more-'. $post->ID .'">'. $rg->more_text .'</a>';
	}
	// text, excerpt, content
	else {
		$text = trim( strip_tags($text, $rg->save_tags) );

		// Обрезаем
		if( mb_strlen($text) > $rg->maxchar ){
			$text = mb_substr( $text, 0, $rg->maxchar );
			$text = preg_replace( '~(.*)\s[^\s]*$~s', '\\1...', $text ); // убираем последнее слово, оно 99% неполное
		}
	}

	// Сохраняем переносы строк. Упрощенный аналог wpautop()
	if( $rg->autop ){
		$text = preg_replace(
			array("/\r/", "/\n{2,}/", "/\n/",   '~</p><br ?/?>~'),
			array('',     '</p><p>',  '<br />', '</p>'),
			$text
		);
	}

	$text = apply_filters( 'kama_excerpt', $text, $rg );

	if( isset($text_append) )
		$text .= $text_append;

	return ( $rg->autop && $text ) ? "<p>$text</p>" : $text;
}
/* Сhangelog:
 * 2.6.4 - Убрал пробел между словом и многоточием
 * 2.6.3 - Рефакторинг
 * 2.6.2 - Добавил регулярку для удаления блочных шорткодов вида: [foo]some data[/foo]
 * 2.6   - Удалил параметр 'save_format' и заменил его на два параметра 'autop' и 'save_tags'.
 *       - Немного изменил логику кода.
 */

Как работает функция

  1. Обрезать до определенного количества символов. Указывается в параметре maxchar.

  2. Понимает тег <!--more--> в записи. Если он присутствует желаемое количество выводимых символов игнорируется и выводится все что выше  <!--more--> с сохранением HTML разметки.

  3. Можно указать сохранять переносы строк или писать весь текст в одну строку. По умолчанию переносы сохраняются, если нужен "сплошняк" ставим параметр autop=0.

  4. Можно указан какие HTML теги не нужно вырезать, например мы хотим оставить тег stront или em, тогда указываем их в параметре save_tags: save_tags=<strong><em>

  5. Также, можно использовать функцию, чтобы обрезать любой текст, который ей будет передан через параметр text.

Обрезка во всех случаях вычисляет количество символов, а затем убирает последние символы до пробела. Нужно это для того, чтобы в конце всегда оставалось законченное слово, а не кусок недописанного слова - я думаю это некрасиво.

Использование

Вставляем указанный выше код в файл functions.php вашего шаблона. И где нужно вывести обрезанный текст вызываем функцию так:

<?php echo kama_excerpt( array('maxchar'=>100, 'text'=>'бла бла') ); ?>

Чтобы заменить стандартный the_excerpt() нужно просто заменить the_excerpt() на kama_excerpt() Все это должно быть внутри цикла the loop.

ВАЖНО: Параметр text при замене the_excerpt(); указывать не надо!

Пример использования функции, как обрезка любого текста (в любом месте шаблона):

$str = "Функцию [foo]some text[/foo] [foo]some text[/foo] обрезки текста для Worpress, 
можно применять [foo url='bar'] и на других движках.";

echo esc_html( kama_excerpt([ 'text'=>$str, 'maxchar'=>70 ]) );

Получим:

<p>Функцию   обрезки текста для Worpress,<br />
можно применять  и на других ...</p>

При обрезке передаваемого функции текста, срабатывает только та часть функции которая необходима, т.е. никаких лишних операций.

Очень простой пример обрезки текста

Если вы не хотите использовать функцию и вам нужно просто обрезать текст не сохранения html теги и прочее, то можно воспользоваться такой короткой строкой внутри цикла WordPress:

<?php 
$text = strip_tags( get_the_content() );
echo mb_substr( $text, 0, 152 );
?>

где 152 - это количество оставляемых символов.

143 коммента
Полезные 4 Вопросы 3 Все
  • @ Евгений

    Здравствуйте, Kama.

    Очень нужная функция, которую вы представили, но в моем случае она, все равно, выводит первое изображение?

    В моей теме разработчиками вывод задуман просто:

    <?php echo wp_trim_words(get_the_content(), 20); ?>

    Но у меня контентом являются стихи, что особо бережно требует относиться к переносам строк help

    Стандартные функции вордпресса не умеют сохранять переносы?

    Спасибо за фунцию good

    Ответить5 лет назад #
    • Kama7629

      А что конкретно нужно изменить?

      Ответить5 лет назад #
      • @ Евгений

        Прошу прощения, не отвечал... ждал уведомления по почте об ответе smile теперь галочку увидел smile

        Нужно выводить краткую часть поста до "more", сохраняя переносы (стихи smile требуют).
        Может быть вы подскажете?

        Ответить5 лет назад #
        • Vanya grainheaders.com

          echo mb_substr( strip_tags( get_the_content(), '' ), 0, 152 );
          Будет удалять все теги кроме абзацов.

          Ответить3.9 года назад #
  • @ Слава

    Здравствуйте!Хочу сказать спасибо за создание этого сайта. Без него вообще не как в создании тем для wp.

    Ответить4.7 года назад #
    • Kama7629

      Пожалуйста. А много тем создали уже? Интересно посмотреть что получается, но я не настаиваю совсем smile

      Ответить4.7 года назад #
      • @ Слава

        Пока на локалке всего одна тема, если примерно, то процентов 70 от задуманного уже выполнил. Могу с радостью скинуть её вам, например на почту. Так как тема делается для ресурса с 1000+ посещаемостью (Что для меня безумное достижение) буду очень рад любым замечанием и рекомендациям.

        PS Планируется ещё один проект, для него тема пока в PSD но уже точно знаю на 100% что создаваться она будет с помощью wp-kama.ru тут у вас все есть и по сети искать не надоmosking

        Ответить4.7 года назад #
  • @ Артем2 all-mods.ru

    Случайно никто не "расширял" эту функцию?

    В большими постами, даже kama_excerpt работает не очень быстро. Хочется что бы:

    При вызове kama_excerpt(100)

    Проверяем наличие контента в поле цитата

    Если есть - проверяем что бы оно было 100 символов, если символов больше - обрезаем / меньше - выводим что есть;

    Если нету контента в поле цитата берем контент поста и обрезаем до 100 символов, затем записываем его в поле цитата и выводим;

    Ответить4.5 года назад #
    • Kama7629

      Как вы проверяете скорость, можно узнать?

      Проверяем наличие контента в поле цитата

      Если есть - проверяем что бы оно было 100 символов, если символов больше - обрезаем / меньше - выводим что есть;

      Так и работает...

      Если нету контента в поле цитата берем контент поста и обрезаем до 100 символов,

      Если нет цитаты, берется контент и обрезается до 100. Но только в том случае, если в контенте не указан тег <!--more-->. Если more есть, то выводится он (текст до него) как есть.

      затем записываем его в поле цитата и выводим;

      Это уже обработка данных в БД. Я думаю - это лишнее... Кому нужно сам должен сделать эту оптимизацию... В рамках данной функции это по моему уже слишком...

      -

      Сейчас обновил функцию: изменил немного код и заменил iconv_* на mb_* так немного быстрее работает обрезка.

      Обновите функцию, может подойдет вам, если что не так или так пишите пожалуйста.

      Ответить4.5 года назад #
      • @ Артем2 all-mods.ru

        Просто в моем случае писать цитату к каждому посту не вариант, а если бы при выводе постов использовалось бы поле цитата (которое 1 раз автоматически заполнялось), а не каждый раз обрезалось из контента - думаю было бы быстрее.

        Проблему вроде как нашел: Функция начинает медленно работать если в посте много картинок, например у меня их было 40 штук, в результате время генерации страницы увеличивалось ~ в 2 раза. Обновил пост засунув все картинки в галерею - вроде помогло.

        Ответить4.5 года назад #
        • @ Артем2 all-mods.ru

          Просто у меня на сайте при вызове kama_excerpt() текст всегда обрезается до 250 символов, т.е. нет такого что на одной странице выводится 100 символов, на другой 200 и т.п. - везде одинаково. По-этому, я уверен на 99%, что если при генерации страниц 1 раз обрезать контент и записать получившийся текст в цитату, а затем уже выводить напрямую из цитаты, не используя обрезку - будет быстрее и менее ресурсозатратно.

          Но это уже частный случай, у кого-то, возможно, в одном месте выводится 100 символов, в другом месте 200 - им такое не подойдет.

          Ответить4.5 года назад #
          • Kama7629

            Скрипт обрезает любой текст, вообще любой... Можно передать текст в параметр text и скрпит его обрежет. Если у поста есть цитата, то она тоже обрезается до указанного кол. символов.

            В вашем случае вы сами можете повесить хук. При сохранении поста брать контент обрезать его с помощью kama_excerpt (или как-то еще) и записывать результат в цитату (post_excerpt). Но в эту функцию, такой функционал совать нелогично...

            Ответить4.5 года назад #
            • @ Артем2 all-mods.ru

              Дело в том, что на сайте уже есть больше 8000 записей и пересохранять их - извращение. А если сделать так как написал я - тогда со временем у большинства постов автоматически заполнится поле цитата и затем текст уже будет браться оттуда, без обрезки.

              Я понимаю что это очень частный случай, по этому и спросил, может кто делал себе такое smile

              А почему работа функции замедляется если в посте много картинок? Из-за того что каждая картинка обладает html тегами, ссылками и т.п., что увеличивает кол-во символов в посте?

              Ответить4.5 года назад #
  • Как использовать эту функцию в сторонних функциях?
    Например попытка сделать:

    /*Add Description */
    function og_description(){
    	if (is_single()) $description = kama_excerpt("maxchar=100&save_format=0");
    	elseif (is_home()) $description = get_bloginfo('description');
    	if (empty($description))
    		echo "";
    	else
    		echo '<meta property="og:description" content="'.$description.'"/>'."\n";
    }
    add_action('wp_head', 'og_description', -1);

    Возвращает
    <pre><p>bla-bla-bla<br /></p><meta property="og:description" content="1"/></pre>

    Ответить4.5 года назад #
    • Kama7629

      Вам нужно получить данные для обработки, для этого echo укажите:

      kama_excerpt("maxchar=100&save_format=0&echo=0");
      1
      Ответить4.5 года назад #
      • Отлично, всё получилось! Я невнимательно прочитал аргументы в заголовке )

        Ответить4.5 года назад #
  • @ AndreyEx остекление-балконов-к...

    Благодарю за статью, успешно применил урезанные статьи по второму примеру, не стал загромождать файл functions.php

    Ответить4.3 года назад #
  • Otshelnik-fm213 otshelnik-fm.ru

    Спам с этой страницы сыпется который день. Пора менять защиту от спамеров

    Ответить4.2 года назад #
  • Николай

    выручаете постоянно, спасибо за классную функцию

    1
    Ответить4 года назад #
  • Игорь

    Некорректно обрабатывает амперсанд в тексте. Если попадается, обрезается на нём вне зависимости от указанного количества символов.

    Ответить4 года назад #
    • Игорь
      • Если передавать текст как параметр.

      Дело в том, что параметры можно передавать только как строку, а амперсанд воспринимается как конец параметра.

      Есть два варианта:

      1. Обрабатывать строку параметров до parse_str и экранировать амперсанд.
      2. Дать возможность передавать параметры как массив.

      Я остановился на втором, так как это быстрее:
      Заменяем

      parse_str( $args, $_args );
      $args = array_merge( $default, $_args );

      На

      if(!is_array($args)) {
      		parse_str( $args, $args );
      	}
      
      	$args = array_merge( $default, $args );

      И передаём параметры как массив.

      Но по-хорошему нужно делать и то и то.

      1
      Ответить4 года назад #
      • @ Александр okna-poly-volgograd.ru/prajs-list

        все верно

        Ответить4 года назад #
      • Kama7629

        Обновил функцию до версии 2.2, сделал чтобы массив параметров можно было передавать и еще там по мелочи код изменил. Проверьте плз., отпишитесь. Спасибо!

        2
        Ответить4 года назад #
  • Mari

    Долго и упорно искала решение на английских сайта и нашла опять на родном русском yes . Мой шаблон не поддерживал custom post type и не выводил описания, ваш код помог решить данный вопрос. Спасибо.
    Подскажите, пожалуйста, еще один момент. Есть шоткод в котором размещена ссылка на видео Youtube, так эта ссылка также выводится через вашу функцию. Можно ли как-то убрать и ее, как убрали сами знаки шоткодов. Полностью вырезать шорткод не подходит в моем случае, т.к. исчезает текст полностью.
    Заранее благодарю.

    Ответить3.10 года назад #
    • Kama7629

      Отдельное регулярное выражение надо писать. Скиньте пример что нужно вырезать.

      Ответить3.10 года назад #
      • Mari

        Вот пример ссылки: https://www.youtube.com/watch?v=oM4wfkl2uYE

        Ответить3.10 года назад #
        • Kama7629

          Вашу ссылку на youtube можно вырезывать из текста так:

          $text = preg_replace('/\nhttps?:\/\/(?:www\.)?youtube[^\n]+\n/', '', $text );
          1
          Ответить3.10 года назад #
          • Mari

            Большое спасибо, код работает.
            Ссылки Youtube менялись несколько раз, чтоб захватить все старые ссылки и убрать их из функции the_excerpt() можно использовать код ниже, возможно кому-то будет полезно smile

            $text = preg_replace('~(?:http|https|)(?::\/\/|)(?:www.|)(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\/ytscreeningroom\?v=|\/feeds\/api\/videos\/|\/user\S*[^\w\-\s]|\S*[^\w\-\s]))([\w\-]{11})[a-z0-9;:@?&%=+\/\$_.-]*~i', '', $excerpt);
            }
            Ответить3.9 года назад #
  • Владимир КОМИблог.РФ

    Спасибо!

    В php я - ноль. Ваш сайт очень большой Помощник в моем деле.

    Ответить3.8 года назад #
Здравствуйте, !     Войти . Зарегистрироваться