<?php
include 'lib/vendor/autoload.php';
use Overtrue\Pinyin\Pinyin;

/**
  * @name LibFc(公共函数类)
  * @author Soul
  * @copyright 2013-3-21
  */
class LibFc
{
	/**
	  * @name Escape(编码特殊字符)
	  * @remark  编码特殊字符
	  * @exp 	Escape("a'b")
	  * @param  $Data    string  需要处理的字符串
	  * @param  $Filter    string  是否过来敏感词语
	  * @author Soul
	  * @copyright 2013-3-18
	  * @return 编码后的字符串
	  */
	static function Escape($Data, $Filter = false)
	{
		if($Filter)
		{//敏感词语
			$FilterArr = include('lib/Filter.php');
			$Data = str_replace($FilterArr, '***', $Data);
		}

		$TrimHtmlTag = 'script|i?frame|style|html|body|title|link|meta';
		$FindArr = array(
			sprintf("/<(%s)([^>]*?)>/isU", $TrimHtmlTag),
			sprintf("/<\/(%s)>/isU", $TrimHtmlTag)
		);
		$ToArr = array('&lt;$1$2&gt;', '&lt;/$1&gt;');
		$Data = preg_replace($FindArr, $ToArr, $Data);

		$ReplaceArr = array('\\'=>'\\\\', "'"=>"''");
		return trim(strtr($Data, $ReplaceArr));
	}

	/**
	  * @name Int(数据是否为整型)
	  * @remark 数据是否为整型
	  * @exp Int("123")
	  * @param  $Data  string 需要处理的字符串
	  * @author Soul
	  * @copyright 2013-3-26
	  * @return
	  		是整形：整数
			非整形：false
	  */
	static function Int($Data)
	{
		$Data = trim($Data);
		if($Data != '' && preg_match('/^[-+]?\d+$/', $Data, $Matches))
		{
			return $Matches[0];
		}
		return false;
	}



	/**
	  * @name Float(数据是否为浮点型)
	  * @remark  数据是否为浮点型
	  * @exp  Float("123.2312")
	  * @param  $data     string 需要处理的字符串
	  * @param  $Precision    int 十进制小数点后数字的数目-进行四舍五入的结果
	  * @author Soul
	  * @copyright 2013-3-26
	  * @return
	  		是整形：浮点数
			非整形：false
	  */	
	static function Float($Data, $Precision=2)
	{
		$Precision = self::Int($Precision);
		if($Precision >= 0)
		{
			if(preg_match('/^([-+]?\d+)\.?\d*$/', trim($Data), $Matches))
			{
				return round($Matches[0], $Precision);
			}
		}
		return false;
	}

	/**
     * 字符串截取，支持中文和其他编码
     * @static
     * @access public
     * @param string $str 需要转换的字符串
     * @param string $start 开始位置
     * @param string $length 截取长度
     * @param string $charset 编码格式
     * @param string $suffix 截断显示字符
     * @return string
     */
    static function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true) {
        if(function_exists("mb_substr"))
            $slice = mb_substr($str, $start, $length, $charset);
        elseif(function_exists('iconv_substr')) {
            $slice = iconv_substr($str,$start,$length,$charset);
        }else{
            $re['utf-8']   = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
            $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
            $re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
            $re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
            preg_match_all($re[$charset], $str, $match);
            $slice = join("",array_slice($match[0], $start, $length));
        }
        return $suffix && $str != $slice? $slice.'...' : $slice;
    }
	

	/**
	  * @name ReturnData(接口返回数据)
	  * @remark 接口返回数据
	  * @exp
	  		ReturnData() 返回false、错误代码0、无错误信息
			ReturnData(true, array(1,2,3)) 返回true、数据为array(1,2,3)
	  * @param  $Status     bool  true| 默认false [可选]
	  * @param  $Data     all  所有类型 默认空 [可选]
	  * @param  $Code     int  错误代码，默认0:无错误代码 [可选]
	  * @author Soul
	  * @copyright 2013-3-30
	  * @return
	  			返回值：array('status'=>false, 'data'=>'错误信息', 'code'=>0);
				array('status'=>true, 'data'=>array(1,2,3,4), 'code'=>0);
	  */
	static function ReturnData($Status=false, $Data='', $Code=0)
	{
		$Status = $Status == true? true:false;
		if(!$Status) $_SESSION['MAIN_MONITOR']['tj'] = 0;
		if($Status)
		{
			$Code = 0;
		}
		else
		{
			$Code = self::Int($Code);
			if($Code === false)
			{
				$Code = 0;
			}
		}
		$Arr = ['status'=>$Status, 'data'=>$Data, 'code'=>$Code];
		if($Status)
		{
			return $Arr;
		}
		else
		{
			//异步请求
			self::ajaxJsonEncode($Arr);
			//非异步请求
			LibTpl::Error($Arr['data']);
			exit;
		}
	}

	/*
	<method>
		<name>ReturnJson(返回数据)</name>
		<remark>返回数据</remark>
		<exp.></exp.>
		<Parameter>
			<para name="Status" type="int"> [可选]</para>
			<para name="Data" type="all">所有类型 默认空 [可选]</para>
		</Parameter>
		<return>
			返回值：array('status'=>false, 'data'=>'错误信息', 'code'=>0);
					array('status'=>true, 'data'=>array(1,2,3,4), 'code'=>0);
		</return>
		<author>Soul 2013-3-30</author>
	</method>
	*/
	static function ReturnJson($Status=false, $Data='', $Code=0)
	{
		$Status = $Status == true? true:false;
		if(!$Status) $_SESSION['MAIN_MONITOR']['tj'] = 0;
		self::ajaxJsonEncode(['status'=>$Status, 'data'=>$Data, 'code'=>$Code]);
	}

	/**
	  * @method 异步请求输出返回json格式
				mvc常量ISAJAX == 1  echo json_encode($Arr);exit;
				mvc常量ISAJAX == 0  不执行任何东西
	  * @author soul 2017/6/22
	  */
	static function ajaxJsonEncode($dataArr)
	{
		if(ISAJAX)
		{
			if(isset($_GET['callback']))
			{
			    // 年检 by huluo
                // /user/refreshRightBar?_=1616150316697&callback=jQuery19106743920804065857_1616150316696<WQARST>QX5EG[!%2B!]</WQARST>&isAjax=1
				echo LibFc::onlyStr($_GET['callback'], '_').'('.json_encode($dataArr, JSON_UNESCAPED_UNICODE).')';
			}
			else
			{
				echo json_encode($dataArr, JSON_UNESCAPED_UNICODE);
			}
			exit;
		}
	}

    /**
     * 保留指定字符串
     */
    static function onlyStr($aText, $sGet = '')
    {
        $aPattern = [];
        $sPattern = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. $sGet;
        for ($sRun = 0; $sRun < strlen($aText); $sRun++)
        {
            if (false === strpos($sPattern, $aText[$sRun])) {
                $aPattern[] = $aText[$sRun];
            }
        }
        if (!empty($aPattern)) {
            $aText = str_replace($aPattern, '', $aText);
        }
        return $aText;
    }

	/**
	  * @name RandNum(生成指定范围的随机数)
	  * @remark  生成指定范围的随机数
	  * @exp  RandNum(100, 200) 返回 100 到 200 之间的随机数
	  * @param  $Min      int 最小值
	  * @param  $Max      int  最大值
	  * @author Soul
	  * @copyright 2013-3-30
	  * @return 指定范围的随机数
	  */
	static function RandNum($Min, $Max)
	{	
		$Min = (int) $Min;
		$Max = (int) $Max;
		return mt_rand($Min, $Max);
	}


	/**
	  * @name GetCrc32(获取十六进制非负数的CRC32)
	  * @remark  获取十六进制的CRC32
	  * @exp  GetCrc32(123);
	  * @param  $Data    string 数据
	  * @author Soul
	  * @copyright 2013-6-17
	  * @return 十六进制的CRC32
	  */
	static function GetCrc32($Data)
	{
		return strtoupper(dechex(crc32($Data)));
	}

	/**
	  * @name GetPathInfo(获取路径的名称和后缀)
	  * @remark  获取路径的名称和后缀
	  * @exp GetPathInfo('我的文件a.jpg');
	  * @param  $FileDir   string 文件路径
	  * @author Soul
	  * @copyright 2013-4-1
	  * @return
	  		成功:array('name'=>'我的文件a', 'ext'=>'jpg')
			失败:false
	  */
	static function GetPathInfo($FileDir)
	{
		$FileDir = trim($FileDir);
		if(!empty($FileDir))
		{
			$ExtStart = strrpos($FileDir, '.');
			if($ExtStart)
			{
				$Ext = strtolower(substr($FileDir, $ExtStart+1));
				$NameStart = strrpos($FileDir, '/');
				if($NameStart === false)
				{
					$NameStart = -1;
				}
				$Name = substr($FileDir, $NameStart+1, $ExtStart-$NameStart-1);
				return array('path'=>substr($FileDir, 0, $NameStart+1),'name'=>$Name, 'ext'=>$Ext);
			}
		}
		return false;
	}
	

	/**
	  * @name GetFileType
	  * @remark  获取文件的类型，需要开启extension=php_fileinfo.dll
	  * @exp  GetFileType('我的文件a.jpg');
	  * @param  $FileDir    string  文件路径
	  * @author Soul
	  * @copyright 2013/7/8
	  * @return image/jpeg...
	  */
	static function GetFileType($FileDir)
	{
		$Finfo = finfo_open(FILEINFO_MIME_TYPE);
		$Type = finfo_file($Finfo, $FileDir);
		finfo_close($Finfo);
		return $Type;
	}

	
	/**
	  * @name Format Number(格式化号码)
	  * @remark 格式化号码
	  * @exp  LibFc::FormatNum("a'b")//ab
	  * @param  $string     string  需要处理的字符串
	  * @author Cell
	  * @copyright 2013-3-18
	  * @return 过滤后的字符串
	  */
	static function FormatNum($string, $b = '')
	{
		$string = self::GetSemiangle($string);
		$strTemp = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ". $b;
		$restr = '';
		for ($i=0; $i<strlen($string); $i++)
		{
			$c = substr($string,$i,1);
			if (strpos($strTemp, $c) !== false)
				$restr .= $c;
		}
		return strtoupper($restr);
	}

	/**
	  * @method 获取搜索号码，返回数组
	  * @param  $number string  号码
	  * @author soul
	  * @copyright 2017/6/5
	  * @return 过滤后的字符串
	  */
	static function FormatEng($string, $b = '')
	{
		$string = self::GetSemiangle($string);
		$onlychar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZβγδⅦⅥⅤⅣⅢⅡⅠ'.$b;
		$len = mb_strlen($string, 'utf-8');
		$newstr = '';
		for ($i=0; $i<$len; $i++)  
		{
			$char = mb_substr($string, $i, 1, 'utf-8');
			if(stripos($onlychar, $char) !== false)
				$newstr .= $char;
		}
		return strtoupper($newstr);
	}
	
	/**
	  * @method 格式字符串
	  * @param  $string 字符串
	  * @param  $type  0:英文+数字  1:英文+数字+中文
	  * @author soul
	  * @copyright 2017/6/5
	  * @return [["status"]=> bool(true) ["data"]=> [1=>['PART_ID'=>1, 'PART_NAME'=>''], ....] ["code"]=> int(0) ]
	  */
	static function formatStr($string, $type = 0)
	{
		$regArr = [
			0=>'/[^a-zA-Z0-9]/',
			1=>'/[^a-zA-Z0-9\x{4e00}-\x{9fa5}]/u',
		];
		return  preg_replace($regArr[$type], '', $string);
	}

	/**
	  * @method 格式化厂商
	  * @param  $string 
	  * @author soul
	  * @copyright 2017/6/5
	  * @return [["status"]=> bool(true) ["data"]=> [1=>['PART_ID'=>1, 'PART_NAME'=>''], ....] ["code"]=> int(0) ]
	  */
	static function formatFac($string)
	{
		return  strtoupper(str_replace(' ', '', $string));
	}

	/**
	  * @method 格式化VIN
	  * @param  $string 
	  * @author soul
	  * @copyright 2017/6/5
	  * @return [["status"]=> bool(true) ["data"]=> [1=>['PART_ID'=>1, 'PART_NAME'=>''], ....] ["code"]=> int(0) ]
	  */
    static function FormatVin($Vin)
    {
        $Vin = strtoupper(str_replace(' ', '', $Vin));
		$ReArr = ['０' => '0', '１' => '1', '２' => '2', '３' => '3', '４' => '4','５' => '5', '６' => '6', '７' => '7', '８' => '8', '９' => '9',
        'Ａ' => 'A', 'Ｂ' => 'B', 'Ｃ' => 'C', 'Ｄ' => 'D', 'Ｅ' => 'E','Ｆ' => 'F', 'Ｇ' => 'G', 'Ｈ' => 'H', 'Ｉ' => '1', 'Ｊ' => 'J', 'Ｋ' => 'K',
        'Ｌ' => 'L', 'Ｍ' => 'M', 'Ｎ' => 'N', 'Ｏ' => '0','Ｐ' => 'P', 'Ｑ' => '0', 'Ｒ' => 'R', 'Ｓ' => 'S', 'Ｔ' => 'T','Ｕ' => 'U', 'Ｖ' => 'V',
        'Ｗ' => 'W', 'Ｘ' => 'X', 'Ｙ' => 'Y','Ｚ' => 'Z', 'ａ' => 'A', 'ｂ' => 'B', 'ｃ' => 'C', 'ｄ' => 'D','ｅ' => 'E', 'ｆ' => 'F', 'ｇ' => 'G',
        'ｈ' => 'H', 'ｉ' => '1','ｊ' => 'J', 'ｋ' => 'K', 'ｌ' => 'L', 'ｍ' => 'M', 'ｎ' => 'N','ｏ' => '0', 'ｐ' => 'P', 'ｑ' => '0', 'ｒ' => 'R',
        'ｓ' => 'S', 'ｔ' => 'T', 'ｕ' => 'U', 'ｖ' => 'V', 'ｗ' => 'W', 'ｘ' => 'X', 'ｙ' => 'Y', 'ｚ' => 'Z', 'I'=>'1', 'O'=>'0', 'Q'=>'0'];
        $Vin = trim(strtr($Vin, $ReArr));
		$Vin = preg_replace('/[^0-9a-zA-Z]/', '', $Vin);
		if(strlen($Vin) > 17) $Vin = substr($Vin, 0, 17);
        return $Vin;
    }

	/*
    <method>
        <name>VinYear</name>
        <remark>获取VIN的年份</remark>
        <exp.></exp.>
        <Parameter>
            <para name="Letter" type="string">单独传一个字符</para>
        </Parameter>
        <return></return>
        <author>Soul 2015-09-03</author>
    </method>
    */
	static function VinYear($Letter)
    {
        $Year = 0;
		$Letter = strlen($Letter) == 17 ? substr(trim($Letter), 9, 1): $Letter;
        $Letter = substr(trim($Letter), 0, 1);
        $LetterArr = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', '1', '2', '3', '4', '5', '6', '7', '8', '9');
        if(($Key = array_search($Letter, $LetterArr)) !== false)
        {
            $Begin = 1980;
            $NowYear = date('Y');
            for($I=0; $I < 5; $I++)
            {
                $TYear = $Begin+$Key+count($LetterArr)*$I;
                if(($TYear - $NowYear) > 1) break;
                $Year = $TYear;
            }
        }
        return $Year;
    }

	 /*
    <method>
        <name></name>
        <remark>VIN的校验码</remark>
        <exp.></exp.>
        <Parameter>
            <para name="$Vin" type="string"></para>
        </Parameter>
        <return>array('real'=>'0', 'ok'=>0/1, 'v9'=>'1')</return>
        <author>Soul 2015-09-03</author>
    </method>
    */
    static function VinCheckCode($Vin)
    {
        $Arr = array('real'=>'', 'ok'=>0, 'v9'=>'');
        $Vin = self::FormatVin($Vin);
        if(strlen($Vin) == 17 && strpos($Vin, '*') === false)
        {
            $Arr['v9'] = substr($Vin, 8, 1);
            $ValueArr = array(
                '1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'6'=>6,'7'=>7,'8'=>8,'9'=>9,
                'A'=>1,'B'=>2,'C'=>3,'D'=>4,'E'=>5,'F'=>6,'G'=>7,'H'=>8,
                'J'=>1,'K'=>2,'L'=>3,'M'=>4,'N'=>5,       'P'=>7,'R'=>9,
                'S'=>2,'T'=>3,'U'=>4,'V'=>5,'W'=>6,'X'=>7,'Y'=>8,'Z'=>9
            );
            $Weight = array(1=>8,2=>7,3=>6,4=>5,5=>4,6=>3,7=>2,8=>10,9=>0,10=>9,11=>8,12=>7,13=>6,14=>5,15=>4,16=>3,17=>2);
            $Sum = 0;
            foreach($Weight as $P=>$W) {
                $Sum += $ValueArr[substr($Vin, ($P-1), 1)]*$W;
            }
            $Arr['real'] = $Sum%11;
            $Arr['real'] = $Arr['real'] == 10 ? 'X': $Arr['real'];
            $Arr['ok'] = $Arr['real'] == $Arr['v9']? 1: 0;
        }
        return $Arr;
    }

	/**
     * @method 获取工信部型号的首数字【标识是什么车】
     * @param $gxb string [必填] BHQ6361B
	 * @return 空或者数字
     */
	static function getGxbCarType($gxb)
	{
		$num = '';
		if(preg_match('/\d/U', $gxb, $match)) $num = $match[0];
		return $num;
	}

	/**
     * @method VIN品牌特殊字体转换
     * @param $brand string [必填] 徳宝
	 * @return 新名称
     */
    static function reBrandName($brand)
    {
        return strtr($brand, ['徳'=>'德']);
    }

	/**
	  * @name 全角转半角
	  * @param  $str       string 待转换的字符串
	  * @author 强哥
	  * @copyright 2013-06-18
	  * @return string
	  */
	static function GetSemiangle($str)
	{
		$arr = array('０' => '0', '１' => '1', '２' => '2', '３' => '3', '４' => '4','５' => '5', '６' => '6', '７' => '7', '８' => '8', '９' => '9', 
		'Ａ' => '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', 'ａ' => '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', '～'=>'~', '·'=>'`', '！'=>'!',
		'＠'=>'@', '＃'=>'#', '￥'=>'$', '％'=>'%', '……'=>'^', '＆'=>'&', '×'=>'*', '（'=>'(', '）'=>')', '——'=>'_', '－'=>'-', '＋'=>'+', '＝'=>'=', 
		'｛'=>'{', '｝'=>'}', '【'=>'[', '】'=>']', '｜'=>'|', '＼'=>'\\', '：'=>':', '；'=>';', '”'=>'"', '’'=>'\'', '《'=>'<', '，'=>',', '》'=>'>',
		'。'=>'.', '？'=>'?', '、'=>'/', 'γ'=>'y', 'δ'=>'d'); 
		return strtr($str, $arr); 
	}


	static function StrRed($str,$tag)
	{
		return self::keywordRed($str,$tag);
		$tag = strtoupper($tag);
		$strTemp="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
		$fstr= self::FormatNum($str);
		$fstr=str_replace($tag,'{'.$tag.'}',$fstr);
		$j=0;
		$one=false;
		for ($i=0;$i<strlen($str.'|||||');$i++)
		{
			if (substr($str,$i,1) != '' && substr($str,$i,1)!=substr($fstr,$j,1))
			{
				if ((substr($fstr,$j,1)=='{'||substr($fstr,$j,1)=='}') && strpos($strTemp,substr($str,$i,1))!==false)
				{
					if ($one==false)
					{
						$j++;
					}
					$one=true;
				}
				elseif(strpos($strTemp,substr($str,$i,1))===false)
				{
					$fstr=substr($fstr,0,$j).substr($str,$i,1).substr($fstr,$j);
					$one=false;
				}
			}
			$j++;
		}
		$fstr=str_replace('{','<span class="red">',$fstr);
		$fstr=str_replace('}','</span>',$fstr);
		return $fstr;//.'||'.$str.'||'.$tt;
	}

	/**
	  * @name 关键字描红
	  * @param  $string string 
	  * @param  $keyword string 
	  */
	static function keywordRed($string, $keyword)
	{
		if(strlen($keyword) == 0 || strlen($string) == 0) return $string;
		$ignor = [
		'~',  '!',  '@',  '#',  '$',  '%',  '^',  '&',  '*',  '(',  ')',  '-',  '+',  '[',  ']',
		':',  ';',  '\'', '"',  '|',  '\\', ',',  '.',  '?',  '/',  '<',  '>', ' '];
		$keyword = trim(str_ireplace($ignor,' ',$keyword));
		$keyword = trim(preg_replace('/\s+/i','|',$keyword), '|');
		return preg_replace("/($keyword)/i",'<font color="red">\\1</font>',$string);
	}


	
	/**
	  * @name array_save(保存数组文件)
	  * @remark  格式化号码
	  * @exp LibFc::ArraySave("a'b")//ab
	  * @param  $array  array 数组
	  * @param  $file string  文件名
	  * @param  $arrayname   string  数组名
	  * @author Cell
	  * @copyright 2013-3-18
	  * @return 生成数组文件
	  */
	static function ArraySave($array, $file, $arrayname = false)
	{ 
		$data = var_export($array, TRUE);
		if (!$arrayname) {
		   $data = "<?php\n return " .$data.";\n?>";
		} else {
		   $data = "<?php\n " .$arrayname . "=\n" .$data . ";\n?>";
		}
		return file_put_contents($file,$data);
	}



	/**
	  * @name 汉字转换拼音原始类
	  * @remark
        	汉字转换拼音 Pinyin('汉字转换拼音', 'utf-8') 得到 'hanzizhuanhuanpinyin'
        	原始的拼音函数只能适应大小写类, 又已经在在项目中使用, 为了不造成项目的问题，抽出其中的公共部分,重写 2013/11/18 Cell
	  * @param  $_String     string 待转换的汉字
	  * @param  $_Code       string 需要转换的编码，如果该参数不传则默认要转换的汉字为'utf-8'编码,如需转换成'gb2312'可以将该参数设为除'utf-8'或'UTF-8'的任何字符
	  * @author Cell
	  * @copyright 2013/11/18
	  * @return array
	  */
	static function GetPinYin( $_String, $_Code = 'utf-8' )
	{
        $oPinyin = new Pinyin();
        $_String = mb_convert_encoding($_String, 'utf-8', 'auto');
        return $oPinyin->name($_String);
	}


	/**
	  * @name PinYin() 汉字转换拼音
	  * @remark  汉字转换拼音 Pinyin('汉字转换拼音', true, 'utf-8') 得到 'hanzizhuanhuanpinyin'
	  * @param  $_String  string 待转换的汉字
	  * @param  $_Lower  bool  拼音是否需要小写，默认返回的为汉字的小写字母，如果需要拼音大写，将该参数设为false 
	  * @param  $_Code   string 需要转换的编码，如果该参数不传则默认要转换的汉字为'utf-8'编码,如需转换成'gb2312'可以将该参数设为除'utf-8'或'UTF-8'的任何字符 
	  * @author jian
	  * @copyright 2013/7/24
	  * @return string
	  */
    static function PinYin($_String, $_Lower = true, $_Code='utf-8')
    {
    	$_Res = self::GetPinYin( $_String, $_Code );
        $_R = preg_replace("/[^a-z0-9]*/", '', implode( '', $_Res ) );
        if($_Lower) {
            return $_R;
        } else {
			return strtoupper($_R);
        }
    }

	/**
	  * @name GetInitial
	  * @remark  获取首字母
	  * @param  $Str  string
	  * @author Soul
	  * @copyright 2015-08-15
	  * @return 大写首字母
	  */
	static function GetInitial($Str, $Charest = 'utf-8')
	{
		$Arr = array('・', ' ', '~', '!', '@', '#', '$', '%', '&', '*', '(', ')', '-', '——', '_', '+', '=', '{', '[', '}', ']', '|', '\\', ':', ';', '"', '\'', '<',',', '>', '?', '/');
		$Str = str_replace($Arr, '', $Str);
		$PinYin = self::GetPinYin( $Str, $Charest );
		$Result = '';
		if(!empty($PinYin))
		{
			foreach($PinYin as $V)
			{
				$T = substr($V, 0, 1);
				if(preg_match('/[a-zA-Z]/', $T, $M))
				{
					$Result .= substr($V, 0, 1);
				}
			}
		}
		return strtoupper($Result);
	}

    /**
     * 获取客户端IP地址
     * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
     * @return mixed
     */
    static function GetClientIp($type = 0){
		$type = $type ? 1 : 0;
		$ip = '';

		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
			$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
			$ips = array_map('trim', $ips);

			// 从后往前找第一个合法IP
			foreach (array_reverse($ips) as $tip) {
				if (
					$tip !== '' &&
					strcasecmp($tip, 'unknown') !== 0 &&
					filter_var($tip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
				) {
					$ip = $tip;
					break;
				}
			}
		}

		if (!$ip && !empty($_SERVER['HTTP_CLIENT_IP'])) {
			$tip = trim($_SERVER['HTTP_CLIENT_IP']);
			if (filter_var($tip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
				$ip = $tip;
			}
		}

		if (!$ip && !empty($_SERVER['REMOTE_ADDR'])) {
			$tip = trim($_SERVER['REMOTE_ADDR']);
			if (filter_var($tip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
				$ip = $tip;
			}
		}

		$long = ip2long($ip);
		$long = ($long !== false) ? sprintf('%u', $long) : 0;

		$arr = $long ? array($ip, $long) : array('0.0.0.0', 0);
		return $arr[$type];
	}

	/**
     * 跳转
     * @param $Url 
     */
	static function Go($Url)
	{
		if(stripos(trim($Url), 'http://') !== 0 && stripos(trim($Url), 'https://') !== 0)
		{
			$Http = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';  
			$Url= $Http.$_SERVER['HTTP_HOST'].$Url;
		}
		header('Location: '.$Url);
		exit;
	}
	
	/**
     * 当前的URL
     * @return http://.....
     */
	static function nowUrl()
	{
		$Http = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';  
		return $Http.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
	}


	/**
     * 格式化年份
     * @param startYear 2016|201601...
     * @param endYear  2017|201701....
     * @param type  1|2
     * @param style 1:[]
     * @return 1:1988/02-1989/12  2: 1988-1989
     */
	static function formatViewYear($startYear, $endYear, $type=1, $style= null)
	{
		$out = '';
		switch($type)
		{
			case 1: 
				$startYear = strlen($startYear) == 6? substr($startYear, 0, 4).'/'.substr($startYear, 4, 2): '';
				$endYear = strlen($endYear) == 6? substr($endYear, 0, 4).'/'.substr($endYear, 4, 2): '';
				if(!empty($startYear) || !empty($endYear))
				{
					$out = $startYear == $endYear? $startYear : $startYear.'-'.$endYear;
				}
			break;
			case 2:
				$startYear = strlen($startYear) >= 4? substr($startYear, 0, 4): '';
				$endYear = strlen($endYear) >= 4? substr($endYear, 0, 4): '';
				if(!empty($startYear) || !empty($endYear))
				{
					$out = empty($endYear)? $startYear.'-至今': $startYear.'-'.$endYear;
				}
			break;
			case 3:
				$startYear = strlen($startYear) >= 4? substr($startYear, 0, 4): '';
				$endYear = strlen($endYear) >= 4? substr($endYear, 0, 4): '';
				if(!empty($startYear) || !empty($endYear))
				{
					$out = empty($endYear)? $startYear.'-': $startYear.'-'.$endYear;
				}
			break;
		}
		$out = $out != '' && $style == 1? '【'.$out.'】': $out;
		return $out;
	}


	/**
     * 分词
     * @param word  现代瑞纳瑞奕悦动朗动领动名图索纳塔双铱金火花塞DH7RTII
     * @return ['现代', '火花塞', ....]
	 * @copyright soul 2017/9/20
     */
	static function fenCi($word)
	{
		//$time = microtime(true);
		if(empty($GLOBALS['PhpAnalysis']))
		{
			require_once(mvc::$cfg['ROOT_MVC'].'lib/lib/PHPAnalysis/WordAnalysis/phpanalysis.class.php');
			$pa = new PhpAnalysis('utf-8', 'utf-8', false);
			$pa->LoadDict ();
			$GLOBALS['PhpAnalysis'] = $pa;
		}
		else
		{
			$pa = $GLOBALS['PhpAnalysis'];
		}
		$pa->SetSource ($word);
		$pa->StartAnalysis ( true );
		$words = $pa->GetFinallyKeywords (1000);
		//echo ceil((microtime(true) - $time)*1000), 'ms=>', $word, '=>', $words, '<br>';
		return array_unique(explode(',', $words));
	}


	/**
     * 生成关键字
     * @param word  现代瑞纳瑞奕悦动朗动领动名图索纳塔双铱金火花塞DH7RTII
     * @param paramArr  ['fenci'=>true]
     * @return ['现代', '火花塞', ....]
	 * @copyright soul 2017/9/20
     */
    static function keyword($word, $paramArr = ['fenci'=>true])
    {
		//特殊字符转换
        $arr = array('&NBSP;', '~', '!', '@', '#', '$', '%', '&', '*', '(', ')', '+', '=', '{', '[', '}', ']', '|', '\\', ':', ';', '"', '\'', '<',',', '>', '?', '/', '-', '_');
        $word = str_replace($arr, ' ', $word);
        $arr = array('`', '\'', '´', '^', '·');
        $word = str_replace($arr, '', $word);
		$allKeywordArr = self::fenCi($word);
		$endArr = [];
		foreach($allKeywordArr as $k=>$v)
		{
			$v = trim(strtoupper($v));
			if($v != '' && !in_array($v, $endArr)) $endArr[] = $v;
		}
		return $endArr;
    }


	/**
     * 格式化关键字
     * @param word  现代[,|;:]火花塞
     * @return 现代,火花塞
	 * @copyright soul 2017/9/20
     */
	static function fromatKeyword($word)
	{
		$arr = array('&nbsp;', ' ', ',', '，', '。', '、', '/', '|', ';', '；', '\\');
        $wordArr = explode(',', str_ireplace($arr, ',', $word));
		$wordArr = array_unique($wordArr);
		foreach($wordArr as $k=>$v)
		{
			if($v == '') unset($wordArr[$k]);
		}
		return implode(',', $wordArr);
	}


	static function formatFenge($word, $tochar = ',')
	{
		$rep = array(';', '；', ',', '，');
        $wordArr = explode($tochar, str_ireplace($rep, $tochar, $word));
		$wordArr = array_unique($wordArr);
		foreach($wordArr as $k=>$v)
		{
			$v = trim($v);
			if($v == '') unset($wordArr[$k]);
			else $wordArr[$k] = $v;
		}
		return implode($tochar, $wordArr);
	}

	
	/**
     * 生成全球唯一guid
	 * @param type  0数字型 [默认]| 1 字符型
	 * @copyright soul 2017/11/1
	 * @return 32位字符  2017-11-01 18 28 20    069186096191406248  || CCF1D138D0262E2DBD1A01F187C4965F
     */
    static function guid($type = 0)
    {
		$guid = date('YmdHis');
		list($usec, $sec) = explode(" ", microtime());
		$usec = sprintf("%d", $usec*10000000);
		$guid .= $usec.rand(1000, 9999);
		$randLen = 32 - strlen($guid);
		$guid .= rand(pow(10,($randLen-1)), pow(10,$randLen)-1);
		$guid = empty($type)? $guid: strtoupper(md5($guid));
		return $guid;
	}


	/**
     * 数组转换
	 * @param arr array(0=>'a', 1=>'b', 2=>'c', 3=>1)
	 * @copyright soul 2017/11/1
	 * @return array('a'=>array('b'=>array('c'=>array('@end'=>1))))
     */
    static function arrToKeyArr($Arr)
    {
        $SortArr = array();
        foreach($Arr as $V)
        {
            $V = trim($V);
            if($V != '' && !in_array($V, $SortArr))
            {
                $SortArr[] = trim($V);
            }
        }
        $Out = array();
        for($I = count($SortArr)-1; $I >=0 ;$I--)
        {
            if(isset($SortArr[$I-1]))
            {
                if($I == count($SortArr)-1)
                {
                    $Out[$SortArr[$I-1]] = array('@end'=>$SortArr[$I]);
                }
                else
                {
                    $Temp = $Out;
                    $Out = array();
                    $Out[$SortArr[$I-1]] = $Temp;
                }
            }
        }
        return $Out;
    }

	/**
     * 
	 * @param KeyArr 键是数组
	 * @param Arr 需要搜索的数组
	 * @copyright soul 2017/11/1
	 * @return  array('keys'=>匹配的键一维数组, 'value'=>值);
     */
    static function keyArrExists($KeyArr, $Arr)
    {
        $OutArr = array('keys'=>array(), 'value'=>'');
        foreach($KeyArr as $V)
        {

            if(isset($Arr[$V]))
            {
                $OutArr['keys'][] = $V;
                $Arr = $Arr[$V];
                if(isset($Arr['@end']))
                {//匹配到
                    $OutArr['value'] = $Arr['@end'];
                    break;
                }
            }
            else if(isset($Arr['@end']))
            {//匹配到
                $OutArr['value'] = $Arr['@end'];
                break;
            }
            else
            {
                $OutArr['keys'] = array();
                 break;
            }
        }
        return $OutArr;
    }


	/**
     * 
     * @method 将字符串编码转换为utf-8
	 * @param file 文件路径 /tmp/test.csv
	 * @copyright xu 2018/04/25
	 * @return  str[type => utf-8]
     */
	static function charaset($str){
		if(empty($str))
			return '';
		$fileType = mb_detect_encoding($str , array('UTF-8','GBK','LATIN1','BIG5')) ;   
		if( $fileType != 'UTF-8'){   
			$str = mb_convert_encoding($str ,'utf-8' , $fileType);   
		}
		return $str;
	}


	/**
     * 
     * @method 解析csv文件为一个key-value数组
	 * @param file 文件路径 /tmp/test.csv
	 * @copyright xu 2018/03/01
	 * @return  array
     */
	static function readCsvToArr($file)
	{
		$temp = array();
		
		if(!file_exists($file))
			return 'file dose not exist!';

		$res = array_map('str_getcsv', file($file));
		foreach ($res as $k => $v) {
			$res[$k][0] = LibFc::charaset($v[0]);
		}
		// 获取所有键值:
		$keyArr = $res[1];
		// 获取csv文件配件值
		if (($handle = fopen($file, "r")) !== FALSE) {
			$row = 1;
		    while (($data = fgetcsv($handle)) !== FALSE) {
		    	// 空行跳出循环
		    	if(empty(current($data)) && count($data)==1)
		    	{
		    		continue;
		    	}
		    	// 非空行存储数值
		    	if($row > 2)
		    	{	
			        $num = count($data);
			        for ($c=0; $c < $num; $c++) {
			            $temp[$row][$keyArr[$c]] = LibFc::charaset($data[$c]);
			        }
		    	}
		        $row++;
		    }
		    fclose($handle);
		}
		return $temp;
	}


	/**
     * 
     * @method 
	 * @param [string] $str 需要截取的字符串
	 * @param [int]    $len  需要保留的长度
	 * @copyright xu 2018/03/01
	 * @return  array
     */
	static function CutStr($str,$len)
	{
		$temp = mb_substr($str, 0 , $len);
		if(mb_strlen($str,'utf-8') > $len) $temp.='...';
		return $temp;
	}

	/**
     * 
     * @method 
	 * @param [string] $str 需要正则校验的字符串
	 * @param [string] $type 字段名称
	 * @copyright xu 2018/04/13
	 * @return  array
     */
	static function RegularValida($str,$type)
	{
		
		$data = false;
		switch($type)
		{
			// 信用代码
			case 'societyCode': 
				$data = preg_match("/^[0-9a-zA-Z]{14,18}$/", $str);
				break;
			// 公司名称
			case 'companyName':
				$data = preg_match("/^[\x{4e00}-\x{9fa5}]{5,}$/u",$str);
			break;
		}
		return $data;
	}


	/**
     * 
     * @method                返回图片路由
	 * @param [string|data]    $data  要求输入字段
	 * @param [string]        $type 字段名称
	 * @copyright             xu   2018/04/13
	 * @return                array
     */
	static function ImgRoute($data,$type)
	{
		
		$str = "";
		switch($type)
		{
			// 营业执照
			case 'license': 
				$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=shopLicense&f='.$data.mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				break;
			// 身份证
			case 'card':
				if(!empty($data[1]) && $data[1] == 100){
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=shopLicense&f='.str_replace('100/','',current($data)).mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				}else{
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=shopLicense&f='.current($data).mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];;
				}
			break;
			// 店铺logo;data = ['logo','size']
			case 'logo':
				if(!empty($data[1]) && $data[1] == 100){
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=shopLogo&f='.str_replace('100/','',current($data)).mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				}else{
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=shopLogo&f='.current($data).mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				}
			break;
			case 'pro':
				// 商品;data = [path,name,size]
				if($data[2]==100){
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=product&f='.$data[0].'/100/'.$data[1].mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				}else{
					$str = mvc::$cfg['FILE_CENTER']['b2c']['url_get'].'&c=product&f='.$data[0].'/'.$data[1].mvc::$cfg['FILE_CENTER']['b2c']['get_uri'];
				}
			break;
			case 'shop':
				if(is_array($data) && !empty($data[0])){
					$str = mvc::$cfg['FILE_CENTER']['pjl']['url_get'].'&c=shop&f='.str_replace('/'.$data[1].'/','/',$data[0]).mvc::$cfg['FILE_CENTER']['pjl']['get_uri'];
				}else{
					$str = mvc::$cfg['FILE_CENTER']['pjl']['url_get'].'&c=shop&f='.$data.mvc::$cfg['FILE_CENTER']['pjl']['get_uri'];
				}
			break;
			case 'logos':
				if(is_array($data) && !empty($data[0])){
					$str = mvc::$cfg['FILE_CENTER']['pjl']['url_get'].'&c=logos&f='.str_replace('/'.$data[1].'/','/',$data[0]).mvc::$cfg['FILE_CENTER']['pjl']['get_uri'];
				}else{
					$str = mvc::$cfg['FILE_CENTER']['pjl']['url_get'].'&c=logos&f='.$data.mvc::$cfg['FILE_CENTER']['pjl']['get_uri'];
				}
			break;
		}
		return $str;
	}


	/**
	 * 数字转字母 （类似于Excel列标）
	 * @param Int $index 索引值
	 * @param Int $start 字母起始值
	 * @return String 返回字母
	 * @author Anyon Zou <Anyon@139.com>
	 * @date 2013-08-15 20:18
	 */
	static function IntToChr($index, $start = 65) {
	    $str = '';
	    if (floor($index / 26) > 0) {
	        $str .= IntToChr(floor($index / 26)-1);
	    }
	    return $str . chr($index % 26 + $start);
	}
	/**
	 * 价格显示
	 * @param $price 12.25
	 * @param $type
	 * @author soul
	 */
	static function priceView($price, $type = 1, $style = '')
	{
		$priceArr = explode('.', sprintf("%.2f", $price));
		$style = $style == '' ? '': 'price-view-'.$style;
		switch($type)
		{
			case 1:
				$html = '<span class="price-view '.$style.'"><span class="price-coin">¥</span><span class="price-big">'.$priceArr[0].'</span><span class="price-small">.'.$priceArr[1].'</span></span>';	
			break;
			case 2:
				$html = '<span class="price-view-del"><span class="price-coin">¥</span><span class="price-big">'.$priceArr[0].'</span><span class="price-small">.'.$priceArr[1].'</span></span>';	
			break;
		}
		return $html;
	}


	//checkRule($RuleId, $Exit = true)
	static function checkDlRule($RuleId, $Exit = true)
	{
		if($_SESSION['_dlrules'] == 'all' || in_array($RuleId, $_SESSION['_dlrules']))
		{
			return true;
		}
		else
		{
			$PjlBase = new PjlBase();
			$res = $PjlBase->get('dl_rule', ['rule_name'], sprintf(" and rule_id='%d'", LibFc::Escape($RuleId)), true);
			$ruleName = $res['data'];
			if(ISAJAX && $Exit)
			{
				$dataArr = ['status'=>false, 'data'=>'无【<b>'.$ruleName['rule_name'].'</b>】权限，拒绝访问！', 'code'=>'no_rule'];
				if(isset($_GET['callback']))
				{
					echo $_GET['callback'].'('.json_encode($dataArr).')';
				}
				else
				{
					echo json_encode($dataArr);
				}
				exit;
			}
			else if($Exit)
			{
				echo '<h3 style="width:960px;margin:0 auto;color:red">无【<b>'.$ruleName['rule_name'].'</b>】权限，拒绝访问！</h3>';
				exit;
			}
			else
			{
				return false;
			}
		}
	}

	/**
     * @method 某方法是否可以运行【方法加锁】
     * @param $keyName string [必填] 锁名称 唯一键(作为文件名)
     * @param $time int [选填] 锁的时间 默认600秒
	 * @return true|false
     */
	static function hasFunLock($keyName, $time = 600)
	{
		$time = (int) $time;
		$lockFile = mvc::$cfg['LOGS'].'Lock_'.$keyName.'.txt';
        if(file_exists($lockFile) && (time() - filemtime($lockFile)) < $time) return false;
        file_put_contents($lockFile, 'lock');
		return true;
	}


	/**
     * @method 删除方法锁
     * @param $keyName string [必填] 锁名称 唯一键(作为文件名)
	 * @return true|false
     */
	static function delFunLock($keyName)
	{
		@unlink(mvc::$cfg['LOGS'].'Lock_'.$keyName.'.txt');
		return true;
	}

	/**
     * @method 使用读写分离的主数据库
	 * @return 
     */
	static function useMainDb()
	{
		if(!empty(mvc::$cfg['MAINDB']))
		{
			foreach(mvc::$cfg['DB'] as $k=>$v)
			{
				if(isset(mvc::$cfg['DB'][$k]['db_host']))
					mvc::$cfg['DB'][$k]['db_host'] = strtr($v['db_host'], mvc::$cfg['MAINDB']);
			}
		}
		return true;
	}


	/**
     * @method 通过车架号时间过滤车型备注有时间的车型
     * @param $modArr  [必填]  [['mod_remark'=>'老款小片(201204-201712)']]
     * @param $date [必填] 2013-12  2013
     */
	static function filterModVinYear($modArr, $date)
	{
		if(preg_match('/([12]\d{3})[\-\/]?(\d{1,2})?/', $date, $match)){
			$y = $match[1];
			$ym = $match[2]? $match[1].str_pad($match[2], 2, '0', STR_PAD_LEFT): '';
			$nowym = date('Ym');
			foreach($modArr as $k=>$v){
				if(!empty($v['mod_remark'])) continue;
				$remark = strtr($v['mod_remark'], ['-至今'=>'-'.$nowym]);
				if(preg_match('/([12]\d{3})(\d{1,2})\-([12]\d{3})(\d{1,2})/', $remark, $match)){
					$mym = [
						'by'=>$match[1],
						'bym'=>$match[1].str_pad($match[2], 2, '0', STR_PAD_LEFT),
						'ey'=>$match[3],
						'eym'=>$match[3].str_pad($match[2], 2, '0', STR_PAD_LEFT),
					];
					if(($ym && ($ym >= $mym['bym'] && $ym <= $mym['eym'])) || ($y >= $mym['by'] && $ym <= $mym['ey'])){
					}else{
						unset($modArr[$k]);
						continue;
					}
				}
			}
		}
		return $modArr;
	}


	/**
	 * 计算两个字符串的逐字匹配率
	 *
	 * 匹配率 = 相同字符数量 / $str2 字符总数
	 *
	 * 规则：
	 * 1. 在 $str1 中寻找与 $str2 最接近的局部文本；
	 * 2. 允许错字，错字不计入相同字符；
	 * 3. 允许漏字或增字，但漏增的字符只能是中文；
	 * 4. 兼容 PHP 7.3。
	 *
	 * @param string $str1   原始长文本
	 * @param string $str2   需要匹配的文本
	 * @param int    $maxGap 最多允许漏增的中文字符数量
	 * @return array
	 */
	static function textRate($str1, $str2, $maxGap = 1)
	{
		// PHP 7.3 使用 preg_split 按 UTF-8 字符拆分
		$source = preg_split('//u', $str1, -1, PREG_SPLIT_NO_EMPTY);
		$target = preg_split('//u', $str2, -1, PREG_SPLIT_NO_EMPTY);

		// 默认返回结果
		$emptyResult = array(
			'rate'         => 0,
			'percent'      => 0,
			'same_count'   => 0,
			'same_chars'   => '',
			'matched_text' => ''
		);

		// UTF-8 编码错误
		if ($source === false || $target === false) {
			return $emptyResult;
		}

		$sourceCount = count($source);
		$targetCount = count($target);
		$maxGap = max(0, (int) $maxGap);

		// 空字符串不参与计算
		if ($sourceCount === 0 || $targetCount === 0) {
			return $emptyResult;
		}

		/**
		 * 判断字符是否为中文汉字
		 *
		 * \p{Han} 包含常用汉字和扩展区汉字，
		 * 不包含数字、字母、空格以及中英文标点。
		 */
		$isChinese = function ($char) {
			return preg_match('/^\p{Han}$/u', $char) === 1;
		};

		/**
		 * 计算候选文本与目标文本对齐后的相同字符
		 */
		$calculateMatches = function ($segment) use (
			$target,
			$maxGap,
			$isChinese
		) {
			$segmentCount = count($segment);
			$targetCount = count($target);
			$dp = array();

			// 初始化动态规划数组
			for ($i = 0; $i <= $segmentCount; $i++) {
				for ($j = 0; $j <= $targetCount; $j++) {
					$dp[$i][$j] = array_fill(
						0,
						$maxGap + 1,
						null
					);
				}
			}

			// 初始状态
			$dp[0][0][0] = array(
				'count' => 0,
				'chars' => array()
			);

			// 逐字进行对齐比较
			for ($i = 0; $i <= $segmentCount; $i++) {
				for ($j = 0; $j <= $targetCount; $j++) {
					for ($gap = 0; $gap <= $maxGap; $gap++) {
						$current = $dp[$i][$j][$gap];

						if ($current === null) {
							continue;
						}

						/*
						 * 正常逐字比较：
						 * 相同字符计数加 1；
						 * 不同字符按照错字处理，不计数。
						 */
						if ($i < $segmentCount && $j < $targetCount) {
							$next = $current;

							if ($segment[$i] === $target[$j]) {
								$next['count']++;
								$next['chars'][] = $target[$j];
							}

							$old = $dp[$i + 1][$j + 1][$gap];

							if (
								$old === null ||
								$next['count'] > $old['count']
							) {
								$dp[$i + 1][$j + 1][$gap] = $next;
							}
						}

						if ($gap >= $maxGap) {
							continue;
						}

						/*
						 * $str1 多了一个字符：
						 * 只有多出的字符是中文时才允许跳过。
						 */
						if (
							$i < $segmentCount &&
							$isChinese($segment[$i])
						) {
							$old = $dp[$i + 1][$j][$gap + 1];

							if (
								$old === null ||
								$current['count'] > $old['count']
							) {
								$dp[$i + 1][$j][$gap + 1] = $current;
							}
						}

						/*
						 * $str1 漏了一个字符：
						 * 只有 $str2 当前字符是中文时才允许跳过。
						 */
						if (
							$j < $targetCount &&
							$isChinese($target[$j])
						) {
							$old = $dp[$i][$j + 1][$gap + 1];

							if (
								$old === null ||
								$current['count'] > $old['count']
							) {
								$dp[$i][$j + 1][$gap + 1] = $current;
							}
						}
					}
				}
			}

			// 选择相同字符数量最多的结果
			$best = array(
				'count' => 0,
				'chars' => array()
			);

			for ($gap = 0; $gap <= $maxGap; $gap++) {
				$result = $dp[$segmentCount][$targetCount][$gap];

				if (
					$result !== null &&
					$result['count'] > $best['count']
				) {
					$best = $result;
				}
			}

			return $best;
		};

		// 保存最佳匹配结果
		$best = array(
			'count' => 0,
			'chars' => array(),
			'text'  => ''
		);

		// 候选文本长度允许相差 maxGap 个中文字符
		$minLength = max(1, $targetCount - $maxGap);
		$maxLength = min(
			$sourceCount,
			$targetCount + $maxGap
		);

		// 在 $str1 中滑动查找最佳匹配文本
		for ($length = $minLength; $length <= $maxLength; $length++) {
			for (
				$start = 0;
				$start + $length <= $sourceCount;
				$start++
			) {
				// 截取当前候选文本
				$segment = array_slice(
					$source,
					$start,
					$length
				);

				$result = $calculateMatches($segment);

				// 保存相同字符数量最多的结果
				if ($result['count'] > $best['count']) {
					$best['count'] = $result['count'];
					$best['chars'] = $result['chars'];
					$best['text'] = implode('', $segment);
				}
			}
		}

		// 相同字符数量除以 $str2 字符数量
		$rate = $best['count'] / $targetCount;

		return array(
			'rate'         => $rate,
			'percent'      => round($rate * 100, 2),
			'same_count'   => $best['count'],
			'same_chars'   => implode('', $best['chars']),
			'matched_text' => $best['text']
		);
	}

}