<?php
namespace Common\Logic\Wechat;

use Think\Log;

/**
 * 微信网页接口类
 * 
 * 微信网页开发的接口
 * 
 * @see https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
 */
class WechatWebApi
{
    
    /**
     * 网页授权的地址
     * @var string
     */ 
    const OAUTH2_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect';
    
    /**
     * 网页授权获取access_token的地址
     * @var string
     */
    const OAUTH2_ACCESS_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code';
    
    /**
     * 网页授权后获取用户信息的地址
     * @var unknown
     */
    const USER_INFO_URL = 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN ';
    
    /**
     * 获取token的url
     * @var string
     */
    const TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';
    
    /**
     * 获取js ticket的请求url
     * @var string
     */
    const JS_TICKET_URL = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi';
    
    /**
     * 实例
     * @var object
     */
    protected static $instance;
    
    /**
     * @var string
     */
    protected $appId = '';
    
    /**
     * @var string
     */
    protected $appSecret = '';
    
    /**
     * 错误信息
     * @var array
     */
    protected $error = array();

    /**
     * __construct
     */
    protected function __construct()
    {
        $this->appId        = C('APPID');
        $this->appSecret    = C('APP_SECRET');
    }
    
    /**
     * 获取实例
     * @return \Common\Logic\Wechat\WechatApi
     */
    public static function getInstance()
    {
        return self::$instance ? self::$instance : self::$instance = new WechatWebApi();
    }
    
    public function getAppId()
    {
        return $this->appId;
    }
    
    /**
     * 获取js ticket的请求url
     * @param string $token
     * @return string
     */
    public function getJsTicketUrl($token)
    {
        return sprintf(self::JS_TICKET_URL, $token);
    }
    
    /**
     * 获取js ticket
     * @param string $token
     * @return boolean|array
     */
    public function getJsTicket($token)
    {
        $url = $this->getJsTicketUrl($token);
        
        $curl = new Curl();
        $rst = $curl->setUrl($url)->setHttps()->request()->result;
        $rst_arr = json_decode($rst, true);
        
        if (!is_array($rst_arr) || empty($rst_arr) || !array_key_exists('ticket', $rst_arr)) {
            $this->setError('get js ticket error:'.$rst);
            return false;
        }
        
        return $rst_arr;
    }
    
    /**
     * 获取token的请求url
     * @return string
     */
    public function getTokenUrl()
    {
        return sprintf(self::TOKEN_URL, $this->appId, $this->appSecret);
    }
    
    public function getToken()
    {
        $url = $this->getTokenUrl();
        
        $curl = new Curl();
        $rst = $curl->setUrl($url)->setHttps()->request()->result;
        $rst_arr = json_decode($rst, true);
        
        if (!is_array($rst_arr) || empty($rst_arr) || !array_key_exists('access_token', $rst_arr)) {
            $this->setError('get token error:'.$rst);
            return false;
        }
        
        return $rst_arr;
    }
    
    /**
     * 获取授权地址
     * 
     * @param string 授权后返回的url
     * @param string $scope
     * @param string $state
     * 
     * @return string|boolean 失败返回false否则返回url
     */
    public function getOauthUrl($url, $scope = 'snsapi_base', $state = '')
    {
        if (empty($this->appId)) {
            $this->setError('appId is empty');
            return false;
        }
        
        return sprintf(self::OAUTH2_URL, $this->appId, urlencode($url), $scope, $state);
    }
    
    /**
     * 获取code换取access_token的地址
     *
     * @param string 授权后返回的url
     * @param string $scope
     * @param string $state
     *
     * @return string|boolean 失败返回false否则返回url
     */
    public function getAccessTokenUrl($code)
    {
        if (empty($this->appId) || empty($this->appSecret)) {
            $this->setError('appId or appSecret is empty');
            return false;
        }
    
        return sprintf(self::OAUTH2_ACCESS_URL, $this->appId, $this->appSecret, $code);
    }
    
    /**
     * 获取授权access_token
     * @param string code
     * @return array|boolean 失败返回false，否则返回access_token数组
     */
    public function getOauthAccess($code)
    {
        $url = $this->getAccessTokenUrl($code);
        if (!$url) return false;
        
        $curl = new Curl();
        $rst = $curl->setUrl($url)->setHttps()->request()->result;
        $rst_arr = json_decode($rst, true);
        
        if (!is_array($rst_arr) || empty($rst_arr) || !array_key_exists('access_token', $rst_arr)) {
            $this->setError('get access token error:'.$rst);
            return false;
        }
        
        return $rst_arr;
    }
    
    /**
     * 获取错误信息
     * @return array
     */
    public function getError()
    {
        return $this->error;
    }
    
    /**
     * 设置错误信息
     * @param string $msg
     */
    protected function setError($msg)
    {
        $this->error[] = $msg;
        Log::write($msg);
    }
    
    /**
     * 获取用户信息的地址
     * @param string access_token
     * @param string openid
     * @return boolean|string
     */
    public function userInfoUrl($access_token, $openid)
    {
        if (empty($access_token) || empty($openid)) {
            $this->setError('access token or openid is empty');
            return false;
        }
        
        return sprintf(self::USER_INFO_URL, $access_token, $openid);
    }
    
    /**
     * 获取用户信息
     * @param string access_token
     * @param string openid
     * @return boolean|array
     */
    public function getUserInfo($access_token, $openid)
    {
        $url = $this->userInfoUrl($access_token, $openid);
        if (!$url) return false;
        
        $curl = new Curl();
        $rst = $curl->setUrl($url)->setHttps()->request()->result;
        $rst_arr = json_decode($rst, true);
        
        if (!is_array($rst_arr) || empty($rst_arr) || !array_key_exists('openid', $rst_arr)) {
            $this->setError('get user info error:'.var_export($rst, true));
            return false;
        }
        
        return $rst_arr;
    }
}
