_host = $host;
$this->_port = $port;
$this->_index = $index;
$this->_sphinx = new SphinxClient();
$this->_sphinx->SetServer($this->_host, $this->_port);
$this->_sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
}
public function setSnippetSize(int $size): void { if ($size > 0) $this->_snippetSize = $size; }
public function setIndexTags(string $tags): void { }
public function search(int $start, int $resultsPerPage = 10): bool
{
$this->_resultsPerPage = $resultsPerPage;
$this->_sphinx->SetFieldWeights([
'namespace' => $this->_namespacePriority,
'pagename' => $this->_pagenamePriority,
'title' => $this->_titlePriority,
'body' => $this->_bodyPriority
]);
$this->_sphinx->SetLimits($start, 1000);
$res = $this->_sphinx->Query($this->_query, $this->_index);
$this->_result = $res ?: [];
return !empty($this->_result['matches']);
}
/**
* Optimized: Returns raw page metadata instantly without parsing any text snippets
*/
public function getPages(string $keywords): array|false
{
if (empty($this->_result['matches'])) return false;
$ids = $this->getPagesIds();
$results = [];
foreach ($ids as $id => $data) {
$this->_offset++;
if (auth_quickaclcheck($data['page']) >= AUTH_READ) {
$results[$id] = [
'page' => $data['page'],
'titleTextExcerpt' => !empty($data['title_text']) ? $data['title_text'] : $data['page'],
'hid' => $data['hid'] ?? '',
'bodyExcerpt' => '' // Left empty intentionally for Phase 2 lazy-loading
];
}
}
return $results;
}
/**
* Lazy Loader: Public access point to generate a single rich media snippet on demand
*/
public function generateSingleSnippet(string $pageId, string $query): string
{
$rawText = rawWiki($pageId);
if (empty($rawText)) return '';
$words = preg_split('/\s+/', $query, -1, PREG_SPLIT_NO_EMPTY);
$highlightTargets = [];
foreach ($words as $word) {
$highlightTargets[] = $word;
$collapsed = preg_replace('/(.)\1/u', '$1', $word);
if ($collapsed !== $word) {
$highlightTargets[] = $collapsed;
}
}
$highlightTargets = array_unique($highlightTargets);
$pos = false;
foreach ($highlightTargets as $word) {
if (($pos = mb_stripos($rawText, $word)) !== false) {
break;
}
}
if ($pos !== false) {
$start = max(0, $pos - 150);
$windowLength = 400;
$snippetWiki = mb_substr($rawText, $start, $windowLength);
$openBraces = mb_substr_count($snippetWiki, '{{');
$closeBraces = mb_substr_count($snippetWiki, '}}');
if ($openBraces > $closeBraces) {
$tail = mb_substr($rawText, $start + $windowLength, 120);
if (($bracePos = mb_strpos($tail, '}}')) !== false) {
$snippetWiki .= mb_substr($tail, 0, $bracePos + 2);
}
}
$openCode = mb_substr_count($snippetWiki, '');
if ($openCode > $closeCode) {
$tail = mb_substr($rawText, $start + $windowLength, 500);
if (($codePos = mb_strpos($tail, '')) !== false) {
$snippetWiki .= mb_substr($tail, 0, $codePos + 7);
}
}
$info = [];
$rendered = '... ' . p_render('xhtml', p_get_instructions($snippetWiki), $info) . ' ...';
} else {
$snippetWiki = mb_substr($rawText, 0, 300);
$info = [];
$rendered = '... ' . p_render('xhtml', p_get_instructions($snippetWiki), $info) . ' ...';
}
$rendered = preg_replace('/^(\s*
\s*\.\.\.)/i', '...', $rendered); foreach ($highlightTargets as $word) { if (mb_strlen($word) < 2) continue; $q = preg_quote(hsc($word), '/'); $rendered = preg_replace("/(?![^<]*>)$q/iu", '$0', $rendered); } return $rendered; } public function getExcerpt(array $data, string $query): array { $words = preg_split('/\s+/', $this->removeStars($query), -1, PREG_SPLIT_NO_EMPTY); $highlightTargets = []; foreach ($words as $word) { $highlightTargets[] = $word; $collapsed = preg_replace('/(.)\1/u', '$1', $word); if ($collapsed !== $word) { $highlightTargets[] = $collapsed; } } $highlightTargets = array_unique($highlightTargets); $res = []; foreach ($data as $text) { $out = hsc($text); foreach ($highlightTargets as $word) { if (mb_strlen($word) < 2) continue; $q = preg_quote(hsc($word), '/'); $out = preg_replace("/($q)/iu", '$1', $out); } $res[] = $out; } return $res; } public function getPagesIds(): array { return (new PageMapper())->getByCrc(array_keys($this->_result['matches'] ?? [])); } public function removeStars(string $query): string { return trim(str_replace('*', '', $query)); } public function starQuery(string $query): string { $words = preg_split('/\s+/', $this->removeStars($query), -1, PREG_SPLIT_NO_EMPTY); $starred = []; foreach ($words as $w) $starred[] = (str_starts_with($w, '-') || mb_strlen($w) < 3 || str_contains($w, '"')) ? $w : "*$w*"; return implode(" ", $starred); } public function getOffset(): int { return $this->_offset; } public function getError(): string { return $this->_sphinx->GetLastError(); } public function getTotalFound(): int { return (int)($this->_result['total_found'] ?? 0); } public function setNamespacePriority(int $p): void { $this->_namespacePriority = $p; } public function setPagenamePriority(int $p): void { $this->_pagenamePriority = $p; } public function setSearchOnlyPagename(): void { $this->_titlePriority = 0; $this->_bodyPriority = 0; } public function setTitlePriority(int $p): void { $this->_titlePriority = $p; } public function setBodyPriority(int $p): void { $this->_bodyPriority = $p; } public function setSearchAllQuery(string $k, string $c): void { $this->_query = $k; } public function setSearchAllQueryWithCategoryFilter(string $k, string $c): void { $cat = $this->_sphinx->EscapeString($c); $f = str_starts_with($c, "-") ? '-"' . substr($cat, 1) . '"' : '"' . $cat . '"'; $this->_query = "(@(namespace,pagename) $f) & ($k)"; } }