65 $contextlines = self::DEFAULT_CONTEXT_LINES,
66 $contextchars = self::DEFAULT_CONTEXT_CHARS
68 $searchHighlightBoundaries = MediaWikiServices::getInstance()
69 ->getMainConfig()->get( MainConfigNames::SearchHighlightBoundaries );
76 $spat =
"/(\\{\\{)|(\\[\\[[^\\]:]+:)|(\n\\{\\|)";
79 1 =>
'/(\{\{)|(\}\})/',
80 2 =>
'/(\[\[)|(\]\])/',
81 3 =>
"/(\n\\{\\|)|(\n\\|\\})/" ];
85 if ( \ExtensionRegistry::getInstance()->isLoaded(
'Cite' ) ) {
87 $endPatterns[4] =
'/(<ref>)|(<\/ref>)/';
93 $textLen = strlen( $text );
95 while ( $start < $textLen ) {
97 if ( preg_match( $spat, $text,
$matches, PREG_OFFSET_CAPTURE, $start ) ) {
99 foreach (
$matches as $key => $val ) {
100 if ( $key > 0 && $val[1] != -1 ) {
103 $ns = substr( $val[0], 2, -1 );
105 MediaWikiServices::getInstance()->getContentLanguage()->
112 $epat = $endPatterns[$key];
113 $this->splitAndAdd( $textExt, $count, substr( $text, $start, $val[1] - $start ) );
121 $offset = $start + 1;
123 while ( preg_match( $epat, $text, $endMatches, PREG_OFFSET_CAPTURE, $offset ) ) {
124 if ( array_key_exists( 2, $endMatches ) ) {
127 $len = strlen( $endMatches[2][0] );
128 $off = $endMatches[2][1];
129 $this->splitAndAdd( $otherExt, $count,
130 substr( $text, $start, $off + $len - $start ) );
131 $start = $off + $len;
142 $offset = $endMatches[0][1] + strlen( $endMatches[0][0] );
146 $this->splitAndAdd( $textExt, $count, substr( $text, $start, strlen(
$matches[0][0] ) ) );
153 $this->splitAndAdd( $textExt, $count, substr( $text, $start ) );
156 '@phan-var string[] $textExt';
158 $all = $textExt + $otherExt;
161 foreach ( $terms as $index => $term ) {
163 if ( preg_match(
'/[\x80-\xff]/', $term ) ) {
164 $terms[$index] = preg_replace_callback(
166 [ $this,
'caseCallback' ],
170 $terms[$index] = $term;
173 $anyterm = implode(
'|', $terms );
174 $phrase = implode(
"{$searchHighlightBoundaries}+", $terms );
179 $scale = strlen( $anyterm ) / mb_strlen( $anyterm );
180 $contextchars = intval( $contextchars * $scale );
182 $patPre =
"(^|{$searchHighlightBoundaries})";
183 $patPost =
"({$searchHighlightBoundaries}|$)";
185 $pat1 =
"/(" . $phrase .
")/ui";
186 $pat2 =
"/$patPre(" . $anyterm .
")$patPost/ui";
188 $left = $contextlines;
196 foreach ( $textExt as $index => $line ) {
197 if ( strlen( $line ) > 0 && $line[0] !=
';' && $line[0] !=
':' ) {
198 $firstText = $this->extract( $line, 0, $contextchars * $contextlines );
206 foreach ( $terms as $term ) {
207 if ( !preg_match(
"/$patPre" . $term .
"$patPost/ui", $firstText ) ) {
213 $snippets[$first] = $firstText;
214 $offsets[$first] = 0;
219 $this->process( $pat1, $textExt, $left, $contextchars, $snippets, $offsets );
221 $this->process( $pat1, $otherExt, $left, $contextchars, $snippets, $offsets );
223 $this->process( $pat2, $textExt, $left, $contextchars, $snippets, $offsets );
225 $this->process( $pat2, $otherExt, $left, $contextchars, $snippets, $offsets );
232 if ( count( $snippets ) == 0 ) {
234 if ( array_key_exists( $first, $all ) ) {
235 $targetchars = $contextchars * $contextlines;
236 $snippets[$first] =
'';
237 $offsets[$first] = 0;
241 if ( array_key_exists( $first, $snippets ) && preg_match( $pat1, $snippets[$first] )
242 && $offsets[$first] < $contextchars * 2 ) {
243 $snippets = [ $first => $snippets[$first] ];
247 $targetchars = intval( ( $contextchars * $contextlines ) / count( $snippets ) );
250 foreach ( $snippets as $index => $line ) {
251 $extended[$index] = $line;
252 $len = strlen( $line );
255 if ( $len < $targetchars - 20 ) {
257 if ( $len < strlen( $all[$index] ) ) {
258 $extended[$index] = $this->extract(
263 $offsets[$index] + $targetchars,
266 $len = strlen( $extended[$index] );
273 while ( $len < $targetchars - 20
274 && array_key_exists( $add, $all )
275 && !array_key_exists( $add, $snippets ) ) {
279 $tt =
"\n" . $this->extract( $all[$add], 0, $targetchars - $len, $offsets[$add] );
280 $extended[$add] = $tt;
281 $len += strlen( $tt );
288 $snippets = $extended;
291 foreach ( $snippets as $index => $line ) {
294 } elseif ( $last + 1 == $index
295 && $offsets[$last] + strlen( $snippets[$last] ) >= strlen( $all[$last] )
297 $extract .=
" " . $line;
299 $extract .=
'<b> ... </b>' . $line;
305 $extract .=
'<b> ... </b>';
309 foreach ( $terms as $term ) {
310 if ( !isset( $processed[$term] ) ) {
311 $pat3 =
"/$patPre(" . $term .
")$patPost/ui";
312 $extract = preg_replace( $pat3,
313 "\\1<span class='searchmatch'>\\2</span>\\3", $extract );
314 $processed[$term] =
true;