PHP 检测字符串是否包含外部链接的方法

巷口听风
巷口听风
发布时间:2026-10-13 | 5人浏览过
原创

在 Web 开发中,检测用户输入或文章内容是否包含指向其他域名的“外链”是常见的安全需求,常用于防止钓鱼攻击、引流跳转或垃圾信息注入。本文将展示如何使用 PHP 的正则表达式功能 `preg_match_all` 来高效识别字符串中的外部链接,并提供一个完整的函数实现。

核心逻辑与目标

检测外链的核心在于两个步骤:首先是 提取,即从混杂的文本中找出所有符合 URL 格式的字符串;其次是 校验,即对比提取出的 URL 域名是否与当前网站(本站)域名一致。如果存在不一致的域名,则该字符串包含外链。

准备条件

  1. PHP 环境:确保服务器已启用 PHP 并支持 `preg_*` 系列正则函数。
  2. 获取当前域名:通常使用超级全局变量 $_SERVER['HTTP_HOST'] 获取当前请求的主机名。

具体操作步骤

第1步:构建正则匹配规则

我们需要一个能够匹配常见 HTTP 和 HTTPS 协议的通用正则表达式。该表达式应能捕获协议头以及后续的域名部分。

$reg = '/http(?:s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}/';
  • http(?:s?)?:匹配 http 或 https。
  • \/\/ :匹配双斜杠。
  • (?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}:匹配域名主体及顶级域名(如 .com, .cn 等)。

第2步:提取所有匹配的 URL

使用 preg_match_all 函数在目标字符串中进行全局搜索,将所有匹配到的结果存入数组。我们将重点分析 $data[0](完整匹配串)或根据分组提取的子串。

preg_match_all($reg, $text, $data);

注意:原文代码中使用了 $data[1],但在上述正则中并未定义捕获组(括号),因此 $data[0] 才是完整匹配的 URL 列表。若需精确到域名,建议修改正则为带捕获组的格式,例如:'/http(?:s?):\/\/((?:[A-za-z0-9-]+\.)+[A-za-z]{2,4})/',此时 $data[1] 即为域名部分。

第3步:遍历比对域名

定义一个函数 has_external_link,接收待检测字符串和可选的目标域名参数。逻辑如下:

  1. 默认主机设置:若未传入域名参数,默认使用 $_SERVER['HTTP_HOST'] 作为本站域名。
  2. 空值检查:若字符串为空或无匹配项,直接返回不包含外链(true)。
  3. 逐个比对:遍历提取出的每一个域名,若发现任一域名不等于本站域名,立即返回 false(表示有外链)。
  4. 安全结论:若遍历结束未发现不同域名,则返回 true(表示无外链)。

第4步:完整代码实现

结合上述逻辑,优化后的健壮代码如下:

/**
 * 检测字符串是否包含外部链接
 *
 * @param string $text 待检测的文本内容
 * @param string $current_host 当前站点域名,默认为 $_SERVER['HTTP_HOST']
 * @return bool true 表示仅包含内部链接或无链接;false 表示包含外部链接
 */
function has_no_external_link($text = '', $current_host = '') {
    // 如果没有提供主机名,尝试从服务器环境变量获取
    if (empty($current_host)) {
        $current_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
    }
    
    // 如果无法确定主机名,且需要严格检测,可能需调整策略
    // 此处假设只要能获取 host 就进行比对
    if (empty($current_host)) {
        return false; // 保守起见,无法确认时视为有风险或需特殊处理
    }

    // 优化后的正则:明确捕获域名部分,方便后续比对
    // 匹配 http:// 或 https:// 后跟随的域名
    $pattern = '/https?:\/\/([a-zA-Z0-9\-]+\.)*[a-zA-Z]{2,}/';
    
    preg_match_all($pattern, $text, $matches);
    
    if (empty($matches[0])) {
        return true; // 没有 URL,当然没有外链
    }

    foreach ($matches[0] as $url) {
        // 从完整 URL 中提取域名部分进行比对
        // 简单做法:移除协议头,截取第一个斜杠前的部分
        $parsed_url = parse_url($url);
        if ($parsed_url && isset($parsed_url['host'])) {
            $host_in_text = $parsed_url['host'];
            // 去除 www. 前缀以便更宽松地匹配(可选)
            $clean_current_host = preg_replace('/^www\./', '', $current_host);
            $clean_text_host = preg_replace('/^www\./', '', $host_in_text);
            
            if ($clean_text_host !== $clean_current_host) {
                return false; // 发现外链
            }
        }
    }
    
    return true; // 所有链接均为内部链接
}

// 示例用法
$text_content = "欢迎访问本站 http://example.com 或者查看外部资源 https://github.com";
$my_domain = "example.com";

if (has_no_external_link($text_content, $my_domain)) {
    echo "内容为纯内部链接。";
} else {
    echo "检测到外部链接!";
}

完成判断与维护

当函数返回 true 时,表示提供的字符串中不存在指向其他域名的链接,可以用于通过审核、保存数据等操作。若返回 false,则需根据业务逻辑决定是拦截、标记还是替换为相对路径。

注意事项:

  • 协议兼容性:现代网站多使用 HTTPS,正则应涵盖 https:。
  • 子域名处理:如需区分主域名和子域名(如 a.example.com vs b.example.com),应在比对逻辑中进一步细化域名解析规则。
  • 性能考量:对于超长文本,建议使用 pcre.jit=1 提升 PCRE JIT 性能,或使用更轻量级的字符串查找替代复杂正则。

以上就是PHP检查字符串中是否有外链的详细内容,更多关于PHP正则表达式应用与内容安全的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表