117 lines
3.9 KiB
PHP
117 lines
3.9 KiB
PHP
<?php
|
|||
|
|
/**
|
||
|
|
* 简航 SimpleNav —— 搜索联想代理
|
||
|
|
* 浏览器直接调用各搜索引擎的联想接口会有跨域/JSONP 限制,这里由服务端代理并统一输出 JSON。
|
||
|
|
* 用法:suggest.php?slug=bing&q=关键词
|
||
|
|
*/
|
||
|
|
declare(strict_types=1);
|
||
|
|
require_once __DIR__ . '/functions.php';
|
||
|
|
|
||
|
|
header('Content-Type: application/json; charset=utf-8');
|
||
|
|
header('Cache-Control: no-store');
|
||
|
|
|
||
|
|
$slug = isset($_GET['slug']) ? preg_replace('/[^a-z0-9\-_]/i', '', (string)$_GET['slug']) : '';
|
||
|
|
$q = isset($_GET['q']) ? trim((string)$_GET['q']) : '';
|
||
|
|
|
||
|
|
if ($q === '') {
|
||
|
|
echo json_encode(['q' => '', 'items' => []]);
|
||
|
|
exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
$items = [];
|
||
|
|
try {
|
||
|
|
$st = db()->prepare('SELECT * FROM engines WHERE slug=? AND is_active=1 LIMIT 1');
|
||
|
|
$st->execute([$slug]);
|
||
|
|
$engine = $st->fetch();
|
||
|
|
if ($engine && $engine['suggest_url'] !== '') {
|
||
|
|
$u = str_replace('{q}', rawurlencode($q), $engine['suggest_url']);
|
||
|
|
$resp = http_get($u, 100000, 5);
|
||
|
|
if ($resp !== null) {
|
||
|
|
$items = parse_suggest($engine['suggest_type'], ensure_utf8($resp['body']), $q);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} catch (Throwable $e) {
|
||
|
|
$items = [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 去重、限长、裁剪
|
||
|
|
$items = array_values(array_unique(array_filter(array_map('trim', $items))));
|
||
|
|
$items = array_slice($items, 0, 10);
|
||
|
|
|
||
|
|
echo json_encode(['q' => $q, 'items' => $items], JSON_UNESCAPED_UNICODE);
|
||
|
|
exit;
|
||
|
|
|
||
|
|
/** 按引擎类型解析联想结果 */
|
||
|
|
function parse_suggest(string $type, string $body, string $q): array
|
||
|
|
{
|
||
|
|
$body = trim($body);
|
||
|
|
$out = [];
|
||
|
|
|
||
|
|
switch ($type) {
|
||
|
|
case 'bing_osjson':
|
||
|
|
case 'google_firefox':
|
||
|
|
// ["query", ["s1","s2",...]]
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j) && isset($j[1]) && is_array($j[1])) {
|
||
|
|
$out = $j[1];
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'baidu_sugrec':
|
||
|
|
// {"q":"x","g":[{"type":"..","q":".."}], ...}
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j)) {
|
||
|
|
if (!empty($j['g']) && is_array($j['g'])) {
|
||
|
|
foreach ($j['g'] as $it) { if (isset($it['q'])) $out[] = $it['q']; }
|
||
|
|
} elseif (!empty($j['s']) && is_array($j['s'])) {
|
||
|
|
$out = $j['s'];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'so360':
|
||
|
|
// {"s":[".."]} 或 {"result":[{"word":".."}]}
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j)) {
|
||
|
|
if (!empty($j['s']) && is_array($j['s'])) { $out = $j['s']; }
|
||
|
|
elseif (!empty($j['result']) && is_array($j['result'])) {
|
||
|
|
foreach ($j['result'] as $it) { if (isset($it['word'])) $out[] = $it['word']; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'sogou_ajaj':
|
||
|
|
// {"query":"x","items":["a","b"]}(可能是 GBK,已在上面转码)
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j) && !empty($j['items']) && is_array($j['items'])) {
|
||
|
|
$out = $j['items'];
|
||
|
|
} else {
|
||
|
|
// 兜底:正则抓引号字符串
|
||
|
|
if (preg_match_all('#"([^"]{1,40})"#u', $body, $m)) {
|
||
|
|
$out = array_slice($m[1], 1); // 跳过 query 自身
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'ddg':
|
||
|
|
// [{"phrase":".."},...]
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j)) {
|
||
|
|
foreach ($j as $it) {
|
||
|
|
if (is_array($it) && isset($it['phrase'])) { $out[] = $it['phrase']; }
|
||
|
|
elseif (is_string($it)) { $out[] = $it; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
// 通用兜底:尝试 JSON,再退化正则
|
||
|
|
$j = json_decode($body, true);
|
||
|
|
if (is_array($j)) {
|
||
|
|
if (isset($j[1]) && is_array($j[1])) { $out = $j[1]; }
|
||
|
|
elseif (!empty($j['s']) && is_array($j['s'])) { $out = $j['s']; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return $out;
|
||
|
|
}
|