4.6.x node.module node_teaser($body, $format = NULL)
4.7.x node.module node_teaser($body, $format = NULL)
5.x node.module node_teaser($body, $format = NULL)
6.x node.module node_teaser($body, $format = NULL, $size = NULL)

Generate a teaser for a node body.

If the end of the teaser is not indicated using the <!--break--> delimiter then we generate the teaser 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).


$body: The content for which a teaser will be generated.

$format: The format of the content. If the content contains PHP code, we do not split it up to prevent parse errors. If the line break filter is present then we treat newlines embedded in $body as line breaks.

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

Return value

The generated teaser.

2 calls to node_teaser()
node_preview in modules/node/node.pages.inc
Generate a node preview.
node_submit in modules/node/node.module
Prepares a node for saving by populating teaser, author, and creation date.


modules/node/node.module, line 306
The core that allows content to be submitted to the site. Modules and scripts may programmatically submit nodes using the usual form API pattern.


function node_teaser($body, $format = NULL, $size = NULL) {
  if (!isset($size)) {
    $size = variable_get('teaser_length', 600);

  // Find where the delimiter is in the body
  $delimiter = strpos($body, '<!--break-->');

  // If the size is zero, and there is no delimiter, the entire body is the teaser.
  if ($size == 0 && $delimiter === FALSE) {
    return $body;

  // If a valid delimiter has been specified, use it to chop off the teaser.
  if ($delimiter !== FALSE) {
    return substr($body, 0, $delimiter);

  // We check for the presence of the PHP evaluator filter in the current
  // format. If the body contains PHP code, we do not split it up to prevent
  // parse errors.
  if (isset($format)) {
    $filters = filter_list_format($format);
    if (isset($filters['php/0']) && strpos($body, '<?') !== FALSE) {
      return $body;

  // If we have a short body, the entire body is the teaser.
  if (drupal_strlen($body) <= $size) {
    return $body;

  // If the delimiter has not been specified, try to split at paragraph or
  // sentence boundaries.
  // The teaser may not be longer than maximum length specified. Initial slice.
  $teaser = truncate_utf8($body, $size);

  // Store the actual length of the UTF8 string -- which might not be the same
  // as $size.
  $max_rpos = strlen($teaser);

  // How much to cut off the end of the teaser 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 teaser.  We use strpos on the reversed needle and
  // haystack for speed and convenience.
  $reversed = strrev($teaser);

  // Build an array of arrays of break points grouped by preference.
  $break_points = array();

  // A paragraph near the end of sliced teaser is most preferable.
  $break_points[] = array(
    '</p>' => 0,

  // If no complete paragraph then treat line breaks as paragraphs.
  $line_breaks = array(
    '<br />' => 6,
    '<br>' => 4,

  // Newline only indicates a line break if line break converter
  // filter is present.
  if (isset($filters['filter/1'])) {
    $line_breaks["\n"] = 1;
  $break_points[] = $line_breaks;

  // If the first paragraph is too long, split at the end of a sentence.
  $break_points[] = array(
    '. ' => 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 teaser.
    foreach ($points as $point => $offset) {

      // The teaser is already reversed, but the break point isn't.
      $rpos = 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 return the teaser.
    if ($min_rpos !== $max_rpos) {

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

  // If a break point was not found, still return a teaser.
  return $teaser;


dalin’s picture

Note that even though the format is a function argument, no check_markup is done in this function. The returned string is not safe and you need to sanitize before displaying.

dMaggot’s picture

This function was changed to text_summary in Drupal 7.

Baysaa’s picture

$teaser = truncate_utf8($body, $size);

I'm really surprised that this function doesn't call truncate_utf8 with the $wordsafe=TRUE parameter:

$teaser = truncate_utf8($body, $size, TRUE);

Is there a way to enforce this without having to hack the node.module core? I notice that the rest of the function tries to find a full stop or some sort of a paragraph ending or line endings, but if it fails to find any of those, then it just leaves it with a cut-off word.