yaf路由解析错误一次问题记录
问题描述
nginx服务器,部分配置如下:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php($|/) {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info; #打开PATH_INFO模式
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
然后yaf的所有请求路由解析之后均默认指向了index控制器index方法,即为默认方法;
当把PATH_INFO模式关闭,即注释掉之后,则yaf的路由解析正常;
问题分析
PATH_INFO是一个CGI的标准,可以用来做为传参载体。
PAHT_INFO模式的url是这样的: http://localhost/index.php/home/user/login?var=value
其中home/user/login就是PATH_INFO的值,var=value是参数;
而且当开启nginx的PATH_INFO模式的时候,PHP中预定义变量$_SERVER[‘PATH_INFO’]是有值的,是被nginx赋值的,如果关闭nginx的PATH_INFO模式,$_SERVER[‘PATH_INFO’]没有被定义(是没有这个key,不是为空值);
通过以下例子来分析:
开启PATH_INFO模式
访问 http://yaf.com/index.php/index/test(PAHT_INFO模式url)
结果:
$_SERVER[“PATH_INFO”]=>string(11) “/index/test”
$_SERVER[“REQUEST_URI”]=>string(25) “/index.php/index/test?a=b”
解析正常,路由解析到index/test
访问 http://yaf.com/index/test(无index.php的url)
结果:
$_SERVER[“PATH_INFO”]=>string(0) “”
$_SERVER[“REQUEST_URI”]=>string(11) “/index/test”
解析错误,路由解析到index/index关闭PATH_INFO模式
访问 http://yaf.com/index.php/index/test(PAHT_INFO模式url)
结果:
$_SERVER[“PATH_INFO”] undefined
$_SERVER[“REQUEST_URI”]=>string(11) “/index/test”
解析正常,路由解析到index/test
访问 http://yaf.com/index/test(无index.php的url)
结果:
$_SERVER[“PATH_INFO”] undefined
$_SERVER[“REQUEST_URI”]=>string(11) “/index/test”
解析正常,路由解析到index/test
因为我们想要的实现的访问路径是不带index.php的,我们用nginx隐藏了index.php,所以我们出问题的就是上面例子中开启PATH_INFO模式的第二个例子;正常访问就是上面例子中关闭PATH_INFO模式的第二个例子;
从上面例子中得知,当有PATH_INFO这个值的时候,使用无index.php的url访问,会解析错误;而当没有PATH_INFO这个值得时候,使用无index.php的url访问,解析正常;所以我们猜想,yaf的路由解析肯定优先解析PATH_INFO的值,然后才会去解析REQUEST_URI的值;
问题原因
yaf的源码:https://github.com/laruence/yaf/blob/6f67e9c68077542e54d149046c77d5c55efeaf90/requests/yaf_request_http.c
其中关于PAHT_INFO和REQUEST_URI的解析顺序相关的代码:
do {
...
# 解析PATH_INFO
uri = yaf_request_query_str(YAF_GLOBAL_VARS_SERVER, "PATH_INFO", sizeof("PATH_INFO") - 1);
if (uri) {
if (EXPECTED(Z_TYPE_P(uri) == IS_STRING)) {
settled_uri = zend_string_copy(Z_STR_P(uri));
break; #直接断开
}
}
# 解析REQUEST_URI
uri = yaf_request_query_str(YAF_GLOBAL_VARS_SERVER, "REQUEST_URI", sizeof("REQUEST_URI") - 1);
if (uri) {
if (EXPECTED(Z_TYPE_P(uri) == IS_STRING)) {
/* Http proxy reqs setup request uri with scheme and host [and port] + the url path,
* only use url path */
if (strncasecmp(Z_STRVAL_P(uri), "http", sizeof("http") - 1) == 0) {
php_url *url_info = php_url_parse(Z_STRVAL_P(uri));
if (url_info && url_info->path) {
settled_uri = zend_string_init(url_info->path, strlen(url_info->path), 0);
}
php_url_free(url_info);
} else {
char *pos = NULL;
if ((pos = strstr(Z_STRVAL_P(uri), "?"))) {
settled_uri = zend_string_init(Z_STRVAL_P(uri), pos - Z_STRVAL_P(uri), 0);
} else {
settled_uri = zend_string_copy(Z_STR_P(uri));
}
}
break;
}
}
...
} while (0);
可以看到,只要PATH_INFO存在,那么yaf会优先解析PATH_INFO,否则解析REQUEST_URI;
不知道用rewrite模式是否可以同时开启PATH_INFO模式,并且同时还能隐藏index.php;有待尝试!