Ucenter 1.6和Discuz X2整合通信流程原理詳細分析

本文將以本站IT快訊網整合Discuz X2結合Discuz X2用戶登陸流程源碼來分析整個Ucenter的通信流程原理步驟
 

1. 當會員從登陸界面輸入正確的用戶名和密碼單擊登陸按鈕後,我們看到打開源碼discuzx/source/class/class_member.php  找到第155行代碼.

 

  1. $ucsynlogin = $this->setting['allowsynlogin'] ? uc_user_synlogin($_G['uid']) : '';  

從上面的代碼可以看到,當會員登陸驗證通過後, 當你在ucenter管理面板裏面設置開啓同步登陸後,X2將會調用uc_user_synlogin($_G['uid']) 方法發出同步登陸的通知。

2. 調用uc_user_synlogin($_G['uid']) 方法後,實際調用的是discuzx/uc_client/client.php  中的 uc_user_synlogin函數,我們打開源碼看方法簽名,在310 - 320 之間:

  1. function uc_user_synlogin($uid) { 
  2.             $uid = intval($uid); 
  3.             if(@include UC_ROOT.'./data/cache/apps.php') { 
  4.                     if(count($_CACHE['apps']) > 1) { 
  5.                             $return = uc_api_post('user''synlogin'array('uid'=>$uid)); 
  6.                     } else { 
  7.                             $return = ''
  8.                     } 
  9.             } 
  10.             return $return
  11.     } 

跟着這個方法流程,我們繼續看 uc_api_post 方法簽名, 仍然是在discuzx/uc_client/client.php文件,找到54-74行

 

  1. function uc_api_post($module$action$arg = array()) { 
  2.             $s = $sep = ''
  3.             foreach($arg as $k => $v) { 
  4.                     $k = urlencode($k); 
  5.                     if(is_array($v)) { 
  6.                             $s2 = $sep2 = ''
  7.                             foreach($v as $k2 => $v2) { 
  8.                                     $k2 = urlencode($k2); 
  9.                                     $s2 .= "$sep2{$k}[$k2]=".urlencode(uc_stripslashes($v2)); 
  10.                                     $sep2 = '&'
  11.                             } 
  12.                             $s .= $sep.$s2
  13.                     } else { 
  14.                             $s .= "$sep$k=".urlencode(uc_stripslashes($v)); 
  15.                     } 
  16.                     $sep = '&'
  17.             } 
  18.             $postdata = uc_api_requestdata($module$action$s); 
  19.             return uc_fopen2(UC_API.'/index.php', 500000, $postdata'', TRUE, UC_IP, 20); 
  20.     }  

可以看到這個方法主要調用

uc_api_requestdata

來組裝一個post請求需要發送的數據,看這個方法的簽名

 

  1. function uc_api_requestdata($module$action$arg=''$extra='') { 
  2.            $input = uc_api_input($arg); 
  3.            $post = "m=$module&a=$action&inajax=2&release=".UC_CLIENT_RELEASE."&input=$input&appid=".UC_APPID.$extra
  4.            return $post
  5.    }  

注意看uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20)函數中的 UC_API和 UC_APPID常量實際就是我們在 discuzx/config/config_ucenter.php 文件中配置的預定的常量,相信到這裏大家已經明白了這幾個常量的用意,UC_API就是你定義的uc_server的URL.

return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);

 

  1. define('UC_API''http://www.itkuaixun.com/xxxx');  //uc_server地址 
  2.    define('UC_APPID''1');  

 

3. 接着看uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20) 方法簽名和調用原理

 

  1. function uc_fopen2($url$limit = 0, $post = ''$cookie = ''$bysocket = FALSE, $ip = ''$timeout = 15, $block = TRUE) { 
  2.             $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1; 
  3.             if($__times__ > 2) { 
  4.                     return ''
  5.             } 
  6.             $url .= (strpos($url'?') === FALSE ? '?' : '&')."__times__=$__times__"
  7.             return uc_fopen($url$limit$post$cookie$bysocket$ip$timeout$block); 
  8.     }  

最終由uc_fopen函數調用PHP函數fsockopen 或者 pfsockopen 打開一個socket 連接將數據用流的形式發送通知數據到uc_server 

 

  1. function uc_fopen($url$limit = 0, $post = ''$cookie = ''$bysocket = FALSE, $ip = ''$timeout = 15, $block = TRUE) { 
  2.             $return = ''
  3.             $matches = parse_url($url); 
  4.             !isset($matches['host']) && $matches['host'] = ''
  5.             !isset($matches['path']) && $matches['path'] = ''
  6.             !isset($matches['query']) && $matches['query'] = ''
  7.             !isset($matches['port']) && $matches['port'] = ''
  8.             $host = $matches['host']; 
  9.             $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/'
  10.             $port = !emptyempty($matches['port']) ? $matches['port'] : 80; 
  11.             if($post) { 
  12.                     $out = "POST $path HTTP/1.0\r\n"
  13.                     $out .= "Accept: */*\r\n"
  14.                     //$out .= "Referer: $boardurl\r\n"; 
  15.                     $out .= "Accept-Language: zh-cn\r\n"
  16.                     $out .= "Content-Type: application/x-www-form-urlencoded\r\n"
  17.                     $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n"
  18.                     $out .= "Host: $host\r\n"
  19.                     $out .= 'Content-Length: '.strlen($post)."\r\n"
  20.                     $out .= "Connection: Close\r\n"
  21.                     $out .= "Cache-Control: no-cache\r\n"
  22.                     $out .= "Cookie: $cookie\r\n\r\n"
  23.                     $out .= $post
  24.             } else { 
  25.                     $out = "GET $path HTTP/1.0\r\n"
  26.                     $out .= "Accept: */*\r\n"
  27.                     //$out .= "Referer: $boardurl\r\n"; 
  28.                     $out .= "Accept-Language: zh-cn\r\n"
  29.                     $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n"
  30.                     $out .= "Host: $host\r\n"
  31.                     $out .= "Connection: Close\r\n"
  32.                     $out .= "Cookie: $cookie\r\n\r\n"
  33.             } 
  34.             if(function_exists('fsockopen')) { 
  35.                     $fp = @fsockopen(($ip ? $ip : $host), $port$errno$errstr$timeout); 
  36.             } elseif (function_exists('pfsockopen')) { 
  37.                     $fp = @pfsockopen(($ip ? $ip : $host), $port$errno$errstr$timeout); 
  38.             } else { 
  39.                     $fp = false; 
  40.             } 
  41.             if(!$fp) { 
  42.                     return ''
  43.             } else { 
  44.                     stream_set_blocking($fp$block); 
  45.                     stream_set_timeout($fp$timeout); 
  46.                     @fwrite($fp$out); 
  47.                     $status = stream_get_meta_data($fp); 
  48.                     if(!$status['timed_out']) { 
  49.                             while (!feof($fp)) { 
  50.                                     if(($header = @fgets($fp)) && ($header == "\r\n" ||  $header == "\n")) { 
  51.                                             break
  52.                                     } 
  53.                             } 
  54.                             $stop = false; 
  55.                             while(!feof($fp) && !$stop) { 
  56.                                     $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit)); 
  57.                                     $return .= $data
  58.                                     if($limit) { 
  59.                                             $limit -= strlen($data); 
  60.                                             $stop = $limit <= 0; 
  61.                                     } 
  62.                             } 
  63.                     } 
  64.                     @fclose($fp); 
  65.                     return $return
  66.             } 
  67.     }  

至此,uc_client的調用流程結束,轉入uc_server部份.

 

4. 繼續打開

discuzx/uc_server/index.php

  部份,找到52行,我們就可以完全理解ucenter的內部通知機制,當接收到

uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20)  方法發送過來的請求後,

 

  1. if(in_array($marray('app''frame''user''pm''pm_client''tag''feed''friend''domain''credit''mail''version'))) { 
  2.             if(file_exists(UC_ROOT.RELEASE_ROOT."control/$m.php")) { 
  3.                     include UC_ROOT.RELEASE_ROOT."control/$m.php"
  4.             } else { 
  5.                     include UC_ROOT."control/$m.php"
  6.             } 
  7.             $classname = $m.'control'
  8.             $control = new $classname(); 
  9.             $method = 'on'.$a
  10.             if(method_exists($control$method) && $a{0} != '_') { 
  11.                     $data = $control->$method(); 
  12.                     echo is_array($data) ? $control->serialize($data, 1) : $data
  13.                     exit
  14.             } elseif(method_exists($control'_call')) { 
  15.                     $data = $control->_call('on'.$a''); 
  16.                     echo is_array($data) ? $control->serialize($data, 1) : $data
  17.                     exit
  18.             } else { 
  19.                     exit('Action not found!'); 
  20.             } 
  21.     } else { 
  22.             exit('Module not found!'); 
  23.     }  

在這個方法裏面查找對應的通知模塊,這裏我們以用戶同步登陸爲例,其它原理都是一樣的,所以這裏實際調用的是user我們接着打開 discuzx/uc_server/control/user.php 文件源碼, 在第32行開始,可以看到最終它調用onsynlogin方法,查詢緩存的所有開啓同步通知的應用,

 

  1. // -1 未開啓 
  2.             function onsynlogin() { 
  3.                     $this->init_input(); 
  4.                     $uid = $this->input('uid'); 
  5.                     if($this->app['synlogin']) { 
  6.                             if($this->user = $_ENV['user']->get_user_by_uid($uid)) { 
  7.                                     $synstr = ''
  8.     //這裏循環從緩存中讀取所有需要發送通知的應用 
  9.                                     foreach($this->cache['apps'as $appid => $app) { 
  10.                                             if($app['synlogin']) { 
  11.                                                     $synstr .= '<script type="text/javascript" src="'.$app['url'].'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1"></script>'; 
  12.                                                     if(is_array($app['extra']['extraurl'])) foreach($app['extra']['extraurl'as $extraurl) { 
  13.                                                             $synstr .= '<script type="text/javascript" src="'.$extraurl.'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1"></script>'; 
  14.                                                     } 
  15.                                             } 
  16.                                     } 
  17.                                     return $synstr
  18.                             } 
  19.                     } 
  20.                     return ''
  21.             }  

使用Firefox安裝好firebug可以看到,這就是爲什麼返回我們看到返回的一段javascript代碼,如果不成功將返回-1(false) ,


$app['url'].'/api

   看到這段代碼,這也是爲什麼ucenter API文檔中定義我們必須在根目錄下面創建一個api文件夾, 使用P3P協議來解決cookie發送的問題.到這裏相信你已經明白整個UCenter的運行流程和原理,趕緊動手去整合一個。



Yii 和 Discuz X2完美整合演式地址:

IT快訊網

有問題歡迎回復,時間允許範圍內將會第一時間回覆。


轉載請說明出自:  IT快訊網|  原文地址: Ucenter 1.6和Discuz X2整合通信流程原理詳細分析

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章