Qq.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <?php
  2. /**
  3. * Qq.php UTF-8
  4. * QQ登陆Api
  5. *
  6. * @date : 2018/4/25 16:52
  7. *
  8. * @license 这不是一个自由软件,未经授权不许任何使用和传播。
  9. * @author : wuyonghong <wyh@huosdk.com>
  10. * @version : HUOSDK 8.0
  11. * http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token
  12. */
  13. namespace huolib\oauth\driver;
  14. use huolib\constant\MemConst;
  15. use huolib\oauth\OAuth;
  16. use huolib\tool\Http;
  17. use think\Exception;
  18. use think\Log;
  19. class Qq extends OAuth {
  20. /**
  21. * 获取requestCode的api接口
  22. *
  23. * @var string
  24. */
  25. protected $request_code_url = 'https://graph.qq.com/oauth2.0/authorize';
  26. /**
  27. * 获取Access token的api接口
  28. *
  29. * @var String
  30. */
  31. protected $access_token_url = 'https://graph.qq.com/oauth2.0/token';
  32. /**
  33. * WAP网站
  34. *
  35. * @var string
  36. */
  37. protected $access_token_url_m = 'https://graph.z.qq.com/moc2/token';
  38. /**
  39. * 更新Access token的api接口
  40. *
  41. * @var String
  42. */
  43. protected $refresh_token_url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token';
  44. /**
  45. * API根路径
  46. *
  47. * @var string
  48. */
  49. protected $api_base = 'https://graph.qq.com/';
  50. /**
  51. * 获取request_code的额外参数,可在配置中修改 URL查询字符串格式
  52. *
  53. * @var string
  54. */
  55. protected $authorize = 'get_user_info';
  56. /**
  57. * WxQrcode constructor.
  58. *
  59. * @param array $config
  60. * @param array|null $token
  61. *
  62. * @throws \think\Exception
  63. */
  64. public function __construct(array $config = [], array $token = null) {
  65. $_config = $config;
  66. if (empty($config)) {
  67. if (file_exists(GLOBAL_CONF_PATH."extra/oauth/qq.php")) {
  68. $_config = include GLOBAL_CONF_PATH."extra/oauth/qq.php";
  69. } else {
  70. $_config = array();
  71. }
  72. }
  73. parent::__construct($_config, $token);
  74. }
  75. /**
  76. *
  77. * 第一步:获取Authorization Code
  78. * 请求Authorize访问地址
  79. *
  80. * @param string $display
  81. *
  82. * @return string 跳转地址
  83. */
  84. public function getRequestCodeUrl($display = 'pc') {
  85. $_display = $display;
  86. if ('pc' == $_display) {
  87. $_display = 'default';
  88. $_request_code_url = $this->request_code_url;
  89. } else {
  90. $_display = 'mobile';
  91. $_request_code_url = $this->request_code_url;
  92. }
  93. $params = array(
  94. 'client_id' => $this->app_key,
  95. 'redirect_uri' => $this->callback,
  96. 'response_type' => $this->response_type,
  97. 'scope' => $this->authorize,
  98. 'state' => $this->getState(),
  99. 'display' => $_display,
  100. );
  101. return $_request_code_url.'?'.http_build_query($params);
  102. }
  103. /**
  104. * Step2:通过Authorization Code获取Access Token
  105. *
  106. * @param $code
  107. * @param null $extend
  108. *
  109. * @return array|null
  110. * @throws Exception
  111. */
  112. public function getAccessToken($code, $extend = null) {
  113. $_access_token_url = $this->access_token_url;
  114. $_params = array(
  115. 'grant_type' => $this->grant_type,
  116. 'client_id' => $this->app_key,
  117. 'redirect_uri' => $this->callback,
  118. 'client_secret' => $this->app_secret,
  119. 'code' => $code,
  120. );
  121. $_rdata = Http::get($_access_token_url, $_params);
  122. if (strpos($_rdata, "callback") !== false) {
  123. throw new Exception('没有正确获取到token!'.$_rdata);
  124. }
  125. $this->token = $this->parseToken($_rdata);
  126. return $this->token;
  127. }
  128. /**
  129. * 刷新access_token有效期
  130. *
  131. * @param $refresh_token
  132. *
  133. *
  134. * @return array|mixed|null
  135. * @throws Exception
  136. */
  137. public function getRefreshAccessToken($refresh_token) {
  138. $_params = array(
  139. 'client_id' => $this->app_key,
  140. 'client_secret' => $this->app_secret,
  141. 'refresh_token' => $refresh_token,
  142. 'grant_type' => $this->grant_type,
  143. );
  144. $_rdata = Http::get($this->refresh_token_url, $_params);
  145. $this->token = $this->parseToken($_rdata);
  146. return $this->token;
  147. }
  148. /**
  149. *
  150. * @param string $api API
  151. * @param string $param 调用API的额外参数
  152. * @param string $method HTTP请求方法 默认为GET
  153. * @param null $multi
  154. *
  155. * @return mixed
  156. * @throws Exception
  157. */
  158. public function call($api, $param = '', $method = 'GET', $multi = null) {
  159. $params = array(
  160. 'access_token' => $this->token['access_token'],
  161. 'openid' => $this->getOpenid(),
  162. 'oauth_consumer_key' => $this->app_key
  163. );
  164. $data = Http::request($this->url($api), $params, $method);
  165. return json_decode($data, true);
  166. }
  167. /**
  168. * 获取授权用户的用户信息
  169. * http://wiki.connect.qq.com/get_user_info
  170. *
  171. * @return array
  172. * @throws Exception
  173. * ret 返回码 msg 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
  174. * nickname 用户在QQ空间的昵称。 figureurl 大小为30×30像素的QQ空间头像URL。
  175. * figureurl_1 大小为50×50像素的QQ空间头像URL。
  176. * figureurl_2 大小为100×100像素的QQ空间头像URL。
  177. * figureurl_qq_1 大小为40×40像素的QQ头像URL。
  178. * figureurl_qq_2 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。
  179. * gender 性别。 如果获取不到则默认返回"男"
  180. * is_yellow_vip 标识用户是否为黄钻用户(0:不是;1:是)。
  181. * vip 标识用户是否为黄钻用户(0:不是;1:是)
  182. * yellow_vip_level 黄钻等级
  183. * level 黄钻等级
  184. * is_yellow_year_vip 标识是否为年费黄钻用户(0:不是; 1:是)
  185. *
  186. */
  187. public function getUserInfo() {
  188. $_response = $this->call('user/get_user_info');
  189. if (empty($_response) || (isset($_response['ret']) && $_response['ret'] != 0)) {
  190. throw new Exception('接口访问失败!'.$_response['msg']);
  191. } else {
  192. $_open_id = $this->getOpenid();
  193. $_unionid = $this->getUnionid();
  194. $_oauth_data = array(
  195. 'openid' => $_open_id,
  196. 'unionid' => $_unionid,
  197. 'channel' => 'qq',
  198. 'nickname' => $_response['nickname'],
  199. 'gender' => $this->getGender($_response['gender']),
  200. 'avatar' => $this->getAvatar($_response['figureurl_qq_1']),
  201. 'token' => $this->token
  202. );
  203. return $_oauth_data;
  204. }
  205. }
  206. /**
  207. * 获取当前授权用户的SNS标识
  208. *
  209. * @throws Exception
  210. */
  211. public function getOpenid() {
  212. $_data = $this->token;
  213. if (isset($_data['openid'])) {
  214. return $_data['openid'];
  215. } elseif ($_data['access_token']) {
  216. $_param['access_token'] = $_data['access_token'];
  217. $_param['unionid'] = 1;
  218. $_response = Http::request($this->url('oauth2.0/me'), $_param);
  219. $_rdata = json_decode(trim(substr($_response, 9), " );\n"), true);
  220. if (isset($_rdata['openid'])) {
  221. return $_rdata['openid'];
  222. } else {
  223. throw new Exception("获取用户openid出错:{$_rdata['error_description']}");
  224. }
  225. } else {
  226. throw new Exception('没有获取到openid!');
  227. }
  228. }
  229. /**
  230. * 获取当前授权用户的SNS标识
  231. *
  232. * @throws Exception
  233. */
  234. public function getUnionid() {
  235. $_data = $this->token;
  236. if ($_data['access_token']) {
  237. $_param['access_token'] = $_data['access_token'];
  238. $_param['unionid'] = 1;
  239. $_response = Http::request($this->url('oauth2.0/me'), $_param);
  240. $_rdata = json_decode(trim(substr($_response, 9), " );\n"), true);
  241. if (isset($_rdata['unionid'])) {
  242. return $_rdata['unionid'];
  243. } else {
  244. throw new Exception("获取用户unionid出错:{$_rdata['error_description']}");
  245. }
  246. } else {
  247. throw new Exception('没有获取到unionid!');
  248. }
  249. }
  250. /**
  251. * 解析access_token方法请求后的返回值
  252. *
  253. * @param $result
  254. * @param null $extend
  255. *
  256. * @return mixed
  257. * @throws Exception
  258. */
  259. protected function parseToken($result, $extend = null) {
  260. $_rdata = [];
  261. parse_str($result, $_rdata);
  262. if ($_rdata['access_token'] && $_rdata['expires_in']) {
  263. $this->token = $_rdata;
  264. $_rdata['openid'] = $this->getOpenid();
  265. return $_rdata;
  266. } else {
  267. throw new Exception("获取 ACCESS_token 出错:{$result}");
  268. }
  269. }
  270. /**
  271. * @param string $gender
  272. *
  273. * @return mixed
  274. */
  275. private function getGender($gender = '男') {
  276. switch ($gender) {
  277. case '男':
  278. return MemConst::GENDER_M;
  279. case '女':
  280. return MemConst::GENDER_F;
  281. default :
  282. return MemConst::GENDER_N;
  283. }
  284. }
  285. /**头像处理,修改为https
  286. *
  287. * @param string $avatar
  288. *
  289. * @return string|string[]|null
  290. */
  291. private function getAvatar($avatar = '') {
  292. $_avatar = $avatar;
  293. if ('http' == parse_url($_avatar, PHP_URL_SCHEME)) {
  294. $_avatar = preg_replace("/^http/", "https", $_avatar);
  295. }
  296. return $_avatar;
  297. }
  298. }