最近升级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...
  5. PHP文档-$_SERVER:http://php.net/manual/zh/rese...

来源:https://segmentfault.com/a/1190000018235221

PHP_SELF变量解析和重复路径解决的更多相关文章

  1. 解决 U2000 R017 安装报错: 检查SQL server数据库环境变量信息 ( 异常 ) [ 详细信息 ] PATH环境变量中缺少数据库路径的信息

    U2000 R017 安装报错: 检查SQL server数据库环境变量信息 ( 异常 ) [ 详细信息 ] PATH环境变量中缺少数据库路径的信息 管理员模式打开注册表位置: HKEY_LOCAL_ ...

  2. PHP字符串中的变量解析(+教你如何在PHP字符串中加入变量)

    定义字符串的时候,用单引号或者双引号都是可以的.我个人习惯是用双引号.在输出字符串的时候,若字符串中含有字符串变量,使用单引号和双引号则是有区别的.如下面程序: 1 2 3 4 5 6 7 8 < ...

  3. zencart 具体页面调用规则: $body_code变量解析

    zencart $body_code变量解析 修改centerColumn 可以修改中间产品方框的大小 2.2.5 .BODY文件在这个文件生效 require($body_code) include ...

  4. 【转载】WIN7访问共享:0x80070035 找不到网络路径解决方法

    转载:http://blog.chinaunix.net/uid-12372814-id-3518571.html 昨天刚装WIN7系统,今天早上准备访问服务器安装些软件,结果出现网络错误,提示Win ...

  5. OK335xS U-boot 环境变量解析

    /************************************************************************************************** ...

  6. I.MX6 Linux U-boot 环境变量解析

    /********************************************************************************** * I.MX6 Linux U- ...

  7. C# var声明变量解析

    C# var声明变量解析: 在C#3.0中提供了一种新的声明变量的方式,这就是var. 通过这个关键字,在声明变量时就无需指定类型了,变量类型是在初始化时由编译器确定的.代码如下: var ss = ...

  8. iphone 浏览器自动解析数字为号码解决方法

    iphone 浏览器自动解析数字为号码解决方法 www.MyException.Cn  网友分享于:2015-10-09  浏览:0次   iphone 浏览器自动解析数字为号码解决办法 在工作中遇到 ...

  9. jquery事件重复绑定解决办法

    一$.fn.live 重复绑定 解决:使用die()方法,在live()方法绑定前,将此元素上的前面被绑定的事件统统解除,然后再通过live()方法绑定新的事件. //先通过die()方法解除,再通过 ...

随机推荐

  1. HDFS体系结构(NameNode、DataNode详解)

    hadoop项目地址:http://hadoop.apache.org/ NameNode.DataNode详解 (一)分布式文件系统概述 数据量越来越多,在一个操作系统管辖的范围存不下了,那么就分配 ...

  2. ZOJ 3781 - Paint the Grid Reloaded - [DFS连通块缩点建图+BFS求深度][第11届浙江省赛F题]

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 Time Limit: 2 Seconds      Me ...

  3. POJ 1556 - The Doors - [平面几何+建图spfa最短路]

    题目链接:http://poj.org/problem?id=1556 Time Limit: 1000MS Memory Limit: 10000K Description You are to f ...

  4. hihocoder 1320 - 压缩字符串 - [hiho一下160周]

    这道题目可以说是一道非常好非常一颗赛艇的DP题了. 需要注意的是,其中情形3),字符串必然能完全转化为 N(str)形式,如果有N(str1)M(str2)等等另外样式,应该首先使用拼接形式对其进行划 ...

  5. python字典获取最大值的键的值

    有时我们需要字典中数值最大的那个键的名字,使用max(dict, key=dict.get)函数非常的方便 key_name = max(my_dict, key=my_dict.get) 获取之后你 ...

  6. ip相关问题解答

    用路由器上网时电脑的设置: ===================== 路由器如何设置另外讨论,现只讲电脑的设置.在Windows XP下: 1.确定桌面上有没有“网上邻居”图标,如果没有,在桌面空白 ...

  7. YYLabel计算富文本高度-膜拜大神

    http://www.jianshu.com/p/07cd655fee7e YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:C ...

  8. 02 - nginx - 反向代理、限速

    一.Nginx反向代理 代理服务器,客户机在发送请求时,不会直接发送给目的主机,而是先发送给代理服务器. 代理服务接受客户机请求之后,再向主机发出,并接收目的主机返回的数据,存放在代理服务器的硬盘中, ...

  9. MongoDB的”存储过程“

    使用 db.system.js.insert({_id:"aaa", value:"zzq"}); 这条命令向全局系统的js中添加了一个_id为"aa ...

  10. HTML5 Storage(永久存储)

    localStorage.aa="aa"; //存储了一个key为aa并且value为aa的键值对: localStorage.setItem("bb", &q ...