<?php
/**
 * 加解密类
 */
namespace encrypter;

class Encrypter {

    /**
     * 加密函数
     * @param  mixed   $data   加密数据
     * @param  string  $key    密匙
     * @param  integer $expire 失效时间
     * @return string
     */
    public static function cpEncode($data, $key='', $expire = 0){
        $string = serialize($data);
        $ckey_length = 4;
        $key = md5($key);
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = substr(md5(microtime()), -$ckey_length);

        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);
        
        $string =  sprintf('%010d', $expire ? $expire + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        return $keyc.str_replace('=', '', base64_encode($result));      
    }

    /**
     * 解密函数
     * @param  string $string 加密字符串
     * @param  string $key    密匙
     * @return mixed
     */
    public static function cpDecode($string,$key=''){
        $ckey_length = 4;
        $key  = md5($key);
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = substr($string, 0, $ckey_length);
        
        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);
        
        $string =  base64_decode(substr($string, $ckey_length));
        $string_length = strlen($string);
        
        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
            return unserialize(substr($result, 26));
        }else{
            return '';
        }   
    }

    /**
     * 验证是否符合uuid规范
     */
    public static function isId($uuid) {
        return preg_match("/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/", $uuid);
    }

	/**
	 * 生成UUID
	 * @param  string $value 待解密字符串
	 * @return string
	 */
	public static function getId() {
        if (function_exists('com_create_guid')) {
            $id = strtolower(trim(com_create_guid(), '{}'));
        } else {
            $id = self::createId();
        }
        return $id;
    }

    /**
     * 创建UUID
     * @return string
     */
    protected function createId() {
        $salt = substr(hash('sha256', microtime(true) . mt_rand()), 0, 64);
        $hex  = substr(hash('sha256', $salt), 0, 32);
        $time_low = substr($hex, 0, 8);
        $time_mid = substr($hex, 8, 4);
        $time_hi_and_version = base_convert(substr($hex, 12, 4), 16, 10);
        $time_hi_and_version &= 0x0FFF;
        $time_hi_and_version |= (4 << 12);
        $clock_seq_hi_and_reserved = base_convert(substr($hex, 16, 4), 16, 10);
        $clock_seq_hi_and_reserved &= 0x3F;
        $clock_seq_hi_and_reserved |= 0x80;
        $clock_seq_low = substr($hex, 20, 2);
        $nodes = substr($hex, 20);
        return sprintf('%s-%s-%04x-%02x%02x-%s',$time_low, $time_mid,$time_hi_and_version, $clock_seq_hi_and_reserved,$clock_seq_low, $nodes);
    }
}
