env = 'd'; } elseif (false !== strpos($_SERVER['HTTP_HOST'], 'api.ss.haodian.cn')) {//test 测试 $this->env = 't'; } else { // 正式 $this->env = 'p'; } if ('p' != $this->env) { $this->url_wechat = 'https://sapi.liche.cn/weixin'; } $this->redis = &load_cache('redis'); $param && $this->init($param); } /** * 初始化 * @param $param */ function init($param) { $this->appid = $param['appid']; $this->secret = $param['secret']; $this->log_dir = "libraries_wechat_{$this->appid}"; } /** * 获取或者重置access_token * @param $reset (是否重置) * @return mixed */ function access_token($reset = false) { $redis = $this->redis; $key = "wechat_access_token_{$this->appid}"; debug_log("[info]: appid={$this->appid}", __FUNCTION__, $this->log_dir); $func = __FUNCTION__; if (!$reset) {//不重置 if ($this->access_token) { $access_token = $this->access_token; } elseif ($this->url_wechat) {//从线上获取access_token $url = $this->url_wechat . "/{$func}?appid={$this->appid}&secret={$this->secret}"; $res = $this->http_get($url); $ret = json_decode($res, true); $access_token = $ret['access_token']; if (!$access_token) { debug_log("[warning]# res={$res}; url={$url}", __FUNCTION__, $this->log_dir); } } else {//缓存中获取 $access_token = $redis->get($key); } if ($access_token) { return $access_token; } } //重置access_token,第三方的不处理 if ($this->url_wechat) { $url = $this->url_wechat . "/{$func}?appid={$this->appid}&secret={$this->secret}&reset=1"; } else { $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->secret}"; } $res = $this->http_get($url); $ret = json_decode($res, true); $access_token = $ret['access_token']; if ($access_token) { //线上环境才缓存 'p' == $this->env && $redis->save($key, $access_token, 7000); } else { debug_log("[warning]# res={$res}; url={$url}", __FUNCTION__, $this->log_dir); } $this->access_token = $access_token; return $access_token; } /** * 分享票据 * @param bool $reset "是否重置" * @return mixed */ function api_ticket($reset = false) { $redis = $this->redis; $key = 'wechat_jsApiTicket_' . $this->appid; $func = __FUNCTION__; if (!$reset) {//不重置 if ($this->api_ticket) { $api_ticket = $this->api_ticket; } elseif ($this->url_wechat) {//从线上获取ticket $url = $this->url_wechat . "/{$func}?appid={$this->appid}&secret={$this->secret}"; $res = $this->http_get($url); $ret = json_decode($res, true); $api_ticket = $ret['api_ticket']; if (!$api_ticket) { debug_log("[warning]# res={$res}; url={$url}", __FUNCTION__, $this->log_dir); } } else {//缓存中获取 $api_ticket = $redis->get($key); } if ($api_ticket) { return $api_ticket; } } //重置 if ($this->url_wechat) {//从线上重置获取ticket $url = $this->url_wechat . "/{$func}?appid={$this->appid}&secret={$this->secret}&reset=1"; $res = $this->http_get($url); $ret = json_decode($res, true); } else { $access_token = $this->access_token(); $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={$access_token}"; $res = $this->http_get($url); $ret = json_decode($res, true); if (!$ret['ticket']) { debug_log("[warning]# res={$res}; url={$url}", __FUNCTION__, $this->log_dir); //可能是access_token过期,重置获取一下 $access_token = $this->access_token(true); $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={$access_token}"; $res = $this->http_get($url); $ret = json_decode($res, true); } } $api_ticket = $ret['ticket']; if ($api_ticket) { //线上环境才缓存 'p' == $this->env && $redis->save($key, $api_ticket, 7000); } else { debug_log("[warning]# res={$res}; url={$url}", __FUNCTION__, $this->log_dir); } $this->api_ticket = $api_ticket; return $api_ticket; } /** * 生成二维码 * @param $filename (文件名称) * @param $scene (最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) * @param $page (必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面) * @param $width (二维码的宽度,单位 px,最小 280px,最大 1280px,默认430px) * @return array {'file':'文件路径', 'url':'访问相对路径'} */ function qrcode($filename, $scene, $page, $width) { $access_token = $this->access_token(); if (!$access_token) { debug_log("[error] " . __FUNCTION__ . ": not access_token", __FUNCTION__, $this->log_dir); return ['code' => 0, 'msg' => '获取微信token失败']; } $pre_url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token='; $url = $pre_url . $access_token; $data = array( 'page' => $page, 'width' => $width ? $width : 430, ); $scene && $data['scene'] = $scene; list($code, $res) = $this->http_post($url, $data); $ret = json_decode($res, true); if (isset($ret['errcode']) && 40001 == $ret['errcode']) {//token过期,重置后请求 $url = $pre_url . $this->access_token(true); list($code, $res) = $this->http_post($url, $data); $ret = json_decode($res, true); } if (isset($ret['errcode'])) { debug_log("[error] " . __FUNCTION__ . ": httpcode:{$code}, response:{$res}, filename:{$filename}, scene:{$scene}, page:{$page}, width:{$width}", __FUNCTION__, $this->log_dir); return ['code' => 0, 'msg' => $res['errcode']]; } if ($filename) { //保存图片 $file = APPPATH . '../www/api/wx/' . $filename . '.png'; $dir = substr($file, 0, strrpos($file, '/')); if (!is_dir($dir)) { $ret = mkdir($dir, 0777, true);// 如果文件夹不存在,将以递归方式创建该文件夹 if (!$ret) { debug_log("[error] " . __FUNCTION__ . ": mkdir {$ret}, filename:{$filename}, scene:{$scene}, page:{$page}, width:{$width}", __FUNCTION__, $this->log_dir); return ['code' => 0, 'msg' => '创建文件夹失败']; } } if (file_exists($file)) { return array('code' => 1, 'file' => $file, 'url' => 'wx/' . $filename . '.png'); } $ret = file_put_contents($file, $res); if (false === $ret) { debug_log("[error] " . __FUNCTION__ . ": file_put_contents {$ret}, filename:{$filename}, scene:{$scene}, page:{$page}, width:{$width}", __FUNCTION__, $this->log_dir); return ['code' => 0, 'msg' => '保存图片失败']; } return ['code' => 1, 'file' => $file, 'url' => 'wx/' . $filename . '.png']; } else { //不保存图片 return ['code' => 1, 'file' => $res]; } } /** * @param $url * @return mixed */ private function http_get($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 500); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true); curl_setopt($curl, CURLOPT_URL, $url); $res = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if (200 != $code) { $size = strlen($res); if ($size > 5000) { debug_log("[info]# httpcode={$code}, response is big only show size={$size}", __FUNCTION__, $this->log_dir); } else { debug_log("[info]# httpcode={$code}, response={$res}", __FUNCTION__, $this->log_dir); } return ""; } return $res; } /** * @param $url * @param $data * @return array */ private function http_post($url, $data) { debug_log("[info] " . __FUNCTION__ . ":url={$url}, data=" . json_encode($data), __FUNCTION__, $this->log_dir); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $res = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $size = strlen($res); if ($size > 5000) { debug_log("[info] " . __FUNCTION__ . ":httpcode={$code}, response is big only show size={$size}", __FUNCTION__, $this->log_dir); } else { debug_log("[info] " . __FUNCTION__ . ":httpcode={$code}, response={$res}", __FUNCTION__, $this->log_dir); } return array($code, $res); } }