最近太忙了,好長一段時間沒寫部落格,這段時間總算稍微有點時間了,準備看看thinkphp的源碼,已經看了一些,但離看完還差得遠,總之先單獨記錄一下看過的源碼,以後的東西在陸續放出。
注:我的看的版本不是最新的thinkphp,是3.23的版本。
基礎性的講解:
U函數有四個參數,如 U($url='',$vars='',$suffix=true,$domain=false):
$url是要解析的url的表達式,格式是[子產品/控制器/操作#錨點@域名]?參數1=值1&參數2=值2...],可以定義路由,直接在最前面加上/即可
$vars是url的參數值對,string|array類型,字元串形式格式aaa=1&bbb=2
$suffix是url是否帶字尾,這個參數是string|boolean類型,true是帶字尾,false則為不帶,預設為true,也可以直接填寫字尾的,例如.html。
$domain是url是否帶域名,true顯示域名 $url有@域名部分則顯示這部分的域名,如果沒有則根據情況擷取,false不顯示域名,false要起效,url保證沒有@域名部分,否則還是會顯示@域名部分的域名,因為@域名最後解析會指派給$domain,這樣$domain設定為false就無效了,會變成true
U函數源碼,源碼解釋盡可能詳細了,可以配合下面的流程圖來了解,這裡面還涉及了操作、控制器、子產品映射,由于篇幅問題,以後單獨拿出來寫一篇,下面注釋也有解釋:
/**
* URL組裝 支援不同URL模式
* @param string $url URL表達式,格式:'[子產品/控制器/操作#錨點@域名]?參數1=值1&參數2=值2...'
* @param string|array $vars 傳入的參數,支援數組和字元串
* @param string|boolean $suffix 僞靜态字尾,預設為true表示擷取配置值,false或空表示沒有字尾,還可以直接填寫字尾,例如.html
* @param boolean $domain 是否顯示域名
* true顯示域名 $url有@域名部分則顯示這部分的域名,如果沒有則根據情況擷取
* false不顯示域名,false要起效,url保證沒有@域名部分,否則還是會顯示@域名部分的域名
* 因為@域名最後解析會指派給$domain,這樣$domain設定為false就無效了,會變成true
* @return string
*/
function U($url='',$vars='',$suffix=true,$domain=false) {
// 解析URL
/*parse_url解析url表達式成數組形式并指派給$info,根據url表達式會分為path和fragment兩部分
path對應子產品/控制器/操作,fragment對應#錨點@域名?參數1=值1&參數2=值2...*/
$info = parse_url($url);
/*檢查$info['path']是否為空,如果不為空,則将其指派給$url,如果為空,則将操作名指派給$url*/
$url = !empty($info['path'])?$info['path']:ACTION_NAME;
/*檢查$info['fragment']是否設定了*/
if(isset($info['fragment'])) { // 解析錨點
/*如果設定了,則将其指派給$anchor*/
$anchor = $info['fragment'];
/*檢查$anchor中是否有?,且?不是首字元,注意?後是跟query的字元串,這裡跟随的是1個或多個參數對*/
if(false !== strpos($anchor,'?')) { // 解析參數
/*如果有則根據?将$anchor打散成兩個單元值的數組,?号前對應的單元值指派給$anchor,
?後對應的單元值指派$info['query']*/
list($anchor,$info['query']) = explode('?',$anchor,2);
}
/*檢查$anchor裡是否存在@,且不是首字元,注意@後是跟域名的字元串*/
if(false !== strpos($anchor,'@')) { // 解析域名
/*根據@将其分割成兩個單元的數組,@前對應的數組單元值指派給$anchor,@後對應的單元值指派給$host*/
list($anchor,$host) = explode('@',$anchor, 2);
}
/*如果沒有設定$info['fragment'],則檢查$url裡是否存在@,且不為首字元*/
}elseif(false !== strpos($url,'@')) { // 解析域名
/*根據@分割成兩個單元的數組,然後将第一個單元也就是@前的部分指派給$url,
@後面對應的單元值指派給$url*/
list($url,$host) = explode('@',$info['path'], 2);
}
// 解析子域名
/*檢查$host是否設定*/
if(isset($host)) {
/*如果設定了,檢查$host是否存在.,如果存在且不位于首字元,則傳回空,如果不存在或者.為首字元,
則檢查$_SERVER['HTTP_HOST']裡是否存在.,如果存在.并傳回第一個出現的位置到最後結束部分的字元,
将傳回的值拼接到$host後組成域名$domain
例如$host=mq,$_SERVER['HTTP_HOST']=www.baidu.com,這裡得到的是
mq.baidu.com
例如$host=.mq 會拼接成 .mq.baidu.com*/
$domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
/*如果沒有設定$host,則檢查使用者送出$domain是否恒等于true*/
}elseif($domain===true){
/*如果帶域名,則擷取$_SERVER['HTTP_HOST']的值指派給$domain*/
$domain = $_SERVER['HTTP_HOST'];
/*檢查是否開啟了子域名配置*/
if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 開啟子域名部署
/*如果開啟了,則檢查$domain是否恒等于localhost,如果是,則傳回locahost,如果不是,
則用www拼接$_SERVER['HTTP_HOST']的第一個.開始位置到結束部分的字元串*/
$domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.');
// '子域名'=>array('子產品[/控制器]');
/*擷取配置檔案裡的APP_SUB_DOMAIN_RULES并進行循環
循環$key是比對的條件,$rule是規則,U函數隻支援子域名比對規則的數組和字元串形式*/
foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {
/*判斷循環的規則$rule是不是數組,如果是則将$rule[0]作為規則$rule
如果不是數組,則将其直接作為$rule*/
$rule = is_array($rule)?$rule[0]:$rule;
/*檢查$key是否存在*号,如果不存在同時$url包含$rule的比對規則,并是從首字元開始比對的*/
if(false === strpos($key,'*') && 0=== strpos($url,$rule)) {
/*如果兩個條件都滿足,将域名檢查是否包含.如果包含則将其所在的位置前面拼接$key,
最後複制給$domain的域名*/
$domain = $key.strstr($domain,'.'); // 生成對應子域名
/*将$url的第0個字元開始,長度為$rule長度的部分替換成空值*/
$url = substr_replace($url,'',0,strlen($rule));
/*跳出循環*/
break;
}
}
}
}
// 解析參數
/*檢查使用者傳入的$vars的參數是不是字元串*/
if(is_string($vars)) { // aaa=1&bbb=2 轉換成數組
/*如果是字元串,将其url的query形式字元串解析成數組形式傳回給$vars*/
parse_str($vars,$vars);
/*如果不是字元串,檢查參數$vars是不是數組*/
}elseif(!is_array($vars)){
/*如果不是數組,也不是字元串,将其指派為空數組*/
$vars = array();
}
/*檢查$info['query']是否設定*/
if(isset($info['query'])) { // 解析位址裡面參數 合并到vars
/*如果設定了,将其解析到$params數組裡*/
parse_str($info['query'],$params);
/*将$params和$vars數組合并後重新指派給$vars*/
$vars = array_merge($params,$vars);
}
// URL組裝
/*擷取url配置的分隔符并指派給$depr*/
$depr = C('URL_PATHINFO_DEPR');
/*擷取配置檔案中的url是否大小寫敏感指派給$urlCase*/
$urlCase = C('URL_CASE_INSENSITIVE');
/*檢查$url是否存在*/
if($url) {
/*如果存在,則檢查$url首字元是不是/*/
if(0=== strpos($url,'/')) {// 定義路由
/*如果是則設定$route為true*/
$route = true;
/*去掉$url的首字元/*/
$url = substr($url,1);
/*檢查url的分隔符是不是/*/
if('/' != $depr) {
/*如果不是,則将$url裡的/替換成設定的分隔符$depr*/
$url = str_replace('/',$depr,$url);
}
/*如果首字元不是/,則不定義路由*/
}else{
/*檢查分隔符是不是/*/
if('/' != $depr) { // 安全替換
/*如果/不是分隔符将/替換成設定的分隔符*/
$url = str_replace('/',$depr,$url);
}
// 解析子產品、控制器和操作
/*修剪$url兩端的分隔符*/
$url = trim($url,$depr);
/*根據分隔符将$url分割成數組并指派給$path*/
$path = explode($depr,$url);
/*給$var初始化為空數組*/
$var = array();
/*将子產品變量名指派給$varModule,預設是m*/
$varModule = C('VAR_MODULE');
/*将控制器變量名指派給$varController,預設是c*/
$varController = C('VAR_CONTROLLER');
/*将方法變量名指派給$varAction,預設是a*/
$varAction = C('VAR_ACTION');
/*檢查$path是否為空,如果不為空,則将其取出數組裡的最後一個單元值指派給$var[$varAction],
如果為空,則調用常量ACTION_NAME作為指派的值*/
$var[$varAction] = !empty($path)?array_pop($path):ACTION_NAME;
/*檢查$path是否為空,如果不為空,再次取出最後一個單元值指派給$var[$varController],
如果為空,則調用常量CONTROLLER_NAME*/
$var[$varController] = !empty($path)?array_pop($path):CONTROLLER_NAME;
/*擷取URL_ACTION_MAP操作映射配置并指派給$maps,并檢查是否為空
這裡用來檢查是否開啟了操作映射并根據映射進行替換*/
if($maps = C('URL_ACTION_MAP')) {
/*如果不為空則将$var[$varController]轉變為小寫,并作為$maps的鍵檢查該值是否設定
這裡是為了操作映射裡找到對應控制器對應的操作映射*/
if(isset($maps[strtolower($var[$varController])])) {
/*如果存在,将其指派給$maps*/
$maps = $maps[strtolower($var[$varController])];
/*将$var[$varAction]轉為小寫,同時檢查該值是否存在于$maps内,如果存在傳回其值對應的鍵指派給$action
如果不存在,則傳回false給$action,檢測$action是否為真*/
if($action = array_search(strtolower($var[$varAction]),$maps)){
/*如果$action存在,則将$action指派給$var[$varAction]*/
$var[$varAction] = $action;
}
}
}
/*檢測URL_CONTROLLER_MAP是否存在,如果存在指派給$maps,并檢測其的值是否為真*/
if($maps = C('URL_CONTROLLER_MAP')) {
/*如果為真,則檢測$maps是否存在$var[$varController]的小寫值,如果存在,則将其對應的鍵指派給$controller,
如果不存在則指派false*/
if($controller = array_search(strtolower($var[$varController]),$maps)){
/*将$controller指派給$var[$varController]*/
$var[$varController] = $controller;
}
}
/*檢查$urlCase也就是url是否區分大小寫 true為不區分,false為區分*/
if($urlCase) {
/*如果不區分大小寫,則将$var[$varController]轉化為小寫*/
$var[$varController] = parse_name($var[$varController]);
}
/*$module初始化為空字元串*/
$module = '';
/*檢測$path是否為空*/
if(!empty($path)) {
/*如果不為空,根據分隔符将$path分割成數組,并指派給$var[$varModule]*/
$var[$varModule] = implode($depr,$path);
}else{
/*如果為空,則需要自行判斷子產品是哪個
檢查是否允許多子產品的配置,true為允許,false為不允許,并且需要設定預設子產品DEFAULT_MODULE*/
if(C('MULTI_MODULE')) {
/*如果為true,則檢測MODULE_NAME常量是不是等于預設子產品配置,如果等于,
再檢測允許子產品清單名單是否為false,以下有兩種可能
如果通路子產品不為預設的子產品 則$var[$varModule]子產品值為通路子產品
如果通路子產品為預設子產品,則允許的子產品清單為false,則$var[$varModule]子產品值為通路子產品
*/
if(MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')){
/*如果子產品常量不等于預設子產品,或者沒有設定子產品允許清單,将其指派給$var[$varModule]*/
$var[$varModule]= MODULE_NAME;
}
}
}
/*檢查子產品映射配置是否設定,并指派給$maps,如果沒有設定就是false*/
if($maps = C('URL_MODULE_MAP')) {
/*如果$maps子產品映射設定了,則檢查$var[$varModule]的小寫是否存在于$maps中,如果存在将其
指派給$_module,如果不存在指派$_module為false*/
if($_module = array_search(strtolower($var[$varModule]),$maps)){
/*如果存在,将$var[$varModule]指派$_module*/
$var[$varModule] = $_module;
}
}
/*檢查$var[$varModule]是否設定了*/
if(isset($var[$varModule])){
/*如果設定了,則将其指派給$module*/
$module = $var[$varModule];
/*同時登出$var[$varModule]*/
unset($var[$varModule]);
}
}
}
/*檢測配置URL_MODEL通路模式是否等于0模式,也就是普通URL通路模式*/
if(C('URL_MODEL') == 0) { // 普通模式URL轉換
/*如果是,則将url拼接起來,__APP__為項目入口檔案位址,VAR_MODULE為子產品變量名配置
$module是上面擷取到的子產品名稱,$var是數組,裡面包含了控制器及其方法以及其他的查詢參數值,
array_reverse将其倒轉順序後使用http_build_query拼接為url查詢語句形式
拼接好的url指派給$url
*/
$url = __APP__.'?'.C('VAR_MODULE')."={$module}&".http_build_query(array_reverse($var));
/*檢查是否大小寫敏感,true為不敏感*/
if($urlCase){
/*将$url全部轉化為小寫*/
$url = strtolower($url);
}
/*檢查$vars是否為空*/
if(!empty($vars)) {
/*如果不為空,則将其轉變為url查詢語句形式*/
$vars = http_build_query($vars);
/*同時将其拼接在$url後并添加&符*/
$url .= '&'.$vars;
}
/*如果url通路不為普通模式*/
}else{ // PATHINFO模式或者相容URL模式
/*檢測是否設定了$route,也就是定義了路由,定義路由的情況直接用$url定義的部分,
而沒有定義則使用前面擷取到的$var來組裝成$url*/
if(isset($route)) {
/*如果定義了路由,則用__APP__項目入口檔案位址拼接/和去除$url後側url分隔符的部分并重新指派給$url*/
$url = __APP__.'/'.rtrim($url,$depr);
}else{
/*如果沒有定義路由,則檢測是否定義BIND_MODULE的常量,同時檢測BIND_MODULE是否等于$module,如果兩個條件
都滿足的話,$module為空值,如果不滿足,則$module不變*/
$module = (defined('BIND_MODULE') && BIND_MODULE==$module )? '' : $module;
/*項目入口檔案後面拼接/和子產品名以及子產品分割符,如果子產品名不存在,則拼接/,
将$var數組順序倒置然後根據url分隔符拼接成字元串,拼接完畢後指派給$url*/
$url = __APP__.'/'.($module?$module.MODULE_PATHINFO_DEPR:'').implode($depr,array_reverse($var));
}
/*檢測$urlCase的url大小寫是否敏感,true為不敏感*/
if($urlCase){
/*如果不敏感,則将$url轉化為小寫*/
$url = strtolower($url);
}
/*檢測$vars是否為空*/
if(!empty($vars)) { // 添加參數
/*如果不為空,則進行循環,$var為循環的key,$val為循環的值*/
foreach ($vars as $var => $val){
/*檢查循環出來的$val值去掉兩端的空白字元後是否恒等于空,如果不為空,則
$url後面拼接分隔符$depr和鍵$var分隔符$depr以及值$val*/
if('' !== trim($val)) $url .= $depr . $var . $depr . urlencode($val);
}
}
/*檢測$suffix*/
if($suffix) {
/*檢測$suffix也就是字尾恒等于true,如果是則将URL_HTML_SUFFIX的配置指派給$suffix,
如果不是true,則使用傳入的$suffix*/
$suffix = $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
/*檢查$suffix裡|首次出現的位置并指派給$pos*/
if($pos = strpos($suffix, '|')){
/*$pos如果不為false,則将0到$pos之間的部分從$suffix裡裁剪出來指派給$suffix*/
$suffix = substr($suffix, 0, $pos);
}
/*$url最後一位字元裁剪出來不為/,同時$suffix為true*/
if($suffix && '/' != substr($url,-1)){
/*将$suffix左側的.去除後前面拼接.後再在前面拼接$url*/
$url .= '.'.ltrim($suffix,'.');
}
}
}
/*檢測是否存在錨點$anchor*/
if(isset($anchor)){
/*如果存在,則将其拼接在$url後,$anchor放在#後面*/
$url .= '#'.$anchor;
}
/*檢測是否存在域名$domain*/
if($domain) {
/*如果有則使用is_ssl()函數判定是http還是https協定,判定完拼接相應的協定加上域名以及url,最後指派給$url*/
$url = (is_ssl()?'https://':'http://').$domain.$url;
}
/*傳回拼接好的url*/
return $url;
}
U函數運作流程圖,盡可能的省略但不影響表達,希望能有幫助,圖檔比較大,可以另外打開檢視: