問題描述
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;有待嘗試!