手工修改:文章页外链改为新窗口打开(兼容 LinkGo 外链转内链)
只改 2 个文件:usr/themes/robes/functions.php、usr/themes/robes/post.php
纯 PHP 改动,不用清缓存。改之前先把这两个文件各复制一份.bak。
一、先搞清楚网上的一些方法为什么没生效
装了 LinkGo(外链转内链),它会把文章里的外链改写掉:
| 地址 | |
|---|---|
| 正文里原本写的 | https://www.jb51.net/article/285123.htm |
| LinkGo 改写后(页面真实源码) | https://www.loveq.cc/go/aHR0cHM6Ly93d3cuamI1MS5uZXQvYXJ0aWNsZS8yODUxMjMuaHRt |
那串 aHR0cHM6... 就是原地址 base64 编码后的结果(+→-、/→_,还去掉了结尾的 =)。
所以链接的域名变成了自己的站(www.loveq.cc),网上教程的逻辑是「域名跟本站不一样才算外链」——一看域名是本站,就当站内链接原样放过了,自然没有 target="_blank"。
修正思路:遇到这种 本站/go/xxx 的跳转链接,先解码还原出真实地址,再拿真实域名判断是不是外链。
第 1 步:functions.php 新增两个函数
搜 function themeOption,会看到:
function themeOption($key, $default = '') {
$val = \Helper::options()->{$key};
return ($val !== null && $val !== '') ? $val : $default;
}在它的 } 后面空一行,粘贴这一整段:
// ══════════════════════════════════════
// 外链自动新窗口打开(仅文章页调用)
// ══════════════════════════════════════
/**
* 识别「外链转内链」插件(如 LinkGo)生成的跳转地址,返回解码后的原始地址
*
* LinkGo 会把 https://其它站/xxx 改写成 本站/go/<base64url>,
* 其中 base64url = base64 编码后把 "+"→"-"、"/"→"_" 并去掉尾部 "="。
* 这类链接的域名是本站,光看域名会被误判成站内链接,所以要解出真实地址。
*
* @param string $href 形如 https://www.loveq.cc/go/aHR0cHM6Ly9...
* @return string 解码成功且是 http(s) 地址时返回原始地址,否则返回空串
*/
function robesResolveGoTarget($href) {
if (strpos($href, '/go/') === false) {
return '';
}
$path = (string) parse_url($href, PHP_URL_PATH);
if ($path === '' || !preg_match('#(?:^|/)go/([A-Za-z0-9_\-=+/]+)$#', $path, $m)) {
return '';
}
$token = strtr($m[1], '-_', '+/');
$pad = strlen($token) % 4;
if ($pad !== 0) {
$token .= str_repeat('=', 4 - $pad);
}
$decoded = base64_decode($token, true);
if ($decoded === false || !preg_match('#^https?://#i', $decoded)) {
return '';
}
return $decoded;
}
/**
* 给正文中的外部链接自动补上 target="_blank"
*
* 规则:链接主机名与本站主机名(含 www / 裸域名变体)不同,即视为外链,
* 加上 target="_blank" 与 rel="noopener noreferrer";
* 站内链接、锚点、相对地址以及 mailto: / tel: 等非 http(s) 链接一律保持原样,
* 以免破坏主题的 PJAX 无刷新跳转。
*
* 兼容「外链转内链」插件:形如 本站/go/<base64url> 的跳转地址会先解码成
* 真实地址,再按真实域名判断是不是外链。
*
* 目前只由 post.php(文章详情页)调用,其他页面不受影响。
*
* @param string $content 已经过 Markdown 解析的 HTML
* @return string
*/
function robesExternalBlank($content) {
if (!is_string($content) || $content === '' || stripos($content, '<a') === false) {
return $content;
}
// 本站所有可能的主机名(本站域名 + 当前访问域名 + www 变体)
static $hosts = null;
if ($hosts === null) {
$hosts = array();
$urls = array();
try {
if (class_exists('\Helper') && method_exists('\Helper', 'options')) {
$urls[] = (string) \Helper::options()->siteUrl;
}
} catch (\Throwable $e) {
// 忽略:下面的 HTTP_HOST 兜底
}
if (!empty($_SERVER['HTTP_HOST'])) {
$urls[] = 'http://' . $_SERVER['HTTP_HOST'];
}
foreach ($urls as $url) {
$host = strtolower((string) parse_url($url, PHP_URL_HOST));
if ($host === '') {
continue;
}
$bare = preg_replace('/^www\./', '', $host);
$hosts[] = $host;
$hosts[] = $bare;
$hosts[] = 'www.' . $bare;
}
$hosts = array_values(array_unique($hosts));
}
return preg_replace_callback('/<a\s[^>]*>/i', function ($match) use ($hosts) {
$tag = $match[0];
if (!preg_match('/\bhref\s*=\s*(["\'])(.*?)\1/is', $tag, $hm)) {
return $tag;
}
$href = trim(html_entity_decode($hm[2], ENT_QUOTES, 'UTF-8'));
// 只处理 http(s) 绝对地址;
// 空链接、锚点、相对地址、协议相对地址,以及 mailto:/tel:/javascript: 等一律跳过
if ($href === '' || !preg_match('#^https?://#i', $href)) {
return $tag;
}
$linkHost = strtolower((string) parse_url($href, PHP_URL_HOST));
// 解析不出主机名,跳过
if ($linkHost === '') {
return $tag;
}
if (in_array($linkHost, $hosts, true)) {
// 域名是本站:可能是「外链转内链」插件的跳转地址,解出真实地址再判断
$realUrl = robesResolveGoTarget($href);
if ($realUrl === '') {
return $tag; // 真正的站内链接,原样放过(PJAX 无刷新靠这个)
}
$realHost = strtolower((string) parse_url($realUrl, PHP_URL_HOST));
if ($realHost === '' || in_array($realHost, $hosts, true)) {
return $tag; // 跳转地址指向本站,同样放过
}
// 跳转地址指向外站 → 继续往下,按外链处理
}
// 外链:先剥掉原有的 target / rel,再统一补上,保证重复执行结果一致
$tag = preg_replace('/\s+target\s*=\s*(["\']).*?\1/is', '', $tag);
$rel = 'noopener noreferrer';
if (preg_match('/\s+rel\s*=\s*(["\'])(.*?)\1/is', $tag, $rm)) {
$old = trim($rm[2]);
if ($old !== '') {
$rel = $old . ' ' . $rel;
}
$tag = preg_replace('/\s+rel\s*=\s*(["\']).*?\1/is', '', $tag, 1);
}
$rel = implode(' ', array_unique(preg_split('/\s+/', trim($rel))));
return rtrim(substr($tag, 0, -1)) . ' target="_blank" rel="' . $rel . '">';
}, $content);
}第 2 步:post.php 换一行
搜 themePhotoFilter::processShortcodes(这个文件里只有 1 处)。
找到:
<?php ob_start(); $this->content(); $c = ob_get_clean(); echo themePhotoFilter::processShortcodes($c); ?>换成:
<?php /* 正文输出:外链自动新窗口打开 */ ?>
<?php ob_start(); $this->content(); $c = ob_get_clean(); echo robesExternalBlank(themePhotoFilter::processShortcodes($c)); ?>(第二行前面保留原来的 24 个空格缩进。page.php 里有一模一样的一行,别改错文件。)
跳到「四、改完怎么验证」。
二、改完怎么验证
- 打开一篇带外链的文章,Ctrl+F5 刷新;
- 右键查看网页源代码,搜
target="_blank"—— 外链那个<a>上应该有了; - 点外链:新标签页打开 LinkGo 的中间跳转页,中间页再自动跳到真实外链;原文章页不动;
- 点站内链接(分类、上下篇、文章里的站内链接):仍然是本页无刷新跳转,不会开新窗口。
排错
| 现象 | 原因 |
|---|---|
| 白屏 / 500 | 粘贴时漏了字符(最常见是少一个 }),用 .bak 还原重来 |
| 外链还是没有 target | functions.php 里搜 robesResolveGoTarget:搜不到说明补丁 A 没生效;搜得到就清一下 CDN/缓存再试(宝塔若有「静态缓存」插件,清一次缓存) |
三、这段代码在干什么(一句话)
正文输出前逐个检查 <a>:
本站/go/xxx→ 先按 base64 解码还原出真实地址,再判断:真实域名是外站就加target="_blank" rel="noopener noreferrer",指向本站就放过;- 域名本来就不是本站(比如没被 LinkGo 接管的裸外链)→ 同样加
target="_blank"; - 站内链接、锚点、相对地址、
mailto:、tel:→ 一个字都不动,所以站内跳转和 PJAX 无刷新完全不受影响。
暂无评论
还没有评论,来说点什么吧