<?php
/**
 * Created by PhpStorm.
 * User: stanley-king
 * Date: 2017/6/20
 * Time: 上午10:25
 */

namespace thrid_author;

use Log;

class signaturer
{
    const appid = 'wx6b42e00ecaade538';
    const appsecret ='ee64233b3144d76217161666f8cb4c86';

    const access_token_url = "https://api.weixin.qq.com/cgi-bin/token";
    const ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";

    const prefix = 'signaturer';
    const try_sleep = 5;

    private $mData;
    private static $stInstance = null;

    public static function instance()
    {
        if(self::$stInstance == null) {
            self::$stInstance = new signaturer();
        }
        return self::$stInstance;
    }

    private function __construct()
    {
        $this->mData = [];
    }

    public function signurl($sign_url)
    {
        $ticket = $this->rjsapi_ticket();
        if($ticket == false) {
            Log::record(__METHOD__ . " sign error",Log::ERR);
            return false;
        }

        $timestamp = time();
        $nonceStr = $this->noncestr();
        $string = "jsapi_ticket={$ticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$sign_url}";
        $signature = sha1($string);
        Log::record(__METHOD__ . " signurl={$sign_url} noncestr={$nonceStr} timestamp={$timestamp} ticket={$ticket} signature={$signature}",Log::DEBUG);

        return ["appid" => signaturer::appid,"noncestr" => $nonceStr,"timestamp" => $timestamp,"signature" => $signature];
    }

    private function noncestr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    private function rjsapi_ticket()
    {
        if(empty($this->mData) || empty($this->mData['jsapi_ticket'])) {
            $fReadCache = true;
        }
        else
        {
            $expires = $this->mData['jsapi_ticket']['expires'];
            if(time() >= $expires) {
                $fReadCache = true;
            } else {
                $fReadCache = false;
            }
        }

        if($fReadCache)
        {
            $items = rcache('jsapi_ticket',self::prefix);
            if(empty($items)) {
                $this->mData['jsapi_ticket'] = $this->request();
            }
            else
            {
                $expires = intval($items['expires']);
                if($expires <= time()) {
                    $this->mData['jsapi_ticket'] = $this->request();
                } else {
                    $this->mData['jsapi_ticket'] = $items;
                }
            }
        }

        return $this->mData['jsapi_ticket']['ticket'];
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public function request()
    {
        while (true)
        {
            $token = $this->access_token();
            if($token != false) {
                break;
            } else {
                sleep(signaturer::try_sleep);
            }
        }

        $accessToken = $token['token'];
        while (true)
        {
            $ticket = $this->jsapi_ticket($accessToken);
            if($ticket != false) {
                break;
            }
            else {
                sleep(signaturer::try_sleep);
            }
        }
        $ticket['expires'] = intval($ticket['expires'] / 2 + time());
        wcache('jsapi_ticket',$ticket,self::prefix);
        Log::record(__METHOD__ . " ticket={$ticket['ticket']}",Log::DEBUG);
        return $ticket;
    }

    private function jsapi_ticket($accessToken)
    {
        $params = ['type' => 'jsapi','access_token' => $accessToken];
        $res = http_request(self::ticket_url,$params);
        if($res == false) return false;

        $res = json_decode($res,true);
        if($res['errcode'] != 0) {
            Log::record("jsapi_ticket error : code={$res['errcode']} msg={$res['errmsg']}",Log::ERR);
            return false;
        }

        $ticket['expires'] = intval($res['expires_in']);
        $ticket['ticket'] = $res['ticket'];

        return $ticket;
    }

    private function access_token()
    {
        $params = ['grant_type' => 'client_credential','appid' => signaturer::appid,'secret' => signaturer::appsecret];
        $res = http_request(self::access_token_url,$params);
        if($res == false) return false;

        $res = json_decode($res,true);
        if(array_key_exists('errcode',$res)) {
            $code = $res['errcode'];
            $msg  = $res['errmsg'];
            Log::record("signurl access_token error code={$code} msg={$msg}",Log::DEBUG);
            return false;
        }
        else {
            $token['expires'] = intval($res['expires_in']);
            $token['token'] = $res['access_token'];
            return $token;
        }
    }
}