最近升级PHP到PHP7版本,并重新部署了新的Nginx,启动时发现一个问题,全局变量$_SERVER['PHP_SELF']的值发生变化,影响到功能的代码。那么我们来了解下$_SERVER全局变量中的PHP_SELF/PATH_INFO/SCRIPT_NAME等参数及其关系。CGI1.1规范之前的文章【php-fpm进程号管理】已经简单提到了CGI的内容,这里再详细说一下。CGI即CommonGatewayInterface(通用网络管理协议),一种用来让交互式程序与Web服务器进行通信的协议。它负责处理URL请求,启动一个进程,将客户端发送的数据作为输入,Web服务器收集程序的输出并添加适当的标头,然后将其发送回客户端。FastCGI是基于CGI协议的增强版。与创建新流程来服务请求不同,它使用连续流程并创建子流程来处理一系列流程。这些进程由FastCGI服务器管理,开销更小,效率更高。CGI于1993年诞生于美国国家计算机中心,目的是为不同服务器(apache/nginx)下的不同动态页面处理语言(php/python/java)提供一致的接口规范,并提供会话环境变量和会话客户端。和其他信息。该协议的全部内容包含在RFC-CGI1.1文档中,我们只关注其4.1节:RequestMeta-Variables。该标准定义了处理请求时应该实现的17个属性以及如何自定义新属性,例如:SERVER_PROTOCOL:信息协议的名称和版本。格式是协议/版本。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:对于带有附加信息的请求,例如HTTPPOST和PUT,这是数据的内容类型。CONTENT_LENGTH:客户端给出的数据内容的长度。这些变量是每个语言和服务器都需要实现的,它们也会有自己定义的一些变量。比如我们今天说的PHP语言中的$_SERVER['PHP_SELF']变??量。PHP的超全局变量$_SERVER$_SERVER是一个数组,包含头信息(header)、路径(path)、脚本位置(scriptlocations)等信息。此数组中的项目由Web服务器创建。不能保证每台服务器都会提供所有项目;服务器可能会忽略一些,或提供此处未列出的一些项目。这意味着?CGI1.1规范中指定了大量此类变量,因此应仔细研究。__FILE__常量包含当前(例如包含)文件的完整路径和文件名。与此相关,我们这里主要关注的变量有:PHP_SELF:当前执行脚本的文件名,与文档根目录相关。例如,在地址为http://example.com/foo/bar.php的脚本中,值为/foo/bar.php。SCRIPT_NAME:包含当前脚本的路径。这在页面需要指向自身时很有用。PATH_INFO:包含客户端提供的路径信息,在实际脚本名称之后和查询字符串之前(如果存在)。例如,如果通过URLhttp://www.example.com/php/path_info.php/some/stuff?foo=bar访问当前脚本,则该值为/some/stuff。文档中描述的web服务器是指我环境中的Nginx。在Apache中,当没有添加任何配置时,PHP脚本默认接受AcceptPathInfo。对于Nginx,不支持PATHINFO,即默认不设置PATH_INFO。因此,对于Nginx架构的常规请求,这些字段的取值是:#http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF:/odp/index.phpSCRIPT_NAME:/odp/index.phpPATH_INFO:null问题:PHP_SELF中出现重复路径我部署新的Nginx服务后,得到上面三个字段的值分别是:#http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF:/odp/index.php/odp/index.phpSCRIPT_NAME:/odp/index.phpPATH_INFO:/odp/index.php注意PHP_SELF字段有重复路径,PATH_INFO也有一个值。Atthistime,thenginx.confconfigurationis:fastcgi_paramSCRIPT_FILENAME$document_root$fastcgi_script_name;fastcgi_paramQUERY_STRING$query_string;fastcgi_paramREQUEST_METHOD_$request_method;fastcgiCONTENT_TYPE$content_type;fastcgi_paramCONTENT_LENGTH$content_length;#注意这一行,我们配置了PATH_INFO字段fastcgi_paramPATH_INFO$fastcgi_script_name;fastcgi_paramSCRIPT_NAME$fastcgi_script_name;fastcgi_paramREQUEST_URI$request_uri;fastcgi_paramDOCUMENT_URI$document_uri;fastcgi_paramDOCUMENT_ROOT$document_root;fastcgi_paramSERVER_PROTOCOL$server_protocol;fastcgi_paramHTTPS$httpsif_not_empty;fastcgi_paramGATEWAY_INTERFACECGI/1.1;fastcgi_paramSERVER_SOFTWAREnginx/$nginx_version;那我们为什么要配置PATH_INFO来影响PHP_SELF的值呢?此时,我们首先会想到自定义属性PHP_SELF的来源,然而,我并没有找到任何文档但是我们可以通过重命名来探索它的定义:改变这两行,我们将其重命名为指定的字符串,而不是请求传入的变量,nginxreload后,此时的结果是:#http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF:SCRIPT_NAMEPATH_INFOSCRIPT_NAME:SCRIPT_NAMEPATH_INFO:PATH_INFO其他变量都是正常的,所以我们可以进一步理解:PHP_SELF=SCRIPT_NAME+PATH_INFO自定义变量:PHP_SELF那么PHP为什么要自定义这个属性呢?官方文档中有这样一个url请求,此时:#http://www.example.com/php/path_info.php/some/stuff?foo=barPHP_SELF:/php/path_info.php/some/stuffSCRIPT_NAME:/php/path_info.phpPATH_INFO:/some/stuff所以,在这种情况下,只有PHP_SELF可以获取当前执行脚本的完整文件或路径。总结为了在不同的服务器和不同的语言之间请求通信,有一个CGI协议规范。该规范在不同的服务器和语言中有自己的实现。在WebServer:Nginx配置文件中,可以设置不同变量的值,解析后传递给PHP-FPM(PHP-FastCGIProcessManager),再进一步传递给负责响应请求的PHP子进程,而PHP中也定义了关于请求通信的全局变量$_SERVER,用于解析请求和处理逻辑。这就是解析请求信息的整个过程。由于PHP中$_SERVER中这些变量的定义有些混乱,还取决于不同的实现和服务器环境,比如Nginx/Apache中PATH_INFO的默认状态不同。因此,如果需要页面指向自己,除非是上面例子中的url,建议使用SCRIPT_NAME变量。参考资料segmentfault-php-fpm进程号管理:https://segmentfault.com/a/11...RFC-CGI1.1:https://tools.ietf.org/html/r...CGI规范和它的历史:http://www.voidcn.com/article...php$_SERVER中一些环境相关的参数详解:https://www.jianshu.com/p/fea...PHPDocumentation-$_服务器:http://php.net/manual/zh/rese...
