$terms lower-cased, matched as prefixes like the full-text search */ public static function around(string $text, array $terms, int $length = self::LENGTH): string { $text = trim((string) preg_replace('/\s+/u', ' ', $text)); $textLength = mb_strlen($text); if ($textLength <= $length) { return $text; } $position = null; $lower = mb_strtolower($text); foreach ($terms as $term) { $found = mb_strpos($lower, $term); if (false !== $found && (null === $position || $found < $position)) { $position = $found; } } // Start a third of the window before the match, so it is read in context. $start = max(0, min(($position ?? 0) - intdiv($length, 3), $textLength - $length)); $excerpt = mb_substr($text, $start, $length); if ($start > 0) { $space = mb_strpos($excerpt, ' '); $excerpt = '…'.(false !== $space && $space < 40 ? mb_substr($excerpt, $space + 1) : $excerpt); } if ($start + $length < $textLength) { $space = mb_strrpos($excerpt, ' '); $excerpt = (false !== $space && mb_strlen($excerpt) - $space < 40 ? mb_substr($excerpt, 0, $space) : $excerpt).'…'; } return $excerpt; } }