<?php
/**
* 得到当前时间
*
* @return string
*/
function getNowTime(){
	return date('Y-m-d H:i:s');
}

/**
* 得到当前日期
*
* @return string
*/
function getNowDate(){
	return date('Y-m-d');
}

/**
* 显示短日期
*
* @return string
*/
function shortDate($datetime){
	return date('Y-m-d',strtotime($datetime));
}

/**
* 显示格式化后的小数
*
* @param decimal $value
* @return decimal
*/
function decimal($value){
	return sprintf("%.2f",$value);
}

function curl(array $params){
	//$params=>[url,data,dataType,dataCharset,header,timeout,auth=>[type,username,password]]
	$url = trim($params['url']);
	$data = empty($params['data']) ? [] : $params['data'];
	$dataType = empty($params['dataType']) ? 'post' : strtolower($params['dataType']);
	$dataCharset = empty($params['dataCharset']) ? 'utf-8' : strtolower($params['dataCharset']);
	$headerArr = empty($params['header']) ? [] : $params['header'];
	$timeout = empty($params['timeout']) ? 120 : $params['timeout'];
	//url 与 get数据处理
	if($dataType=='get' && !empty($data)){
		if(strpos($url,'?')===false){
			$url = $url.'?'.http_build_query($data);
		}else{
			$url = $url.'&'.http_build_query($data);
		}
	}
	//初始化curl
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
	if($dataType=='json'){
		$data = json_encode($data);
		$headerArr = array_merge($headerArr,[
			'Content-Type: application/json',
			//'Content-Type: application/json; charset='.$dataCharset,
			//'Content-Length:' . strlen($data)
		]);
		//curl_setopt($ch, CURLOPT_HEADER, 0);
	}
	//AUTH
	if(!empty($params['auth'])){
		$headerArr = array_merge($headerArr,[
			"Authorization: Basic ".base64_encode($params['auth']['username'].":".$params['auth']['password'])
		]);
		/*
		switch(strtoupper($params['auth']['type'])){
			case 'BASIC':
				curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
				break;
			default:
				curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
		}
		curl_setopt($ch, CURLOPT_USERPWD, $params['auth']['username'].':'.$params['auth']['password']);
		*/
	}
	//设置请求头
	if(!empty($headerArr)){
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
	}
	//post,json 数据处理 
	if(!empty($data) && $dataType!='get'){
		curl_setopt($ch, CURLOPT_POST, true);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
	}
	//SSL
	$ssl = substr($url, 0, 8) == "https://" ? true : false;
	if($ssl){
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
	}
	//
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	//Exec
	$status = true;
	$content = curl_exec($ch);
	$code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
	curl_close($ch);
	//异常处理
	if($content===false){
		$status = false;
		$content = curl_error($ch);
	}
	if($code!='200'){
		$status = false;
		$content = preg_replace('/<script>.*<\/script>/s', '', $content);
		$content = preg_replace('/<style>.*<\/style>/s', '', $content);
		$content = strip_tags($content);
		$content = str_replace("\r\n", ', ', trim($content));
		if(strlen($content)>1024){
			$content = 'curl:'.substr($content,0,1020).'...';
		}
	}
	//返回结果
	return ['status'=>$status,'code'=>$code,'content'=>$content];
}
/**
* 将字符串转成不可逆密文
*
* md5的加强版
*
* 自定义盐 \mvc::$cfg['safe']['passwordSalt'] 即使为空系统也会加内置盐
*
* 自定义盐为空或不存在盐配置时
*
* 1234456 = 0f903b04ebcd241544d83ba497ad1ee6
*
* Pass.123456 = cdb2fe5bf2f12844a5d6e95d9b5ea1b6
* @param string $str
* @return string 32位小写字符串
*/
function password($str){
	//2020/4/18
	if(empty($str) || strlen($str)<6){
		throw new \Exception ( lg('密码类转密文至少要求提供6个字符') );
	}
	$salt = '$7%8C3f&Y0h:Ma';
	if(isset(\mvc::$cfg['safe']) && isset(\mvc::$cfg['safe']['passwordSalt'])) $salt .= \mvc::$cfg['safe']['passwordSalt'];
	$result = strtolower(md5(substr(md5($str[0].$str[5].'Q.G.M.V.C'),3,21).md5($str[1].$str.$str[3]).md5($salt.$str[2])));
	$result =  strrev(substr($result,24,8).substr($result,8,8).substr($result,0,8).substr($result,16,8));
	$i1 = (ord($str[0]) + ord($str[5])) % 32;
	$i2 = ($i1 + ord($str[3])) % 32;
	if($i1==$i2) $i2 = ($i2 + 5) % 32;
	$c = $result[$i1]; $result[$i1] = $result[$i2]; $result[$i2] = $c;
	return strrev(md5(substr(md5($salt.'q-g-m-v-c'),7,27).sha1($result).md5('q;g;m;v;c'.$salt)));
}
/**
* 生成唯一ID,字符串, 最长11位字符
*
* 返回小写10-11位长度可排序唯一ID，如需要存数据库可以使用 char(11)
* @param string $isSerial 是否生成可排序的唯一ID，默认false
* @return string
*/
function guid(){
	$prefix = base_convert(getUnixTimestamp(),10,36);
	list($usec, $sec) = explode(" ", microtime());
	$guid = uniqid(rand(0,PHP_INT_MAX),true)
				. $sec . $usec
				. serialize($_SERVER)
				. serialize($_REQUEST)
				. mt_rand(0,PHP_INT_MAX)
				. random_int(0,PHP_INT_MAX);
	$arr = shortStrUid($guid,1);
	return $prefix.$arr[1].$arr[2];
}
/**
* 生成唯一ID,整数, 最大17位数字
*
* 返回最大17位数字，如需要存数据库，可以使用 bigint 来存储
* @return int
*/
function guidInteger(){
	return base_convert(guid(),36,10) * 1;
}
/**
* 获取毫秒级时间戳 13位整数
*
* @return int
*/
function getUnixTimestamp(){
    list($s1, $s2) = explode(' ', microtime());
    return (float)sprintf('%.0f',(floatval($s1) + floatval($s2)) * 1000);
}
/**
* 将一个字条串生成一个短标识，类似短网址
*
* 返回4组结果,可任意取一组
* 如果要给数据库记录做短标记，可用这4组去判断有没有重复
* @return array
*/
function shortStrUid($str, $length=6) {
	$base32 = array (
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
			'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
			'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
			'y', 'z', '0', '1', '2', '3', '4', '5'
	);
	$hex = md5($str);
	$subHexLen = 4; // 划分为4段，保留4组结果
	$resultArray = [];
	for ($i = 0; $i < $subHexLen; $i++) {
		$subHex = substr($hex, $i * 8, 8);    // 分成4段，每段取8位字符
		$int = 0x3FFFFFFF & hexdec($subHex);  // 8位字节与16进制取与操作
		$out = '';
		for ($j = 0; $j < $length; $j++) {
			$val = $int % 32;   //取0~31之间的整数
			$out .= $base32[$val]; //从数组中获取对应字符
			$int = $int >> (30/$length);
		}
		$resultArray[] = $out;
	}
	return $resultArray;
}
/**
* md4 md5中间的16位
* @return array
*/
function md4($str) {
	return substr(md5($str),8,16);
}

/**
 * 将网址 ？ 后的内容转成数组
 *
 * @param string $url 网址
 * @return array
 */
function urlQuery2Array($url){
	$arr = parse_url($url);
	$query = $arr['query'];
	$arr = explode('&', $query);
	$result = [];
	foreach ($arr as $k => $v) {
		$arr = explode('=', $v);
		$result[$arr[0]] = $arr[1];
	}
	return $result;
}


/**
* 格式化打印变量,并中断程序执行
*
* 在前端带缩进格式显示,并以JSON格式输出到浏览器控制台
* @param mixed $args 待打印的变量,可变参数，支持多个变量
* @return void
*/
function put(...$args){
	return call_user_func_array('dumpResult',$args);
}

function putCli(...$args){
	echo call_user_func_array('dumpResultCli',$args);
}

function dumpResult(...$args){
	$result = '';
	foreach($args as $index => $v){
		$result .= _dump($v,$index);
	}
	return $result;
}

function dumpResultCli(...$args){
	$result = '';
	foreach($args as $v){
		$result .=  print_r($v,true);
		$result .=  "\r\n";
	}
	return $result;
}

function _dump($variable,$index){
	$json = json_encode($variable);
	$backtrace = debug_backtrace();
    $i=1;
	$caller = $backtrace[$i];
    while(strpos($caller['file'],'function.php')>0){
        $i++;
        $caller = $backtrace[$i];
    }
	$fileArr = file($caller['file']);
	$code = trim($fileArr[$caller['line']-1]);
	$t = explode(',',$code);
    if(isset($t[$index])) $t[$index] = preg_replace('/(\$\w+)/',"<span class='dd-call-active'>$1</span>",$t[$index]);
    $code = implode(',',$t);
	unset($fileArr);
	$result = trim("<script>console.log($json);</script>
			<style>
				.dd pre{line-height: 1.5em;font-size:16px;font-family: monaco, Consolas, Menlo, Courier, monospace;padding:0;margin:0}
				.dd pre .dd-blue{color:blue}
				.dd pre .dd-to{color:#ccc}
				.dd pre .dd-num{color:#F00}
				.dd pre .dd-string{color:#690}
				.dd pre .dd-array{color:#990}
				.dd pre .dd-object{color:#C0C}
				.dd pre .dd-object-val{color:#000;font-size:11px;line-height:15px}
				.dd pre .dd-stdClass{color:#C00;font-weight:bold}
				.dd pre .dd-file{color:#ccc}
				.dd pre .dd-call{color:#609;font-weight:bold}
				.dd pre .dd-call-active{color:#f00}
				.dd pre .dd-code{color:#000;font-size:10px}
				.dd pre .dd-area-1{color:#f00;font-weight:bold}
				.dd pre .dd-area-2{color:#090;font-weight:bold}
				.dd pre .dd-area-3{color:#00f;font-weight:bold}
				.dd pre .dd-area-4{color:#F0F;font-weight:bold}
			</style>
			<div class='dd' style='background-color:#fffffe;
				color:#000;
				border: 1px #ddd solid;
				margin:10px 0;
				padding:0 10px;'>
			<pre style='max-height:auto;overflow:auto'>
	");
	$result .= '<span class="dd-call">'.$code.'</span>';
	$result .= '<span class="dd-file">'.$caller['file'].'('.$caller['line'].')</span>'.PHP_EOL;
	$result .= _print_r($variable);
	$result .= PHP_EOL .'</pre></div>' .PHP_EOL;
	return $result;
}
/**
* 自定义对象格式化输出 print_r
* @param mixed $variable 待打印的变量
* @return string
* @ignore
*/
function _print_r($variable,$tab=1){
	if(is_object($variable) && !($variable instanceOf Closure)){
		$arr = json_decode(json_encode($variable),true);
		if(!empty($arr)){
			$variable = $arr;
		}
	}
	if(!is_array($variable)){
		return _echoValueByType($variable);
	}
	$result = '';
	$result .= '<span class="dd-area-'.$tab.'">[</span>'.PHP_EOL;
	$reg=0;
	foreach($variable as $k=>$v){
		for($i=0;$i<$tab;$i++){
			$result .= "    ";
		}
		if(is_array($v)){
			$result .= '"<span class="dd-array">'.$k.'</span>" => ';
			$result .= _print_r($v,$tab+1);
		}else if(is_object($v)){
			$result .= '"<span class="dd-object">'.$k.'</span>" => ';
			$result .= _print_r($v,$tab+1);
		}else{
			$result .= '"'.$k.'" <span class="dd-to">=></span> ';
			$result .= _echoValueByType($v);
		}
		if($reg!=count($variable)-1) $result .= ',';
		$result .= PHP_EOL;
		$reg++;
	}
	for($i=0;$i<$tab-1;$i++){
		$result .= "    ";
	}
	$result .= '<span class="dd-area-'.$tab.'">]</span>';
	return $result;
}
/**
* 按值的类型输出对应的颜色
* @param mixed $value
* @return string
* @ignore
*/
function _echoValueByType($value){
	$type = gettype($value);
	if(is_null($value)){
		$cssClass = "dd-blue";
		$value = 'null';
	}else if(is_bool($value)){
		$cssClass = "dd-blue";
		$value = $value ? 'true' : 'false';
	}else if(is_float($value) || is_integer($value)){
		$cssClass = "dd-num";
	}else if(is_string($value) && substr($value,0,10)=='{stdClass:'){
		$cssClass = "dd-stdclass";
	}else if(is_string($value) && in_array(strtoupper(explode(' ',$value)[0]),['SELECT','INSERT','UPDATE','DELETE'])){
		$cssClass = "";
        $value = \SqlFormatter::format($value);
	}else if(is_string($value)){
		$cssClass = "dd-string";
		$value = '"'.htmlspecialchars($value).'" ('.strlen($value).')';
	}else if($value instanceOf Closure){
		$cssClass = "dd-code";
		$value = phpCodeColor(closureToString($value));
	}else if(is_object($value)){
		$cssClass = "dd-object-val";
		$json = json_encode($value);
		if($json=='{}'){
            try{
                $value = print_r($value,true);
            }catch(\Throwable | \Exception $e){
                dd(time());
                $cssClass = "dd-stdClass";
                $class = new ReflectionClass($value);
                $value = "new {$class->name}()";
            }
		}else{
			$value = $json;
		}
	}else{
        $cssClass = '';
    }
	return '<span class="'.$cssClass.'">'.$value.'</span>';
}

/**
* php代码变色
* @param string $code_string 代码行
* @return string 变色后的字符串
*/
function phpCodeColor($code_string){
	$code_string = trim($code_string);
	$code_string = preg_replace('/(\/\*[\s\S]*\*\/)/U', '<span style="color:green">$1</span>', $code_string); //注释变色
	$code_string = preg_replace('/\/\/(.*)?\n/', '<span style="color:green">//$1</span>', $code_string); //注释变色
	$code_string = preg_replace("/(\'([^\']+)?\')/s", '<span style="color:#FF00FF">$1</span>', $code_string); //单引号
	$code_string = preg_replace('/(\$[\w]+)/', '<span style="color:#008080">$1</span>', $code_string); //变量变色
	$code_string = preg_replace('/([{|}|(|)])/', '<span style="font-weight:bold;color:#800;padding-right:2px">$1</span>', $code_string); //{}()加粗
	$code_string = preg_replace('/(\\\\\w+)/', '<span style="color:#C00">$1</span>', $code_string); //命名空间引用
	//关键字
	$keywords = [
		'new','function','private','public','static','as','namespace','use','class','extends',
		'foreach','for','while','if','else','namespace','return','echo',
		'require','require_once','include','include_once','isset','empty','is_file','method_exists'
	];
	foreach($keywords as $key){
		$partern = '/\b'.$key.'\b[\s]*/i';
		$code_string = preg_replace($partern,'<span style="color:blue">'.$key.' </span>',$code_string);
	}
	//函数
	$functions = [
		'ReflectionClass','print_r','var_dump','dump','dd',
	];
	foreach($functions as $func){
		$partern = '/\b'.$func.'\b[\s]*/i';
		$code_string = preg_replace($partern,'<span style="color:red">'.$func.' </span>',$code_string);
	}
	return $code_string;
}

/**
* 创建新版本号
* @return string 16位
*/	
function newVer()
{
	return strtoupper(md4(guid()));
}
