function text_summary

Same name in other branches
  1. 7.x modules/field/modules/text/text.module \text_summary()
  2. 9 core/modules/text/text.module \text_summary()
  3. 8.9.x core/modules/text/text.module \text_summary()
  4. 10 core/modules/text/text.module \text_summary()

Generates a trimmed, formatted version of a text field value.

If the end of the summary is not indicated using the <!--break--> delimiter then we generate the summary automatically, trying to end it at a sensible place such as the end of a paragraph, a line break, or the end of a sentence (in that order of preference).

Parameters

$text: The content for which a summary will be generated.

$format: The format of the content. If the line break filter is present then we treat newlines embedded in $text as line breaks. If the htmlcorrector filter is present, it will be run on the generated summary (if different from the incoming $text).

$size: The desired character length of the summary. If omitted, the default value will be used. Ignored if the special delimiter is present in $text.

Return value

string The generated summary.

4 calls to text_summary()
node_tokens in core/modules/node/node.tokens.inc
Implements hook_tokens().
TextSummaryTest::assertTextSummary in core/modules/text/tests/src/Kernel/TextSummaryTest.php
Calls text_summary() and asserts that the expected teaser is returned.
TextSummaryTest::testNormalization in core/modules/text/tests/src/Kernel/TextSummaryTest.php
Test text normalization when filter_html or filter_htmlcorrector enabled.
TextTrimmedFormatter::preRenderSummary in core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php
Pre-render callback: Renders a processed text element's #markup as a summary.

File

core/modules/text/text.module, line 64

Code

function text_summary($text, $format = NULL, $size = NULL) {
    if (!isset($size)) {
        $size = \Drupal::config('text.settings')->get('default_summary_length');
    }
    // Find where the delimiter is in the body.
    $delimiter = strpos($text, '<!--break-->');
    // If the size is zero, and there is no delimiter, the entire body is the summary.
    if ($size == 0 && $delimiter === FALSE) {
        return $text;
    }
    // If a valid delimiter has been specified, use it to chop off the summary.
    if ($delimiter !== FALSE) {
        return substr($text, 0, $delimiter);
    }
    // Retrieve the filters of the specified text format, if any.
    if (isset($format)) {
        $filter_format = FilterFormat::load($format);
        // If the specified format does not exist, return nothing. $text is already
        // filtered text, but the remainder of this function will not be able to
        // ensure a sane and secure summary.
        if (!$filter_format || !($filters = $filter_format->filters())) {
            return '';
        }
    }
    // If we have a short body, the entire body is the summary.
    if (mb_strlen($text) <= $size) {
        return $text;
    }
    // If the delimiter has not been specified, try to split at paragraph or
    // sentence boundaries.
    // The summary may not be longer than maximum length specified. Initial slice.
    $summary = Unicode::truncate($text, $size);
    // Store the actual length of the UTF8 string -- which might not be the same
    // as $size.
    $max_right_pos = strlen($summary);
    // How much to cut off the end of the summary so that it doesn't end in the
    // middle of a paragraph, sentence, or word.
    // Initialize it to maximum in order to find the minimum.
    $min_right_pos = $max_right_pos;
    // Store the reverse of the summary. We use strpos on the reversed needle and
    // haystack for speed and convenience.
    $reversed = strrev($summary);
    // Build an array of arrays of break points grouped by preference.
    $break_points = [];
    // A paragraph near the end of sliced summary is most preferable.
    $break_points[] = [
        '</p>' => 0,
    ];
    // If no complete paragraph then treat line breaks as paragraphs.
    $line_breaks = [
        '<br />' => 6,
        '<br>' => 4,
    ];
    // Newline only indicates a line break if line break converter
    // filter is present.
    if (isset($format) && $filters->has('filter_autop') && $filters->get('filter_autop')->status) {
        $line_breaks["\n"] = 1;
    }
    $break_points[] = $line_breaks;
    // If the first paragraph is too long, split at the end of a sentence.
    $break_points[] = [
        '. ' => 1,
        '! ' => 1,
        '? ' => 1,
        '。' => 0,
        '؟ ' => 1,
    ];
    // Iterate over the groups of break points until a break point is found.
    foreach ($break_points as $points) {
        // Look for each break point, starting at the end of the summary.
        foreach ($points as $point => $offset) {
            // The summary is already reversed, but the break point isn't.
            $right_pos = strpos($reversed, strrev($point));
            if ($right_pos !== FALSE) {
                $min_right_pos = min($right_pos + $offset, $min_right_pos);
            }
        }
        // If a break point was found in this group, slice and stop searching.
        if ($min_right_pos !== $max_right_pos) {
            // Don't slice with length 0. Length must be <0 to slice from RHS.
            $summary = $min_right_pos === 0 ? $summary : substr($summary, 0, 0 - $min_right_pos);
            break;
        }
    }
    // If filter_html or filter_htmlcorrector is enabled, normalize the output.
    if (isset($format)) {
        $filter_enabled = function (string $filter) use ($filters) : bool {
            return $filters->has($filter) && $filters->get($filter)->status;
        };
        if ($filter_enabled('filter_html') || $filter_enabled('filter_htmlcorrector')) {
            $summary = Html::normalize($summary);
        }
    }
    return $summary;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.