最近升级PHP
到PHP7
版本,并从新部署了新的Nginx
,启动的时候发现了一个问题,全局变量$_SERVER['PHP_SELF']
的值发生了改变,从而影响到代码的功能。所以咱们来了解下$_SERVER
全局变量中的PHP_SELF/PATH_INFO/SCRIPT_NAME
等参数以及其关系。php
CGI 1.1
规范以前的文章 [ php-fpm进程数管理 ] 已经简单说过CGI的内容,这里咱们再详细讲一下。html
CGI
是Common Gateway Interface(通用网管协议)
,用于让交互程序和Web服务器通讯的协议。它负责处理URL的请求,启动一个进程,将客户端发送的数据做为输入,由Web服务器收集程序的输出并加上合适的头部,再发送回客户端。java
FastCGI
是基于CGI
的加强版本的协议,不一样于建立新的进程来服务请求,使用持续的进程和建立的子进程来处理一连串的进程,这些进程由FastCGI服务器管理,开销更小,效率更高。python
CGI
诞生于1993
年美国国家计算机中心,目的是为不一样的动态页面处理语言(php/python/java)
在不一样的服务器下(apache/nginx)
提供一致的接口规范,提供会话环境变量、会话客户端等信息。nginx
在RFC-CGI1.1文档中包含了协议的所有内容,咱们如今只关注它的 4.1节:Request Meta-Variables
。apache
标准中定义了处理请求应该实现的17
个属性和如何自定义新属性,好比:segmentfault
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 POST
和PUT
,这是数据的内容类型。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_INFO
在Nginx/Apache
中的不一样默认状态,所以,若是须要页面指向本身时,除非如上面示例中的那种url
,建议使用SCRIPT_NAME
变量便可。