PHP_SELF变量解析和重复路径解决

栏目: PHP · 发布时间: 6年前

内容简介:最近升级之前的文章

最近升级 PHPPHP7 版本,并重新部署了新的 Nginx ,启动的时候发现了一个问题,全局变量 $_SERVER['PHP_SELF'] 的值发生了改变,从而影响到代码的功能。因此我们来了解下 $_SERVER 全局变量中的 PHP_SELF/PATH_INFO/SCRIPT_NAME 等参数以及其关系。

CGI 1.1 规范

之前的文章 [ php-fpm进程数管理 ] 已经简单说过CGI的内容,这里我们再详细讲一下。

CGICommon Gateway Interface(通用网管协议) ,用于让交互程序和Web服务器通信的协议。它负责处理URL的请求,启动一个进程,将客户端发送的数据作为输入,由Web服务器收集程序的输出并加上合适的头部,再发送回客户端。

FastCGI 是基于 CGI 的增强版本的协议,不同于创建新的进程来服务请求,使用持续的进程和创建的子进程来处理一连串的进程,这些进程由FastCGI服务器管理,开销更小,效率更高。

CGI 诞生于 1993 年美国国家计算机中心,目的是为不同的动态页面处理语言 (php/python/java) 在不同的服务器下 (apache/nginx) 提供一致的接口规范,提供会话环境变量、会话客户端等信息。

RFC-CGI1.1 文档中包含了协议的全部内容,我们现在只关注它的 4.1节:Request Meta-Variables

标准中定义了处理请求应该实现的 17 个属性和如何自定义新属性,比如:

  • SERVER_PROTOCOL :信息协议的名字和修订版。格式为 protocol/reVision
  • SERVER_PORT :发送请求的端口号。
  • REQUEST_METHOD :请求的方法。对于 HTTP ,有 "GET"、 "HEAD"、 "POST" 等等。
  • PATH_INFO :额外的路径信息,由客户端给出的。换句话说,脚本可以由他们的虚拟路径名来访问,在这个路径的末尾附带额外的信息。这个额外信息被作为 PATH_INFO 发送。这个信息如果在传递给 CGI 脚本之前来自 URL 就可以由服务器来解码。
  • PATH_TRANSLATED :服务器提供了一个 PATH_INFO 的转换版本,它需要路径并且为它做虚拟到物理的映射。
  • SCRIPT_NAME :将要执行的脚本的一个虚拟路径。
  • QUERY_STRING :在引用脚本的 URL 中紧跟在 之后的信息。这是一个查询信息。它不能以任何方式来解码。这个变量总是可以在有查询信息的时候被设置,而不管命令行解码。
  • REMOTE_HOST :产生请求的主机名。如果服务器没有这个信息,它应该设置 REMOTE_ADDR 并且让这个为未设置状态。
  • REMOTE_ADDR :产生请求的远程主机的 IP 地址。
  • AUTH_TYPE :如果服务器支持用户验证,脚本就受保护。这是一个协议规范授权方法,用于验证用户。
  • REMOTE_USER :如果服务器支持用户验证,脚本就受保护。这是他们授权的用户名。
  • REMOTE_IDENT :如果 HTTP 服务器支持 RFC931 认证,这个变量将被设置为从服务器取出的远程用户名。这个变量的用法应该只限制在登陆的时候。
  • CONTENT_TYPE :对于哪些已经附上信息的请求,比如 HTTP POSTPUT ,这是数据的内容类型。
  • CONTENT_LENGTH :客户端给的数据内容的长度。

这些变量需要各个语言和服务器进行自己的实现,同时他们也会有自己定义的一些变量。如我们今天要说的 PHP 语言中的 $_SERVER['PHP_SELF'] 变量。

PHP 的超全局变量 $_SERVER

$_SERVER 是一个包含了诸如 头信息(header)、路径(path)、以及脚本位置(script locations) 等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。这也就意味着大量的此类变量都会在 » CGI 1.1 规范中说明,所以应该仔细研究一下。

__FILE__ 常量包含当前(例如包含)文件的完整路径和文件名。

与此相关的,我们这里主要关注的几个变量是:

  • PHP_SELF : 当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/foo/bar.php 的脚本中值为 /foo/bar.php
  • SCRIPT_NAME : 包含当前脚本的路径。这在页面需要指向自己时非常有用。
  • PATH_INFO : 包含由客户端提供的、跟在真实脚本名称之后并且在查询语句 (query string) 之前的路径信息,如果存在的话。例如,如果当前脚本是通过 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar 被访问,那么值为 /some/stuff

文档里表述的 Web 服务器,在我的环境里指代的是 Nginx 。在 Apache 中,当不加配置的时候对于 PHP 脚本, AcceptPathInfo 是默认接受的。而对于 Nginx 下, 是不支持 PATH INFO 的, 也就是它不会默认设置 PATH_INFO .

因此,对于一个 Nginx 架构的常规请求来说,这几个字段的值分别是:

# http://www.baidu.com:8080/odp/index.php?r=update
PHP_SELF: /odp/index.php
SCRIPT_NAME: /odp/index.php
PATH_INFO: null

问题: PHP_SELF 中出现重复路径

在我部署完成新的 Nginx 服务后,得到的上面三个字段的值为:

# http://www.baidu.com:8080/odp/index.php?r=update
PHP_SELF: /odp/index.php/odp/index.php
SCRIPT_NAME: /odp/index.php
PATH_INFO: /odp/index.php

注意这里的 PHP_SELF 字段存在重复的路径,而 PATH_INFO 也存在了值,此时的 nginx.conf 配置为:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

# 注意这一行,我们配置了PATH_INFO字段
fastcgi_param  PATH_INFO          $fastcgi_script_name;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

那么我们为什么配置了 PATH_INFO 就会影响 PHP_SELF 的值了呢?这一点,我们首先会想到 PHP_SELF 这个自定义属性的来源是什么,然而,我并没有找到任何的文档说明。但我们可以通过重命名的方式,来探究一下它的定义:

fastcgi_param  PATH_INFO          PATH_INFO;
# fastcgi_param  PATH_INFO          $fastcgi_script_name;

fastcgi_param  SCRIPT_NAME        SCRIPT_NAME;
# fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;

变更这两行,我们将其重命名为指定字符串,而不是请求传入的变量, nginx reload 后,此时的结果是:

# http://www.baidu.com:8080/odp/index.php?r=update
PHP_SELF: SCRIPT_NAMEPATH_INFO
SCRIPT_NAME: SCRIPT_NAME
PATH_INFO: PATH_INFO

而其他变量均正常,因此我们可以进一步理解:

PHP_SELF = SCRIPT_NAME + PATH_INFO

自定义变量: PHP_SELF

那么 PHP 为什么要自定义这个属性呢?在官方文档里有这么一个 url 请求,此时:

# http://www.example.com/php/path_info.php/some/stuff?foo=bar 
PHP_SELF: /php/path_info.php/some/stuff
SCRIPT_NAME: /php/path_info.php
PATH_INFO: /some/stuff

所以,在这种场景下,只有 PHP_SELF 才能拿到完整的当前执行脚本的文件或路径。

总结

为了不同服务器、不同语言之间的请求通信,于是有了 CGI 协议规范,这个规范在不同的服务器和语言中有自己的实现,在 Web Server: Nginx 的配置文件中,可以设置不同变量的值,解析后传递给 PHP-FPM(PHP-FastCGI Process Manager) ,再进一步传递给负责响应请求的 PHP 子进程,而 PHP 中也定义了关于请求通信的全局变量 $_SERVER ,用于解析请求和处理逻辑。这就是整个关于解析请求信息的流程。

由于 PHP$_SERVER 中的这几个变量的定义有一定混淆,也依赖于不同的实现和 Server 环境,如 PATH_INFONginx/Apache 中的不同默认状态,因此,如果需要页面指向自己时,除非如上面示例中的那种 url ,建议使用 SCRIPT_NAME 变量即可。

参考资料

  1. segmentfault-php-fpm进程数管理: https://segmentfault.com/a/11...
  2. RFC-CGI1.1: https://tools.ietf.org/html/r...
  3. CGI规范及其历史: http://www.voidcn.com/article...
  4. php关于$_SERVER中一些和环境有关的参数详解: https://www.jianshu.com/p/fea...

以上所述就是小编给大家介绍的《PHP_SELF变量解析和重复路径解决》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hit Refresh

Hit Refresh

Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37

Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换