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=\'#\'" /span>.$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=\'#\'" /span>.$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整合通信流程原理详细分析

 

 

 

你可能感兴趣的:(职场,休闲,ucenter,Disucz,X2)