You are here

function readmore_truncate_string in Readmore 2.x

Same name and namespace in other branches
  1. 8 readmore.module \readmore_truncate_string()
  2. 7 readmore.module \readmore_truncate_string()

Truncate string by a number of characters.

Parameters

string $text: The string to truncate.

string $format: The format of the content.

int $size: An upper limit on the returned string length.

bool $wordsafe: If TRUE, attempt to truncate on a word boundary.

Return value

string Return truncated string.

1 call to readmore_truncate_string()
ReadmoreFormatter::viewElements in src/Plugin/Field/FieldFormatter/ReadmoreFormatter.php
Builds a renderable array for a field value.

File

./readmore.module, line 39
Contains readmore module.

Code

function readmore_truncate_string($text, $format = NULL, $size = NULL, $wordsafe = FALSE, $use_break = TRUE) {
  if (!isset($size)) {

    // If size is not set then use default.
    $size = 500;
  }
  if ($use_break) {

    // Find where the delimiter is in the body.
    $delimiter = mb_strpos($text, '<!--break-->');
    if ($delimiter) {

      // Set new size.
      $size = $delimiter;
    }
  }

  // We check for the presence of the PHP evaluator filter in the current
  // format. If the body contains PHP code just return as is.
  $filters = [];
  if (isset($format)) {
    $filter_entities = \Drupal::entityTypeManager()
      ->getStorage('filter_format')
      ->loadByProperties([
      'status' => TRUE,
    ]);
    foreach ($filter_entities as $filter) {
      $filters[] = $filter
        ->id();
      if ($filter
        ->id() == 'php_code' && strpos($text, '<?') !== FALSE) {
        return $text;
      }
    }
  }

  // The summary may not be longer than maximum length specified. Initial slice.
  $summary = Unicode::truncate($text, $size, $wordsafe, FALSE);
  if ($wordsafe) {

    // Store the actual length of the truncated string.
    $max_rpos = mb_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_rpos = $max_rpos;

    // Store the reverse of the summary.
    $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($filters['filter_autop'])) {
      $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.
        $rpos = mb_strpos($reversed, strrev($point));
        if ($rpos !== FALSE) {
          $min_rpos = min($rpos + $offset, $min_rpos);
        }
      }

      // If a break point was found in this group, slice and stop searching.
      if ($min_rpos !== $max_rpos) {

        // Don't slice with length 0. Length must be <0 to slice from RHS.
        $summary = $min_rpos === 0 ? $summary : mb_substr($summary, 0, 0 - $min_rpos);
        break;
      }
    }
  }
  return $summary;
}