PHP中获取完整URL:快速且无错误的方法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

在PHP中获取完整URL:如何避免重复造轮子且不损失性能

\n\n

每个PHP开发者迟早都会遇到这样一个问题:“如何获取当前页面的完整URL?”看起来很简单——取$_SERVER['REQUEST_URI'],加上域名就完成了。但魔鬼总是藏在细节中。关于“在PHP中获取完整URL”的问题在Stack Overflow上获得了数千次浏览和几十个回答。这并非偶然。

\n\n

在本文中,我们将探讨如何在PHP中正确、快速且无错误地获取完整URL。您将了解HTTP_HOST与SERVER_NAME的区别,如何正确判断协议(HTTPS或HTTP),以及为什么不应盲目复制网上的代码。开始吧!

\n\n

为什么简单的方法并不总是最好的?

\n\n

最显而易见的写法如下:

\n\n
<?php\n$fullUrl = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];\n?>
\n\n

这段代码能工作,但前提是您满足以下条件:

\n
    \n
  • 必须使用HTTP(而非HTTPS)
  • \n
  • 没有使用80或443以外的端口
  • \n
  • 服务器正确传递了Host头
  • \n
\n\n

在现实世界中,这些条件往往不成立。如果网站通过HTTPS运行,您将得到以http://开头的URL,这会导致浏览器中的混合内容错误。如果使用了非标准端口(例如8080),端口信息会丢失。而如果攻击者伪造了Host头,您可能得到指向其他域名的URL。

\n\n

完整URL的组成部分:详细解析

\n\n

在编写通用解决方案之前,让我们回顾一下完整URL的组成:

\n\n
scheme://host:port/path?query#fragment
\n\n

在PHP中,我们可以从超全局数组$_SERVER中获取以下元素:

\n
    \n
  • scheme — 协议(HTTP或HTTPS)。没有直接的键,但可以通过$_SERVER['HTTPS']或$_SERVER['REQUEST_SCHEME'](自PHP 7.4起可用)判断。
  • \n
  • host — 域名。有两个候选:HTTP_HOST(来自Host头)和SERVER_NAME(来自服务器配置)。
  • \n
  • port — 端口。$_SERVER['SERVER_PORT']。如果端口是标准端口(HTTP为80,HTTPS为443),可以省略。
  • \n
  • path — 路径。$_SERVER['REQUEST_URI']包含路径和查询字符串。
  • \n
  • query — 查询字符串。可以通过$_SERVER['QUERY_STRING']单独获取。
  • \n
  • fragment — 锚点。它不会发送到服务器,因此无法获取。
  • \n
\n\n

正确的解决方案:getFullUrl()函数

\n\n

以下是一种可靠且高性能的在PHP中获取完整URL的方法:

\n\n
<?php\nfunction getFullUrl(): string {\n    // 判断协议\n    $scheme = 'http';\n    if (\n        (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === '1'))\n        || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')\n    ) {\n        $scheme = 'https';\n    }\n\n    // 主机:优先使用HTTP_HOST,但检查是否为空\n    $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';\n\n    // 端口:仅当非标准端口时添加\n    $port = (int) ($_SERVER['SERVER_PORT'] ?? 80);\n    $portStr = '';\n    if (\n        ($scheme === 'http' && $port !== 80) ||\n        ($scheme === 'https' && $port !== 443)\n    ) {\n        $portStr = ':' . $port;\n    }\n\n    // URI\n    $uri = $_SERVER['REQUEST_URI'] ?? '/';\n\n    return $scheme . '://' . $host . $portStr . $uri;\n}\n?>
\n\n

这个函数考虑了:

\n
    \n
  • HTTPS协议(包括通过带有X-Forwarded-Proto头的代理服务器)。
  • \n
  • 非标准端口。
  • \

博客

书籍推荐