PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

Downloader.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Downloader.class.php'
View Content
<?php

/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class Downloader {
	static function start($url,$saveFile,$timeout = 10) {
		$dataFile = $saveFile . '.download.cfg';
		$saveTemp = $saveFile . '.downloading';
		$fileHeader = is_array($url) ? $url : url_header($url); // 兼容传入的header情况;
		
		$url = $fileHeader['url'];
		if(!$url || !request_url_safe($url)){return array('code'=>false,'data'=>'url error!');}
		if(!$fileHeader['status']){return array('code'=>false,'data'=>LNG('admin.plugin.installNetworkError'));}
		
		//默认下载方式if not support range
		if(!$fileHeader['supportRange'] || 
			$fileHeader['length'] == 0 ){
			@unlink($saveTemp);@unlink($saveFile);
			$result = self::fileDownloadFopen($url,$saveFile,$fileHeader['length']);
			if($result['code']) {
				return $result;
			}else{
				@unlink($saveTemp);@unlink($saveFile);
				$result = self::fileDownloadCurl($url,$saveFile,false,0,$fileHeader['length']);
				@unlink($saveTemp);
				return $result;
			}
		}

		$existsLength  = is_file($saveTemp) ? filesize_64($saveTemp) : 0;
		$contentLength = intval($fileHeader['length']);
		if( file_exists($saveTemp) &&
			time() - filemtime($saveTemp) < 3) {//has Changed in 3s,is downloading 
			return array('code'=>false,'data'=>'downloading');
		}
		
		$existsData = array();
		if(is_file($dataFile)){
			$tempData = file_get_contents($dataFile);
			$existsData = json_decode($tempData, 1);
		}
		// exist and is the same file;
		if( file_exists($saveFile) && $contentLength == filesize_64($saveFile)){
			@unlink($saveTemp);
			@unlink($dataFile);
			return array('code'=>true,'data'=>'exist');
		}

		// check file is expire
		if ($existsData['length'] != $contentLength) {
			$existsData = array('length' => $contentLength);
		}
		if($existsLength > $contentLength){
			@unlink($saveTemp);
		}
		// write exists data
		file_put_contents($dataFile, json_encode($existsData));
		$result = self::fileDownloadCurl($url,$saveFile,true,$existsLength,$contentLength);
		if($result['code']){
			@unlink($dataFile);
		}
		return $result;
	}
	

	// fopen then download
	static function fileDownloadFopen($url, $fileName,$headerSize=0){
		@ini_set('user_agent','Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36');

		$fileTemp = $fileName.'.downloading';
		set_timeout();
		@unlink($fileTemp);
		if ($fp = @fopen ($url, "rb")){
			if(!$downloadFp = @fopen($fileTemp, "wb")){
				return array('code'=>false,'data'=>'open_downloading_error');
			}
			while(!feof($fp)){
				if(!file_exists($fileTemp)){//删除目标文件;则终止下载
					fclose($downloadFp);
					return array('code'=>false,'data'=>'stoped');
				}
				//对于部分fp不结束的通过文件大小判断
				clearstatcache();
				if( $headerSize>0 &&
					$headerSize==filesize(iconv_system($fileTemp))
					){
					break;
				}
				fwrite($downloadFp, fread($fp, 1024 * 8 ), 1024 * 8);
			}
			//下载完成,重命名临时文件到目标文件
			fclose($downloadFp);
			fclose($fp);

			// self::checkGzip($fileTemp);
			if(!@rename($fileTemp,$fileName)){
				usleep(round(rand(0,1000)*50));//0.01~10ms
				@unlink($fileName);
				$res = @rename($fileTemp,$fileName);
				if(!$res){
					return array('code'=>false,'data'=>'rename error![open]');
				}
			}
			return array('code'=>true,'data'=>'success');
		}else{
			return array('code'=>false,'data'=>'url_open_error');
		}
	}

	// curl 方式下载
	// 断点续传 http://www.linuxidc.com/Linux/2014-10/107508.htm
	static function fileDownloadCurl($url, $fileName,$supportRange=false,$existsLength=0,$length=0){
		$fileTemp = $fileName.'.downloading';
		set_timeout();
		$fp = @fopen ($fileTemp, "a");
		if(!$fp) return array('code'=>false,'data'=>'file create error');
		$ch = curl_init($url);
		if($supportRange){
			curl_setopt($ch, CURLOPT_RANGE, $existsLength."-");
		}
		curl_setopt($ch, CURLOPT_FILE, $fp);
		curl_setopt($ch, CURLOPT_REFERER,get_url_link($url));
		curl_setopt($ch, CURLOPT_ENCODING,'');// 内容不进行编码;
		curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_NOPROGRESS, false);
		curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,'curl_progress');curl_progress_start($ch);
		curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36');
		$res = curl_exec($ch);curl_progress_end($ch,$res);
		curl_close($ch);
		fclose($fp);

		$filesize = filesize_64(iconv_system($fileTemp));
		if($filesize < $length && $length!=0){
			return array('code'=>false,'data'=>'downloading');
		}
		if($res && filesize_64($fileTemp) != 0){
			// self::checkGzip($fileTemp);
			if(!@rename($fileTemp,$fileName)){
				@unlink($fileName);
				$res = @rename($fileTemp,$fileName);
				if(!$res){return array('code'=>false,'data'=>'rename error![curl]');}
			}
			return array('code'=>true,'data'=>'success');
		}
		return array('code'=>false,'data'=>'curl exec error!');
	}

	static function checkGzip($file){
		return; // 不处理gzip 流, 避免gz等文件下载被解压问题;
		if(file_sub_str($file,0,2) != "\x1f\x8b") return;

		ob_start();   
		readgzfile($file);   
		$out = ob_get_clean();
		file_put_contents($file,$out);
	}
}
FileParsePdf.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/FileParsePdf.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/


/**
 * 解析获取pdf文件信息;
 * 
 * pdfparser https://www.pdfparser.org/documentation
 * mpdf编辑: http://mpdf.github.io/
 */
class FileParsePdf{
	public static function parse($filePath){
		$chunkSize	= 32 * 1024;//trailer处理;
		$fileInfo   = array(
			'fp'		=> fopen($filePath,'r'),
			'path'		=> $filePath,
			'size'		=> filesize_64($filePath),
			'chunkSize' => $chunkSize,
		);
		$fileInfo['dataStart'] = StreamWrapperIO::read($filePath,0,$chunkSize);
		$fileInfo['dataEnd']   = StreamWrapperIO::read($filePath,$fileInfo['size'] - $chunkSize,$chunkSize);
		// if($_GET['debug'] == '1'){
		// 	include('/Library/WebServer/Documents/localhost/test/000/test/pdfparser-0.18.1/vendor/autoload.php');
		// 	$parser = new \Smalot\PdfParser\Parser();
		// 	$pdf = $parser->parseFile($filePath);pr($pdf->getDetails());exit;
		// }
		
		$xref = self::decodeXref($fileInfo);
		if($xref){
			$infoKey  = $xref['trailer']['info'];
			$dataInfo = self::getObjectValue($fileInfo,$xref,$infoKey);
		}
		
		$dataInfo = is_array($dataInfo) ? $dataInfo : array();
		// 页面尺寸处理;
		$dataInfo['sizeWidth'] = 0;		
		$theReg = '/[\s]*\/MediaBox[\s]*\[[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*\]/i';
		preg_match($theReg,$fileInfo['dataStart'],$matches);
		if (!$dataInfo['sizeWidth'] && count($matches) == 5){
			$dataInfo['sizeWidth']  = $matches[3];
			$dataInfo['sizeHeight'] = $matches[4];
		}
		preg_match($theReg,$fileInfo['dataEnd'],$matches);
		if (!$dataInfo['sizeWidth'] && count($matches) == 5){
			$dataInfo['sizeWidth']  = $matches[3];
			$dataInfo['sizeHeight'] = $matches[4];
		}
		preg_match('/%PDF-([0-9\.]+)/',$fileInfo['dataStart'],$matches);
		if($matches){$dataInfo['version'] = $matches[1];}

		// // 页数计算处理; /Count 8
		$dataInfo['pageNumber'] = 0;
		$theReg = "/[\s]*\/Count[\s]+([0-9]+)[\s]*/i";
		
		preg_match_all($theReg,$fileInfo['dataStart'],$matches);
		if($matches[1] && $dataInfo['pageNumber'] < $matches[1][0]){
			$dataInfo['pageNumber'] = $matches[1][0];
		}		
		preg_match_all($theReg,$fileInfo['dataEnd'],$matches);
		if($matches[1] && $dataInfo['pageNumber'] < $matches[1][0]){
			$dataInfo['pageNumber'] = $matches[1][0];
		}
		
		$dataInfo = self::parseInfoItem($dataInfo);
		return $dataInfo;
	}	
	private static function parseInfoItem($dataInfo){
		if(!$dataInfo) return false;
		$picker = array( //数值统一筛选并处理;
			'title' 		 => array('Title',''),				// 标题
			'auther'	 	 => array('Author',''),				// 作者
			'createTime'	 => array('CreationDate','date'),	// 创建日期
			'modifyTime' 	 =>	array('ModDate','date'),		// 修改日期
			'pageNumber'	 => array('pageNumber','int'),		// 页数
			'sizeWidth'		 => array('sizeWidth','int'),		// 页面宽度
			'sizeHeight'	 => array('sizeHeight','int'),		// 页面高度
			'creator'	 	 => array('Creator',''),			// 内容创作者
			'producer'	 	 => array('Producer',''),			// 编码软件
			'pdfVersion'	 => array('version',''),			// PDF 版本;
		);
		
		$result = array();
		foreach ($picker as $key => $info){
			if(!isset($dataInfo[$info[0]])) continue;
			$value = $dataInfo[$info[0]];
			if(!$value || is_array($value)) continue;
			
			switch($info[1]){
				case 'int' :$value = intval($value);break;
				case 'date':
					if(substr($value,0,2) == 'D:') {
						$value = substr($value,2,14);
					}
					if(strtotime($value)){
						$value = date('Y-m-d H:i:s',strtotime($value));
					}
					break;
			}
			$result[$key] = $value;
		}
		// pr($result,$dataInfo);exit;
		return $result;
	}
	
	private static function decodeXref(&$fileInfo){
		$pdfData = $fileInfo['dataEnd'];
		$xref   = array('trailer'=>array(),'xref'=>array());
		$theReg = '/[\r\n]startxref[\s]*[\r\n]*([0-9]+)[\s]*[\r\n]+%%EOF/i';
		if(!preg_match_all($theReg,$pdfData, $matches,PREG_SET_ORDER,0)) return false;

		// 结尾block索引开始位置,比最小block小则加大;
		$startxref = intval($matches[0][1]);
		if($fileInfo['size'] - $startxref > $fileInfo['chunkSize']){
			$chunkSize = 4 * $fileInfo['chunkSize'];
			$fileInfo['chunkSize'] = $chunkSize;
			$fileInfo['dataStart'] = StreamWrapperIO::read($fileInfo['path'],0,$chunkSize);
			$fileInfo['dataEnd']   = StreamWrapperIO::read($fileInfo['path'],$fileInfo['size'] - $chunkSize,$chunkSize);
			$pdfData = $fileInfo['dataEnd'];
		}

		$objNum = 0;
		// preg_match_all('/([0-9]+)[\x20]([0-9]+)[\x20]?([nf]?)(\r\n|[\x20]?[\r\n])/',$pdfData,$matches);
		preg_match_all('/([0-9]+)[\x20]([0-9]+)[\x20]?([nf]?)(\r\n|[\x20]?[\r\n])/',$pdfData,$matches);
		foreach ($matches[3] as $i=>$item){
			if($matches[3][$i] == 'n'){
				$index = $objNum.'_'.intval($matches[2][$i]);
				$xref['xref'][$index] = intval($matches[1][$i]);
                ++$objNum;
            }else if ($matches[3][$i] == 'f') {
                ++$objNum;
            } else {
				// $objNum = intval($matches[1][$i]); //从1开始;
            }
		}
		
		// 没有索引的情况处理; 优先按照的的进行匹配;
		if(preg_match_all('/[\r\n]([0-9]+)[\s]+([0-9]+)[\s]+obj/iU',$pdfData, $matches)){
			$fileOffset = $fileInfo['size'] - $fileInfo['chunkSize'];
			foreach ($matches[0] as $i => $theValue) {
				$key   = $matches[1][$i].'_'.$matches[2][$i];
				$xref['xref'][$key] = strpos($pdfData,$theValue) + $fileOffset + 1;
			}
		}

		if(preg_match_all('/trailer[\s]*<<(.*)>>/isU',$pdfData,$matches)){
			$trailerData = count($matches[1]) == 1 ? $matches[1][0] : $matches[1][1];
		}else{// 兼容没有trailer情况的数据; 直接从文件最后正则匹配查找;
			$trailerData = substr($pdfData, -1024*5);
		}
		if (preg_match('/Size[\s]+([0-9]+)/i', $trailerData, $matches) > 0) {
			$xref['trailer']['size'] = intval($matches[1]);
		}
		if (preg_match('/Root[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailerData, $matches) > 0) {
			$xref['trailer']['root'] = intval($matches[1]).'_'.intval($matches[2]);
		}
		if (preg_match('/Encrypt[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailerData, $matches) > 0) {
			$xref['trailer']['encrypt'] = intval($matches[1]).'_'.intval($matches[2]);
		}
		if (preg_match('/Info[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailerData, $matches) > 0) {
			$xref['trailer']['info'] = intval($matches[1]).'_'.intval($matches[2]);
		}
		if (preg_match('/ID[\s]*[\[][\s]*[<]([^>]*)[>][\s]*[<]([^>]*)[>]/i', $trailerData, $matches) > 0) {
			$xref['trailer']['id'] = array();
			$xref['trailer']['id'][0] = $matches[1];
			$xref['trailer']['id'][1] = $matches[2];
		}
		if(!$xref['trailer']['info']) return false;
		if (preg_match('/Prev[\s]+([0-9]+)/i', $trailerData, $matches) > 0) {
			// $xref = self::decodeXref($pdfData,intval($matches[1]), $xref);
		}
		return $xref;
	}
	
	private static function getObjectValue($fileInfo,$xref,$infoKey){
		$dataInfoRef  = self::getObject($fileInfo,$xref['xref'][$infoKey]);
		// pr($infoKey,$dataInfoRef);
		
		if(is_string($dataInfoRef[1])) return $dataInfoRef[1];
		if(!is_array($dataInfoRef[1])) return array();
		$dataInfo = array();
		for ($i = 0; $i< count($dataInfoRef[1]);$i+=2){
			$itemKey 	= $dataInfoRef[1][$i];
			$itemValue 	= $dataInfoRef[1][$i+1];
			if(count($itemKey) == 3 && $itemKey[0] == '/'){
				$value = false;
				if($itemValue[0] == 'objref'){
					$itemValue = self::getObject($fileInfo,$xref['xref'][$itemValue[1]]);
				}
				$value = $itemValue[1];			
				if($value === false) continue;
				if(is_string($value)){
					$value = self::decodeStr($value);
				}
				$dataInfo[$itemKey[1]] = $value;
			}
		}
		return $dataInfo;
	}
	private static function getObject($fileInfo,$offset){
		$dataIndex = self::getObjectItem($fileInfo,$offset);
		$dataIndex = self::getObjectItem($fileInfo,$dataIndex[2]);
		return $dataIndex;
	}
	private static function getObjectItem($fileInfo,$offset){
		// return self::getRawObject($fileInfo['dataEnd'],$offset);
		$chunkSize  = $fileInfo['chunkSize'];
		$fileOffset = $fileInfo['size'] - $chunkSize;		
		$thePose = $offset >= $fileOffset ? ($offset - $fileOffset) : $offset;
		$theData = $offset >= $fileOffset ? $fileInfo['dataEnd']: $fileInfo['dataStart'];
		if($offset > $chunkSize && $offset <= $fileOffset ){
			$thePose = 0;
			$theData = StreamWrapperIO::read($fileInfo['path'],$offset,$chunkSize);
			// pr("getFile:$offset;$chunkSize",substr($theData,200));
		}
		$dataIndex = self::getRawObject($theData,$thePose);
		
		// 重置offset;
		if($offset >= $fileOffset){
			$dataIndex[2] = $dataIndex[2] + $fileOffset;
		}else if($offset > $chunkSize && $offset <= $fileOffset){
			$dataIndex[2] = $dataIndex[2] + $offset;
		}
		// pr('getObjectItem:',[$offset,$chunkSize,$fileOffset,$thePose],$dataIndex);
		return $dataIndex;
	}
	
	// 解析节点数据;(xxx xx obj)
	private static function decodeStr($text){
		$text = str_replace(
			['\\\\', '\\ ', '\\/', '\(', '\)', '\n', '\r', '\t'],
			['\\',   ' ',   '/',   '(',  ')',  "\n", "\r", "\t"],$text);
			
		$parts = preg_split('/(\\\\\d{3})/s', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
        $text = '';
        foreach ($parts as $part) {
            if (preg_match('/^\\\\\d{3}$/', $part)) {
                $text .= \chr(octdec(trim($part, '\\')));
            } else {
                $text .= $part;
            }
		}
		$parts = preg_split('/(#\d{2})/s', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
        $text = '';
        foreach ($parts as $part) {
            if (preg_match('/^#\d{2}$/', $part)) {
                $text .= \chr(hexdec(trim($part, '#')));
            } else {
                $text .= $part;
            }
		}
		
		$parts = preg_split('/(<[a-f0-9]+>)/si', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
		$text = '';
        foreach ($parts as $part) {
            if (preg_match('/^<.*>$/s', $part) && false === stripos($part, '<?xml')) {
                $part = preg_replace("/[\r\n]/", '', $part);
                $part = trim($part, '<>');
                $part = pack('H*', $part);
                $text .= $part;
            } else {
                $text .= $part;
            }
		}
		if(preg_match('/^\xFE\xFF/i', $text)) {
            // Strip U+FEFF byte order marker.
            $decode = substr($text, 2);
            $text = '';
            $length = strlen($decode);
            for ($i = 0; $i < $length; $i += 2) {
				$hex   = hexdec(bin2hex(substr($decode, $i, 2)));
				$text .= mb_convert_encoding('&#'.intval($hex).';', 'UTF-8', 'HTML-ENTITIES');
            }
		}
		return $text;
	}
	private static function getRawObject($pdfData, $offset = 0){
        $objtype = ''; // object type to be returned
        $objval = ''; // object value to be returned
        /*
         * skip initial white space chars:
         *      \x00 null (NUL)
         *      \x09 horizontal tab (HT)
         *      \x0A line feed (LF)
         *      \x0C form feed (FF)
         *      \x0D carriage return (CR)
         *      \x20 space (SP)
         */
        $offset += strspn($pdfData, "\x00\x09\x0a\x0c\x0d\x20", $offset);
		$char = $pdfData[$offset];
		// echo "<pre>";var_dump($char,'pos='.$offset.';len='.strlen($pdfData),substr($pdfData,$offset,33));echo "</pre>";
		
        switch ($char) {
            case '%':  // \x25 PERCENT SIGN
                    // skip comment and search for next token
                    $next = strcspn($pdfData, "\r\n", $offset);
                    if ($next > 0) {
                        $offset += $next;
                        return self::getRawObject($pdfData, $offset);
                    }
                    break;
            case '/':  // \x2F SOLIDUS
                    $objtype = $char;
                    ++$offset;
                    $pregResult = preg_match(
                        '/^([^\x00\x09\x0a\x0c\x0d\x20\s\x28\x29\x3c\x3e\x5b\x5d\x7b\x7d\x2f\x25]+)/',
                        substr($pdfData, $offset, 256),
                        $matches
                    );
                    if (1 == $pregResult) {
                        $objval = $matches[1]; // unescaped value
                        $offset += strlen($objval);
                    }
                    break;
            case '(':   // \x28 LEFT PARENTHESIS
            case ')':  // \x29 RIGHT PARENTHESIS
                    // literal string object
                    $objtype = $char;
                    ++$offset;
                    $strpos = $offset;
                    if ('(' == $char) {
                        $open_bracket = 1;
                        while ($open_bracket > 0) {
							if (!isset($pdfData[$strpos])) break;
                            $ch = $pdfData[$strpos];
                            switch ($ch) {
                                case '\\':  // REVERSE SOLIDUS (5Ch) (Backslash)
									// skip next character
									++$strpos;
									break;
                                case '(':  // LEFT PARENHESIS (28h)
									++$open_bracket;
									break;
                                case ')':  // RIGHT PARENTHESIS (29h)
									--$open_bracket;
									break;
                            }
                            ++$strpos;
                        }
                        $objval = substr($pdfData, $offset, ($strpos - $offset - 1));
                        $offset = $strpos;
                    }
                    break;
            case '[':   // \x5B LEFT SQUARE BRACKET
            case ']':  // \x5D RIGHT SQUARE BRACKET
                // array object
                $objtype = $char;
                ++$offset;
                if ('[' == $char) {
                    // get array content
                    $objval = array();
                    do {
                        $oldOffset = $offset;
                        // get element
                        $element = self::getRawObject($pdfData, $offset);
                        $offset = $element[2];
                        $objval[] = $element;
                    } while ((']' != $element[0]) && ($offset != $oldOffset));
                    // remove closing delimiter
                    array_pop($objval);
                }
                break;
            case '<':  // \x3C LESS-THAN SIGN
            case '>':  // \x3E GREATER-THAN SIGN
                if (isset($pdfData[($offset + 1)]) && ($pdfData[($offset + 1)] == $char)) {
                    // dictionary object
                    $objtype = $char.$char;
                    $offset += 2;
                    if ('<' == $char) {
                        $objval = array();
                        do {
                            $oldOffset = $offset;
                            // get element
                            $element = self::getRawObject($pdfData, $offset);
                            $offset = $element[2];
                            $objval[] = $element;
                        } while (('>>' != $element[0]) && ($offset != $oldOffset));
                        // remove closing delimiter
                        array_pop($objval);
                    }
                } else {
                    // hexadecimal string object
                    $objtype = $char;
                    ++$offset;
                    $pregResult = preg_match('/^([0-9A-Fa-f\x09\x0a\x0c\x0d\x20]+)>/iU',substr($pdfData, $offset),$matches);
                    if (('<' == $char) && 1 == $pregResult) {
                        // remove white space characters
                        $objval = strtr($matches[1], "\x09\x0a\x0c\x0d\x20", '');
                        $offset += \strlen($matches[0]);
                    } elseif (false !== ($endpos = strpos($pdfData, '>', $offset))) {
                        $offset = $endpos + 1;
                    }
                }
                break;
            default:
				if ('endobj' == substr($pdfData, $offset, 6)) {
					// indirect object
					$objtype = 'endobj';
					$offset += 6;
				} elseif ('null' == substr($pdfData, $offset, 4)) {
					// null object
					$objtype = 'null';
					$offset += 4;
					$objval = 'null';
				} elseif ('true' == substr($pdfData, $offset, 4)) {
					// boolean true object
					$objtype = 'boolean';
					$offset += 4;
					$objval = 'true';
				} elseif ('false' == substr($pdfData, $offset, 5)) {
					// boolean false object
					$objtype = 'boolean';
					$offset += 5;
					$objval = 'false';
				} elseif ('stream' == substr($pdfData, $offset, 6)) {
					// start stream object
					$objtype = 'stream';
					$offset += 6;
					if (1 == preg_match('/^([\r]?[\n])/isU', substr($pdfData, $offset), $matches)) {
						$offset += strlen($matches[0]);
						$endStreamReg = '/(endstream)[\x09\x0a\x0c\x0d\x20]/isU';
						$pregResult = preg_match($endStreamReg,substr($pdfData, $offset),$matches,PREG_OFFSET_CAPTURE);
						if (1 == $pregResult) {
							$objval = substr($pdfData, $offset, $matches[0][1]);
							$offset += $matches[1][1];
						}
					}
				} elseif ('endstream' == substr($pdfData, $offset, 9)) {
					// end stream object
					$objtype = 'endstream';
					$offset += 9;
				} elseif (1 == preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+R/iU', substr($pdfData, $offset, 33), $matches)) {
					$objtype = 'objref';
					$offset += strlen($matches[0]);
					$objval = intval($matches[1]).'_'.intval($matches[2]);
				} elseif (1 == preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+obj/iU', substr($pdfData, $offset, 33), $matches)) {
					$objtype = 'obj';
					$objval = intval($matches[1]).'_'.intval($matches[2]);
					$offset += strlen($matches[0]);
				} elseif (($numlen = strspn($pdfData, '+-.0123456789', $offset)) > 0) {
					$objtype = 'numeric';
					$objval = substr($pdfData, $offset, $numlen);
					$offset += $numlen;
				}
				break;
        }
        return array($objtype, $objval, $offset);
    }
}
GetInfo.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/GetInfo.class.php'
View Content
<?php

/**
getID3 获取图片,音频,视频信息 https://www.getid3.org/
修改:
	0. getid3/getid3.php  openfile方法474行后面,加入:
		$this->info['filepath']='';$this->info['filenamepath'] = $filename; //add by warlee;
 	1. getid3/module.audio-video.flv.php  Analyze方法311行后加入
		if($found_video && $found_audio && $found_meta){break;}//add by warlee; 找到后停止;
		搜索:$info['playtime_seconds'] = $Duration / 1000;
	2. getid3/module.graphic.gif.php  Analyze方法93行后加入: return; // add by warlee;
 */
class GetInfo{
	private static $fileTypeArray;
	public static function get($file){
		$info = IO::info($file);
		return self::infoAdd($info);
	}
	public static function check(){}
	public static function infoAdd(&$info){
		static $obj;
		if (!$obj) {
			require SDK_DIR.'/getID3/getid3/getid3.php';
			$obj = new getID3();
		}
		if(!$info || $info['type'] != 'file') return;
		if(!self::support($info['ext'])) return;

		$theFile = 'kodio://'.$info['path']; // 地址处理;
		$fileType = $info['ext'];
		try {
			if($info['ext'] == 'psd'){
				$fileInfo = self::psdParse($theFile);
				$fileType = 'image';
			}else if($info['ext'] == 'pdf'){
				$fileInfo = self::pdfParse($theFile);
			}else{
				$fileType = self::$fileTypeArray['extType'][$info['ext']];
				$keyArray = self::$fileTypeArray['support'][$fileType]['keyMap'];
				$getInfo  = @$obj->analyze($theFile,$info['size'],$info['name']);
				$fileInfo = self::parseData($getInfo,$keyArray,$info);
				// pr($theFile,$getInfo,$fileInfo,exif_read_data($info['path']),$fileType);
				// $filePath = 'data://image/jpeg;base64,'.base64_encode(StreamWrapperIO::read($theFile,0,1024*64));
				// pr(exif_read_data($filePath),exif_read_data($info['path']),exif_read_data($theFile),$getInfo);exit;
			}
		} catch (Exception $e){
			$fileInfo = array('fileType'=>$fileType);
		}
		if(!$fileInfo) return;
		
		$fileInfo = self::arrayValueLimit($fileInfo);//长度限制处理;
		$fileInfo['fileType'] = $fileType;
		$info['fileInfoMore'] = $fileInfo;
		return $info;
	}
	public static function support($ext){
		if(!self::$fileTypeArray){
			self::$fileTypeArray = self::fileTypeParse();
		}
		$ext = strtolower($ext);
		$support = array('pdf','psd');
		if(in_array($ext,$support)) return true;
		if(isset(self::$fileTypeArray['extType'][$ext])) return true;
		return false;			
	}
	
	// 长度限制处理;
	public static function arrayValueLimit($fileInfo,$maxLength=5000){
		if(!is_array($fileInfo)) return $fileInfo;
		foreach($fileInfo as $key=>$value){
			if(is_string($value) && strlen($value) > $maxLength){
				$fileInfo[$key] = substr($value, 0, $maxLength);
			}else if(is_array($value)){
				$fileInfo[$key] = self::arrayValueLimit($value,$maxLength);
			}
		}
		return $fileInfo;
	}

	
	// 数据解析处理;
	private static function parseData($getInfo,$keyArray,$fileInfo){
		$ext = $getInfo['fileformat'];
		$result = array();
		
		foreach ($keyArray as $key => $matchKeyArray){
			foreach($matchKeyArray as $matchKey){
				if(is_array($matchKey)){
					$value = self::parseData($getInfo,$matchKeyArray,$fileInfo);
					if($value){$result[$key] = $value;}
					break;
				}				
				$matchKeyTrue = str_replace('@',$ext,$matchKey);
				$matchKeyIngore = str_replace('@.','',$matchKey);
				$value1 = _get($getInfo,$matchKeyTrue);
				$value2 = _get($getInfo,$matchKeyIngore);
				$value  = $value1 ? $value1 : ($value2 ? $value2 : $value1);
				$value = is_array($value) ? $value[0] : $value;
				if($value){break;}
			}			
			if($value || $value === 0 || $value === false){
				$value = self::valueReset($matchKey,$value,$key);
				if($value !== null){
					$result[$key] = $value;
				}
			}
		}
		$result = self::valueResetAll($result,$fileInfo);
		return $result;
	}
	

	// psd文件信息;(尺寸获取)
	// 其他信息: https://github.com/hasokeric/php-psd/blob/master/PSDReader.php
	// getimagesize : https://www.runoob.com/php/php-getimagesize.html
	private static function psdParse($file){
		if(IO::fileSubstr($file,0,4)!='8BPS') return false;
		$info = getImageSize($file,$imageinfo);
		if(!$info) return;
		$result = array(
			'sizeWidth' 	=> $info[0],
			'sizeHeight' 	=> $info[1],
			// 'info' 			=> $info,
		);
		return $result;
	}
	private static function pdfParse($file){
		return FileParsePdf::parse($file);
	}
	private static function fileTypeParse(){
		$support = self::fileTypeArray();
		$extType = array();
		foreach ($support as $type => $item){
			$ext = _get($item,'ext','');
			$extArray = is_string($ext) ? explode(',',$ext) :$ext;
			if(!$extArray){continue;}
			foreach ($extArray as $ext){
				$ext = strtolower(trim($ext));
				if(!$ext) continue;
				$extType[$ext] = $type;
			}
			$support[$type]['ext']    = array_keys($extType);
			$support[$type]['keyMap'] = self::parseKeyMap($item['keyMap']);
		}
		return array('support'=>$support,'extType'=>$extType);
	}
	private static function parseKeyMap($keyMap){
		foreach ($keyMap as $theKey => $matchKeys) {
			if(is_array($matchKeys)){
				$value = self::parseKeyMap($matchKeys);
				if($value){$keyMap[$theKey] = $value;}
				continue;
			}
			$keyArray = explode(',',$matchKeys);
			$keyArrayResult = array();
			foreach ($keyArray as $matchKey){
				$matchKey = trim($matchKey);
				if(!$matchKey) continue;
				// 组合key自动转为数组处理; tags.[id3v1|id3v2|ape].title
				if(preg_match('/\[(.*)\]/',$matchKey,$matchs)){ // 组合key;
					$replaceArray = explode('|',$matchs[1]);
					foreach ($replaceArray as $addKey) {
						$keyArrayResult[] = str_replace($matchs[0],$addKey,$matchKey);
					}
				}else{
					$keyArrayResult[] = $matchKey;
				}
			}
			$keyMap[$theKey] = $keyArrayResult;
		}
		return $keyMap;
	}
	
	
	private static function valueResetAll($info,$fileInfo){
		if(isset($info['playtime'])){
			$time 	= ceil($info['playtime']);
			$hour 	= intval($time / 3600);
			$minute = intval(($time - $hour*3600) / 60);
			$second = $time % 60;
			$info['playtimeShow'] = sprintf('%02d:%02d:%02d',$hour,$minute,$second);
			if($hour == 0){
				$info['playtimeShow'] = sprintf('%02d:%02d',$minute,$second);
			}
		}
		
		// 音乐专辑封面处理;
		$audioImage = _get($info,'tags.image');
		if($audioImage){
			$cacheFile	=  IO_PATH_SYSTEM_TEMP . 'thumb/audio/'.KodIO::hashPath($fileInfo).'.jpg';
			$info['fileThumb'] = Action('toolsCommonPlugin')->pluginCacheFileSet($cacheFile,$audioImage);
			unset($info['tags']['image']);
		}
		
		// 编码处理;
		$audioTitle = _get($info,'tags.title','');
		$audioArtist= _get($info,'tags.artist','');
		$audioAlbum = _get($info,'tags.album','');
		$tagInfo 	= $audioTitle.$audioArtist.$audioAlbum;
		if($tagInfo && get_charset($tagInfo) != 'utf-8'){
			$info['tags']['title'] 	= iconv_to($info['tags']['title'],get_charset($tagInfo),'utf-8');
			$info['tags']['artist'] = iconv_to($info['tags']['artist'],get_charset($tagInfo),'utf-8');
			$info['tags']['album'] 	= iconv_to($info['tags']['album'],get_charset($tagInfo),'utf-8');
		}
		
		return $info;
	}
	
	/**
	 * 数值处理
	 * 时间处理;  时间戳:大圣归来.mp4;  负数:3G2 Video.3g2; 
	 * 音乐封面图片加入系统缓存处理; hashSimple=> img=>url;
	 */
	private static function valueReset($key,$value,$newKey){
		$timeFormate = 'Y-m-d H:i:s';
		switch ($key) {
			case '@.exif.EXIF.ColorSpace':$value = $value == '1'?'sRGB':'RGB';break;
			case 'audio.channels':$value = $value ? $value:null;break;
			// case 'audio.bitrate':
			// case 'video.bitrate':$value = round($value / 1000) .' kbps';break;
			default:break;
		}
		
		switch ($newKey) {
			case 'frameRate':$value = round($value,2);break;
			case 'createTime':
			case 'modifyTime':
				if($value < 0){
					$value = null;
				}else if(is_numeric($value)){
					$value = date($timeFormate,$value);
				}
				break;
			default:break;
		}
		
		return $value;
	}
		
	// 数据整理;
	private static function fileTypeArray(){
		$gps = array(//经度,东西N/E;纬度,南北纬S/N;海拔
			'longitude'	=> '@.exif.GPS.computed.longitude,tags.quicktime.gps_longitude',
			'latitude'	=> '@.exif.GPS.computed.latitude,tags.quicktime.gps_latitude',
			'altitude'	=> '@.exif.GPS.computed.altitude,tags.quicktime.gps_altitude',
		);
		return array(
		// @:代表替换为扩展名; eg: @.exif.IFD0.Software 在png下代表 png.exif.IFD0.Software
		// , 冒号代表多个取值时从前到后依次获取直到满足; eg: 
		// [aa|bb] 自动展开占位;  eg: tags.[id3v1|id3v2].title => tags.id3v1.title,tags.id3v2.title
		
		// 没有key则不取该值; 冒号依次向后取到key为止; 取得值为数组则取第一个值;
		// exif详解: https://www.cnblogs.com/billgore/p/4301622.html
		// https://m.sojson.com/image/exif.html
		'image'	=> array(
			'ext'	=> 'jpg,jpeg,png,gif,bmp,tiff,tif,psd,pcd,svg,efax,webp,'.			// exif解析处理;TODO;
					   'cr2,erf,kdc,dcr,dng,nrw,nef,orf,rw2,pef,srw,arw,sr2,heic,heif,hevc', // heic,heif,hevc
			'keyMap'	=> array(
				'sizeWidth' 	=> 'video.resolution_x',
				'sizeHeight' 	=> 'video.resolution_y',
				'resolutionX' 	=> '@.exif.IFD0.XResolutionx,@.exif.IFD0.XResolution',		// 分辨率 dpi
				'resolutionY' 	=> '@.exif.IFD0.XResolutiony,@.exif.IFD0.YResolution',		// 分辨率 dpi
				'createTime' 	=> '@.exif.IFD0.DateTime,@.EXIF.DateTimeOriginal,
									@.exif.EXIF.DateTimeOriginal,
									xmp.xmp.CreateDate,tags.@.datetime', 					// 拍摄日期
				'modifyTime' 	=> '.xmp.xmp.ModifyDate', 									// 修改日期		
				'orientation' 	=> '@.exif.IFD0.Orientation', 								// 方向
				'software' 		=> '@.exif.IFD0.Software,
									tags.@.Software,tags.@.software,
									xmp.xmp.CreatorTool',						// 内容创建者
				'bitPerSample' 	=> 'video.bits_per_sample',						// 位深度
				'bitsPerPixel'  => '@.header.bits_per_pixel,@.header.raw.bits_per_pixel',//位深度
				
				// 色彩空间;色彩描述文件;
				'colorSpace'	=> '@.exif.EXIF.ColorSpace,@.sRGB.header.type_text,
									@.IHDR.color_type,@.header.compression',
				
				// gif; https://blog.csdn.net/yuejisuo1948/article/details/83617359
				'colorSize'		=> '@.header.global_color_size',
				'animated'		=> '@.animation.animated',
				
				// gps
				'gps' 			=> $gps,
				'deviceMake' 	=> '@.exif.IFD0.Make,tags.@.make', 							// 设备制造商
				'deviceType' 	=> '@.exif.IFD0.Model,tags.@.model',						// 设备型号
				'imageDesc' 	=> '@.exif.IFD0.ImageDescription,tags.@.imagedescription',	// 图片说明
				'imageArtist' 	=> '@.exif.IFD0.artist,tags.@.artist',						// 图片作者
				
				// 拍摄信息; https://blog.csdn.net/dreamboycx/article/details/40591875
				'camera' => array(
					'ApertureFNumber'		=>'@.exif.COMPUTED.ApertureFNumber,exif.EXIF.FNumber',		// 光圈数; f/2.2
					'ApertureValue'			=>'@.exif.EXIF.ApertureValue',				// 光圈值; 2.275
					'ShutterSpeedValue'		=>'@.exif.EXIF.ShutterSpeedValue',			// 快门速度; 5.05
					'ExposureTime'			=>'@.exif.EXIF.ExposureTime',				// 曝光时间s; 0.033  转换为:1/33
					'FocalLength'			=>'@.exif.EXIF.FocalLength',				// 焦距 mm; eg:4.15					
					'FocalLengthIn35mmFilm'	=>'@.exif.EXIF.FocalLengthIn35mmFilm',		// 等价35mm焦距 mm; eg:29
					'FocusDistance'			=>'@.exif.COMPUTED.FocusDistance',			// 对焦距离
					'ISOSpeedRatings'		=>'@.exif.EXIF.ISOSpeedRatings',			// ISO感光度
					'WhiteBalance'			=>'@.exif.EXIF.WhiteBalance',				// 白平衡    1-手动;0-自动
					'ExposureMode'			=>'@.exif.EXIF.ExposureMode',				// 曝光模式  1-手动;0-自动
					'ExposureBiasValue'		=>'@.exif.EXIF.ExposureBiasValueEV',		// 曝光补偿
				),
			),
		),
		'audio'	=> array(
			'ext'	=> 'aac,adts,au,amr,avr,bonk,dsf,dss,ff,dts,flac,la,lpac,midi,mac,it,xm,s3m,'.
					   'mpc,mp3,ofr,rkau,shn,tak,wav,wma,m4a,ogg,vqf,wv,voc,tta',
			'keyMap' => array(
				'playtime' 		=> 'playtime_seconds,@.pageheader.0.page_length',					// 时长,向上取整;
				'createTime' 	=> 'quicktime.timestamps_unix.create.moov mvhd',		// 内容创建时间
				'modifyTime' 	=> 'quicktime.timestamps_unix.modify.moov mvhd',		// 内容修改时间
				'software' 		=> 'audio.matroska.encoder,
									tags.matroska.[encoder|writingapp,handler_name],
									tags.quicktime.[software|encoding_tool],
									audio.encoder',							// 编码软件					
				'rate' 			=> 'audio.sample_rate',						// 采样速率
				'channel' 		=> 'audio.channels',						// 音频声道; 为1则不显示;
				'dataformat' 	=> 'audio.codec',							// 编解码器 h264
				'bitPerSimple' 	=> 'audio.bits_per_sample',					// 每个样本的位数
				'bitrate' 		=> 'audio.bitrate,@.pageheader.theora.nominal_bitrate',	// 比特率 kbps
				
				// 'channelmode' 	=> 'audio.channelmode',					// 
				'tags' 	=> array(
					'title' 		=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].title',			// 标题
					'artist' 		=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].artist',			// 作者/艺术家
					'album' 		=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].album',			// 专辑
					'genre' 		=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].genre',			// 风格
					'year' 			=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].year',			// 年代
					'track'			=> '[id3v1|id3v2|ape|vorbiscomment|quicktime].track_number',	// 音轨
					'image' 		=> 'comments.picture.0.data',									// 专辑封面
				),
			)
		),
		'video'	=> array(
			'ext'	=> 'mp4,flv,f4v,wmv,ogv,mov,avi,rmvb,mkv,rm,webm,m4v,real,swf,'.
					   '3g2,3gp,asf,bink,ivf,nsv,wtv,mts,mpe,mpeg,mpg,vob', //m2ts ts
			'keyMap'	=> array( //TODO; 编解码器;
				'sizeWidth' 	=> '@.meta.onMetaData.width,video.resolution_x',
				'sizeHeight' 	=> '@.meta.onMetaData.height,video.resolution_y',
				'playtime' 		=> 'playtime_seconds',									// 时长,向上取整;
				'createTime' 	=> 'quicktime.timestamps_unix.create.moov mvhd,tags.matroska.creation_time',// 内容创建时间
				'modifyTime' 	=> 'quicktime.timestamps_unix.modify.moov mvhd,tags.matroska.modify_time',  // 内容修改时间
				'frameRate' 	=> 'video.frame_rate',						// 帧率	向上取整;
				'bitrate' 		=> '@.video.raw.bitrate,video.bitrate',		// 数据比特率 kbps

				'gps' 			=> $gps,
				'dataformat' 	=> 'video.fourcc_lookup,
									video.streams.[01|1].dataformat,
									video.streams.[01|1].fourcc,
									video.streams.[01|1].codec,
									quicktime.video.codec_fourcc_lookup,
									video.fourcc,video.codec',			// 编解码器 h264
				'software' 		=> 'tags.matroska.[writingapp|handler_name|encoder],
									@.meta.onMetaData.[encoder|metadatacreator|creator],
									tags.[riff|quicktime].software,
									tags.@.encodingsettings,
									@.comments.encodingsettings,
									tags.quicktime.encoding_tool',		// 编码软件
				'audio' 	=>	array(
					'channel' 		=> 'audio.channels',				// 音频声道; 1 (单声道),2 (立体声),3+ (多声道)
					'channelmode' 	=> 'audio.channelmode',				// 音频模式
					'rate' 			=> 'audio.sample_rate',				// 声音.采样速率
					'bitrate' 		=> 'audio.bitrate',					// 声音.比特率 kbps
					'dataformat' 	=> 'audio.codec,audio.dataformat',	// 音频 编码译码器;
					'bitPerSimple' 	=> 'audio.bits_per_sample',			// 每个样本的位数
				),
			),
		),
		'archive' 	=> array(
			// 'ext'	 => 'zip,tar,rar,gz,7z,tar,xz,par2,szip,cue,iso,hpk',
			'keyMap' => array(
				'unzipSize' 			=> 'uncompressed_size',			// 解压后大小
				'childrenCount' 		=> 'entries_count',				// 字内容数
			)
		),
		'torrent' 	=> array(
			// 'ext'	=> 'torrent',
			'keyMap' => array()
		));
	}
}
// exif_read_data 读取streamwrapper的文件会异常;
// 参考: https://github.com/avalanche123/Imagine/issues/728
function exif_read_data_io($file,$sections = null,$asArray=false,$readThumbnail=false){
	$fileStr  = StreamWrapperIO::read($file,0,1024*256);
	$fileData = 'data://image/jpeg;base64,'.base64_encode($fileStr);
	$result   = exif_read_data($fileData,$sections,$asArray,$readThumbnail);
	return $result;
}
function getimagesize_io($filePath,&$info=false){
	$fileStr  = StreamWrapperIO::read($filePath,0,1024*2560);
	$fileData = 'data://image/jpeg;base64,'.base64_encode($fileStr);
	$result   = getimagesize($fileData,$info);
	return $result;
}
Hook.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Hook.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * hook::add('function','function')
 * hook::add('class:function','class.function')
 *
 * hook::run('class.function',param)
 * hook::run('function',param)
 * 
 */

class Hook{
	static private $events = array();
	static public function get($event=false){
		if(!$event){
			return self::$events;
		}else{
			return self::$events[$event];
		}
	}
	static public function apply($action,$args=array()) {
		$result = ActionApply($action,$args);
		if(is_string($action)){
			Hook::trigger($action); // 调用某个事件后触发的动作继续触发;
		}
		return $result;
	}
	
	/**
	 * 绑定事件到方法;$action 为可调用内容;
	 */
	static public function bind($event,$action,$once=false) {
		if(!is_string($event)) return false;
		if(!isset(self::$events[$event])){
			self::$events[$event] = array();
		}
		self::$events[$event][] = array(
			'action' => $action,
			'once' 	 => $once,
			'times'	 => 0
		);
	}
	static public function once($event,$action) {
		self::bind($event,$action,true);
	}
	static public function unbind($event,$action = false) {
		if(!is_string($event)) return false;
		//解绑所有;
		if(!$action){
			self::$events[$event] = array();
			return;
		}
		// 解绑指定事件;
		$eventsMatch = self::$events[$event];
		self::$events[$event] = array();
		if(!is_array($eventsMatch)) return;

		for ($i=0; $i < count($eventsMatch); $i++){
			if($eventsMatch[$i]['action'] == $action) continue;
			self::$events[$event][] = $eventsMatch[$i];
		}
	}
	
	//数据处理;只支持传入一个参数
	static public function filter($event,$param) {
		$events = self::$events;
		if(!is_string($event)) return false;
		if(!isset($events[$event])) return $param;
		if(count(self::$events[$event]) == 0) return $param;

		$result  = $param;
		$actions = self::$events[$event];
		$actionsCount = count($actions);
		for ($i=0; $i < $actionsCount; $i++) {
			$action = $actions[$i];
			if($action['once'] && $action['times'] > 1) continue;
			
			self::$events[$event][$i]['times'] = $action['times'] + 1;
			$temp = ActionApply($action['action'],array($result));
			Hook::trigger($action['action']);
			
			// 类型相同才替换;
			// pr($action['action'],gettype($result),gettype($temp));
			if(gettype($temp) == gettype($result)){$result = $temp;}
		}
		return $result;
	}
	
	static public function trigger($event) {
		static $_logHook = 100;
		if($_logHook === 100){$_logHook = defined("GLOBAL_LOG_HOOK") && GLOBAL_LOG_HOOK;}
		if(!is_string($event)) return false;
		if(!isset(self::$events[$event])) return false;
		if(count(self::$events[$event]) == 0) return false;
		
		$result  = false;
		$actions = self::$events[$event];
		$actionsCount = count($actions);
		$args = func_get_args();
		array_shift($args);
		for ($i=0; $i < $actionsCount; $i++) {
			$action = $actions[$i];
			if($action['once'] && $action['times'] > 1) continue;
			if($_logHook){write_log($event.'==>start: '.$action['action'],'hook-trigger');}

			self::$events[$event][$i]['times'] = $action['times'] + 1;
			try{
				$res = ActionApply($action['action'],$args);
			}catch(Exception $e){
				$error = '['.$action['action'].']: '.$e->getMessage();
				$res = self::trigger('eventRun.error',$error);
				if(!$res){throw new Exception($error);}
			}
			if(is_string($action['action'])){Hook::trigger($action['action']);}
			
			if($_logHook){
				write_log(get_caller_info(),'hook-trigger');
				if($action['times'] == 200){//避免循环调用
					$msg = is_array($action['action']) ? json_encode_force($action['action']):$action['action'];
					write_log("Warning,Too many trigger on:".$event.'==>'.$msg,'warning');
				}
			}
			$result = is_null($res) ? $result:$res;
		}
		return $result;
	}
}
HttpAuth.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/HttpAuth.class.php'
View Content
<?php

class HttpAuth {
    public static function get() {
        $user     = '';
        $password = '';
        //Apache服务器
        if (isset($_SERVER['PHP_AUTH_USER'])) {
            $user = $_SERVER['PHP_AUTH_USER'];
			$password = $_SERVER['PHP_AUTH_PW'];
        } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
            //其他服务器如 Nginx  Authorization
			$httpAuth = $_SERVER['HTTP_AUTHORIZATION'];
            if (strpos(strtolower($httpAuth), 'basic') === 0) {
                $auth = explode(':', base64_decode(substr($httpAuth, 6)));
                $user = isset($auth[0])?$auth[0]:'';
                $password = isset($auth[1])?$auth[1]:0;
			}
        }
        return array('user'=>$user, 'pass'=>$password);
	}
	
	public static function error() {
		//pr_trace();exit;
		header('WWW-Authenticate: Basic realm="kodcloud"');
		header('HTTP/1.0 401 Unauthorized');
		header('Pragma: no-cache');
		header('Cache-Control: no-cache');
		header('Content-Length: 0');
		exit;
	}
	
	public static function make($user,$pass){
		return "Authorization: Basic " . base64_encode($user.':'.$pass);
	}
}
HttpHeader.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/HttpHeader.class.php'
View Content
<?php

class HttpHeader{
	public static $_headers = array(
        'Host'                => 'HTTP_HOST',
        'User-Agent'          => 'HTTP_USER_AGENT',
        'Content-Type'        => 'HTTP_CONTENT_TYPE',
        'Content-Length'      => 'HTTP_CONTENT_LENGTH',
        'Depth'               => 'HTTP_DEPTH',
        'Expect'              => 'HTTP_EXPECT',
        'If-None-Match'       => 'HTTP_IF_NONE_MATCH',
        'If-Match'            => 'HTTP_IF_MATCH',
        'If-Range'            => 'HTTP_IF_RANGE',
        'Last-Modified'       => 'HTTP_LAST_MODIFIED',
        'If-Modified-Since'   => 'HTTP_IF_MODIFIED_SINCE',
        'If-Unmodified-Since' => 'HTTP_IF_UNMODIFIED_SINCE',
        'Range'               => 'HTTP_RANGE',
        'Timeout'             => 'HTTP_TIMEOUT',
        'If'                  => 'HTTP_IF',
        'Lock-Token'          => 'HTTP_LOCK_TOKEN',
        'Overwrite'           => 'HTTP_OVERWRITE',
        'Destination'         => 'HTTP_DESTINATION',
        'Request-Id'          => 'REQUEST_ID',
        'Request-Body-File'   => 'REQUEST_BODY_FILE',
        'Redirect-Status'     => 'REDIRECT_STATUS',
    );

	public static function init(){
		static $init = false;
		if($init) return;
		foreach ($_SERVER as $key=>$val){
			$key = strtoupper($key);
			if(!array_key_exists($key,$_SERVER)) continue;
			$_SERVER[$key]= $val;
		}
		foreach (self::$_headers as $key=>$keyRequest){
			if(!array_key_exists($key,$_SERVER)) continue;
			$_SERVER[$key]= $_SERVER[$keyRequest];
			$_SERVER[strtoupper($key)]= $_SERVER[$keyRequest];
		}
	}
	
	
	public static function get($key){
		self::init();
		return $_SERVER[$key] ? $_SERVER[$key] : $_SERVER['HTTP_'.strtoupper($key)];
	}
	
	
	public static function method(){
		return strtoupper(self::get('REQUEST_METHOD'));
	}
	public static function length(){
		$result = self::get('X-Expected-Entity-Length');
		if (!$result) {
			$result = self::get('Content-Length');
		}
		return $result;
	}
	public static function range(){
		$range = self::get('Range');
        if (!$range) return false;
        if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i', $range, $matches)) return false;
        if ($matches[1] === '' && $matches[2] === '') return false;
        
        return array(
            $matches[1] !== '' ? $matches[1] : null,
            $matches[2] !== '' ? $matches[2] : null,
        );
	}
	
    public static $statusCode = array(
    	/**
    	https://www.cnblogs.com/chengkanghua/p/11314230.html
    	
    	1xx 临时响应;用于指定客户端应相应的某些动作
		2xx 成功;用于表示请求成功
		3xx 重定向;表示要完成请求,需要进一步操作
		4xx 请求错误; 表示请求可能出错,妨碍了服务器的处理
		5xx 服务器错误;服务器处理请求时内部错误
		*/
		'100' => 'Continue',
		'101' => 'Switching Protocol',
		'102' => 'Processing',
		'103' => 'Early Hints',
		
        '200' => 'OK',
        '201' => 'Created',
        '202' => 'Accepted',
        '203' => 'Non-Authoritative Information',
        '204' => 'No Content',
        '205' => 'Reset Content',
        '206' => 'Partial Content',
        '207' => 'Multi-Status',
        
        '300' => 'Multiple Choices',
        '301' => 'Moved Permanently',
        '302' => 'Found',
        '303' => 'See Other',
        '304' => 'Not Modified',
        '305' => 'Use Proxy',
        '307' => 'Temporary Redirect',
        '308' => 'Permanent Redirect',
        
        '400' => 'Bad Request',
        '401' => 'Unauthorized',
        '402' => 'Payment Required',
        '403' => 'Forbidden',
        '404' => 'Not Found',
        '405' => 'Method Not Allowed',
        '406' => 'Not Acceptable',
        '407' => 'Proxy Authentication Required',
        '408' => 'Request Timeout',
        '409' => 'Conflict',
        '410' => 'Gone',
        '411' => 'Length Required',
        '412' => 'Precondition Failed',
        '413' => 'Request Entity Too Large',
        '414' => 'Request URI Too Large',
        '415' => 'Unsupported Media Type',
        '416' => 'Requested Range Not Satisfiable',
        '417' => 'Expectation Failed',
        '422' => 'Unprocessable Entity',
        '423' => 'Locked',
        '424' => 'Failed Dependency',
        '425' => 'Unordered Collection',
        '426' => 'Upgrade Required',
        '428' => 'Precondition Required',
        '429' => 'Too Many Requests',
        '431' => 'Request Header Fields Too Large',
		'444' => 'No Response',
		'450' => 'Blocked by Windows Parental Controls',
		'451' => 'Unavailable For Legal Reasons',
		'494' => 'Request Header Too Large',
        
        
        '500' => 'Internal Server Error',
        '501' => 'Not Implemented',
        '502' => 'Bad Gateway',
        '503' => 'Service Unavailable',
        '504' => 'Gateway Timeout',
        '505' => 'HTTP Version not supported',
        '507' => 'Insufficient Storage',
    );
    public static function code($code){
    	$code   = $code.'';
    	$result = self::$statusCode[$code];
    	$result = $result ? "HTTP/1.1 $code ".$result : '';
        return $result;
    }
}
I18n.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/I18n.class.php'
View Content
<?php

function LNG($key){
	static $isInit = false;
	if (func_num_args() == 1) {
        return I18n::get($key);
	} else {
		$args = func_get_args();
		array_shift($args);
        return vsprintf(I18n::get($key), $args);
	}
}

class I18n{
	private static $loaded = false;
	private static $lang   = NULL;
	public  static $langType = NULL;
	public static function load(){}
	public static function defaultLang(){
		if(isset($GLOBALS['config']['settings']['language'])){
			return $GLOBALS['config']['settings']['language'];
		}
		$langDefault = 'zh-CN';//zh-CN en;
		$lang  = $langDefault;
		$arr   = $GLOBALS['config']['settingAll']['language'];
		$langs = array();
		foreach ($arr as $key => $value) {
			$langs[$key] = $key;
		}
		$langs['zh'] = 'zh-CN';	//增加大小写对应关系
		$langs['zh-tw'] = 'zh-TW';

		$acceptLanguage = array();
		if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){
			$httpLang = $langDefault;
		}else{
			$httpLang = str_replace("_","-",strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
		}
		preg_match_all('~([-a-z]+)(;q=([0-9.]+))?~',$httpLang,$matches,PREG_SET_ORDER);
		foreach ($matches as $match) {
			$acceptLanguage[$match[1]] = (isset($match[3]) ? $match[3] : 1);
		}
		arsort($acceptLanguage);
		foreach ($acceptLanguage as $key => $q) {
			if (isset($langs[$key])) {
				$lang = $langs[$key];break;
			}
			$key = preg_replace('~-.*~','', $key);
			if (!isset($acceptLanguage[$key]) && isset($langs[$key])) {
				$lang = $langs[$key];break;
			}
		}
		return $lang;
	}

	public static function getAll(){
		self::init();
		return self::$lang;
	}
	public static function getType(){
		self::init();
		return self::$langType;
	}

	public static function init(){
		if(self::$loaded) return;
		if(isset($GLOBALS['in']['language'])){
			return self::setLanguage($GLOBALS['in']['language']);
		}
	    $cookieLang = 'kodUserLanguage';
		if (isset($_COOKIE[$cookieLang])) {
			$lang = $_COOKIE[$cookieLang];
		}else{
			$lang = self::defaultLang();
		}
		//兼容旧版本
		if($lang == 'zh_CN') $lang = 'zh-CN';
		if($lang == 'zh_TW') $lang = 'zh-TW';

		if(isset($GLOBALS['config']['settings']['language'])){
			$lang = $GLOBALS['config']['settings']['language'];
		}
		self::setLanguage($lang);
	}

	private static function setLanguage($lang){
		if(!preg_match('/^[0-9a-zA-z_\-]+$/', $lang)){
			$lang = 'zh-CN';
		}
		$langFile = LANGUAGE_PATH.$lang.'/index.php';
		if(!file_exists($langFile)){//allow remove some I18n folder
			$lang = 'zh-CN';
			$langFile = LANGUAGE_PATH.$lang.'/index.php';
		}

		self::$langType = $lang;
		self::$lang = include($langFile);
		self::$loaded = true;
		$GLOBALS['L'] = &self::$lang;
	}

	public static function get($key){
		self::init();
		if(!isset(self::$lang[$key])) return $key;
		if (func_num_args() == 1) {
	        return self::$lang[$key];
		} else {
			$args = func_get_args();
			array_shift($args);
	        return vsprintf(self::$lang[$key], $args);
		}
	}

	/**
	 * 添加多语言;
	 * @param [type] $args [description]
	 */
	public static function set($array,$value=''){
		self::init();
		if(is_string($array)){
			return self::$lang[$array] = $value;
		}
		if(!is_array($array)) return;
		foreach ($array as $key => $value) {
			self::$lang[$key] = $value;
		}
	}
}
ImageGdBMP.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ImageGdBMP.class.php'
View Content
<?php 
// from elfinder;
class ImageGdBMP{
	public static function load($filename){
		$fp = fopen($filename, "rb");
		if ($fp === false){
			return false;
		}
		$bmp = self::loadFromStream($fp);
		fclose($fp);
		return $bmp;
	}
	public static function loadFromStream($stream){
		$buf = fread($stream, 14); //2+4+2+2+4
		if ($buf === false){
			return false;
		}
		if ($buf[0] != 'B' || $buf[1] != 'M'){
			return false;
		}
		$bitmapHeader = unpack(
			"vtype/".
			"Vsize/".
			"vreserved1/".
			"vreserved2/".
			"Voffbits", $buf
		);
		return self::loadFromStreamAndFileHeader($stream, $bitmapHeader);
	}
	public static function loadFromStreamAndFileHeader($stream, array $bitmapHeader){
		if ($bitmapHeader["type"] != 0x4d42){
			return false;
		}
		$buf = fread($stream, 4);
		if ($buf === false){
			return false;
		}
		list(,$header_size) = unpack("V", $buf);
		if ($header_size == 12){
			$buf = fread($stream, $header_size - 4);
			if ($buf === false){
				return false;
			}
			extract(unpack(
				"vwidth/".
				"vheight/".
				"vplanes/".
				"vbit_count", $buf
			));
			$clr_used = $clr_important = $alpha_mask = $compression = 0;
			$red_mask   = 0x00ff0000;
			$green_mask = 0x0000ff00;
			$blue_mask  = 0x000000ff;
		} else if (124 < $header_size || $header_size < 40) {
			return false;
		} else {
			$buf = fread($stream, 36);
			if ($buf === false){
				return false;
			}
			extract(unpack(
				"Vwidth/".
				"Vheight/".
				"vplanes/".
				"vbit_count/".
				"Vcompression/".
				"Vsize_image/".
				"Vx_pels_per_meter/".
				"Vy_pels_per_meter/".
				"Vclr_used/".
				"Vclr_important", $buf
			));
			if ($width  & 0x80000000){ $width  = -(~$width  & 0xffffffff) - 1; }
			if ($height & 0x80000000){ $height = -(~$height & 0xffffffff) - 1; }
			if ($x_pels_per_meter & 0x80000000){ $x_pels_per_meter = -(~$x_pels_per_meter & 0xffffffff) - 1; }
			if ($y_pels_per_meter & 0x80000000){ $y_pels_per_meter = -(~$y_pels_per_meter & 0xffffffff) - 1; }
			if ($bitmapHeader["size"] != 0){
				$colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count))<<2 : 0;
				$bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height);
				$calcsize = $bitmapHeader["size"] - $bodysize - $colorsize - 14;
				if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124){
					$header_size = $calcsize;
				}
			}
			if ($header_size - 40 > 0){
				$buf = fread($stream, $header_size - 40);
				if ($buf === false){
					return false;
				}
				extract(unpack(
					"Vred_mask/".
					"Vgreen_mask/".
					"Vblue_mask/".
					"Valpha_mask", $buf . str_repeat("\x00", 120)
				));
			} else {
				$alpha_mask = $red_mask = $green_mask = $blue_mask = 0;
			}
			if (
				($bit_count == 16 || $bit_count == 24 || $bit_count == 32)&&
				$compression == 0 &&
				$red_mask == 0 && $green_mask == 0 && $blue_mask == 0
			){
				switch($bit_count){
				case 16:
					$red_mask   = 0x7c00;
					$green_mask = 0x03e0;
					$blue_mask  = 0x001f;
					break;
				case 24:
				case 32:
					$red_mask   = 0x00ff0000;
					$green_mask = 0x0000ff00;
					$blue_mask  = 0x000000ff;
					break;
				}
			}
		}

		if (
			($width  == 0)||
			($height == 0)||
			($planes != 1)||
			(($alpha_mask & $red_mask  ) != 0)||
			(($alpha_mask & $green_mask) != 0)||
			(($alpha_mask & $blue_mask ) != 0)||
			(($red_mask   & $green_mask) != 0)||
			(($red_mask   & $blue_mask ) != 0)||
			(($green_mask & $blue_mask ) != 0)
		){
			return false;
		}
		if ($compression == 4 || $compression == 5){
			$buf = stream_get_contents($stream, $size_image);
			if ($buf === false){
				return false;
			}
			return imagecreatefromstring($buf);
		}
		$line_bytes = (($width * $bit_count + 31) >> 3) & ~3;
		$lines = abs($height);
		$y = $height > 0 ? $lines-1 : 0;
		$line_step = $height > 0 ? -1 : 1;
		if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8){
			$img = imagecreate($width, $lines);
			$palette_size = $header_size == 12 ? 3 : 4;
			$colors = $clr_used ? $clr_used : pow(2, $bit_count);
			$palette = array();
			for($i = 0; $i < $colors; ++$i){
				$buf = fread($stream, $palette_size);
				if ($buf === false){
					imagedestroy($img);
					return false;
				}
				extract(unpack("Cb/Cg/Cr/Cx", $buf . "\x00"));
				$palette[] = imagecolorallocate($img, $r, $g, $b);
			}

			$shift_base = 8 - $bit_count;
			$mask = ((1 << $bit_count) - 1) << $shift_base;
			if ($compression == 1 || $compression == 2){
				$x = 0;
				$qrt_mod2 = $bit_count >> 2 & 1;
				for(;;){
					if ($x < -1 || $x > $width || $y < -1 || $y > $height){
						imagedestroy($img);
						return false;
					}
					$buf = fread($stream, 1);
					if ($buf === false){
						imagedestroy($img);
						return false;
					}
					switch($buf){
					case "\x00":
						$buf = fread($stream, 1);
						if ($buf === false){
							imagedestroy($img);
							return false;
						}
						switch($buf){
						case "\x00": //EOL
							$y += $line_step;
							$x = 0;
							break;
						case "\x01": //EOB
							$y = 0;
							$x = 0;
							break 3;
						case "\x02": //MOV
							$buf = fread($stream, 2);
							if ($buf === false){
								imagedestroy($img);
								return false;
							}
							list(,$xx, $yy) = unpack("C2", $buf);
							$x += $xx;
							$y += $yy * $line_step;
							break;
						default:
							list(,$pixels) = unpack("C", $buf);
							$bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2);
							$buf = fread($stream, ($bytes + 1) & ~1);
							if ($buf === false){
								imagedestroy($img);
								return false;
							}
							for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count){
								list(,$c) = unpack("C", $buf[$pos >> 3]);
								$b = $pos & 0x07;
								imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
							}
							break;
						}
						break;
					default:
						$buf2 = fread($stream, 1);
						if ($buf2 === false){
							imagedestroy($img);
							return false;
						}
						list(,$size, $c) = unpack("C2", $buf . $buf2);
						for($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count){
							$b = $pos & 0x07;
							imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
						}
						break;
					}
				}
			} else {
				for ($line = 0; $line < $lines; ++$line, $y += $line_step){
					$buf = fread($stream, $line_bytes);
					if ($buf === false){
						imagedestroy($img);
						return false;
					}

					$pos = 0;
					for ($x = 0; $x < $width; ++$x, $pos += $bit_count){
						list(,$c) = unpack("C", $buf[$pos >> 3]);
						$b = $pos & 0x7;
						imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]);
					}
				}
			}
		} else {
			$img = imagecreatetruecolor($width, $lines);
			imagealphablending($img, false);
			if ($alpha_mask){
				imagesavealpha($img, true);
			}

			//x軸進行量
			$pixel_step = $bit_count >> 3;
			$alpha_max    = $alpha_mask ? 0x7f : 0x00;
			$alpha_mask_r = $alpha_mask ? 1/$alpha_mask : 1;
			$red_mask_r   = $red_mask   ? 1/$red_mask   : 1;
			$green_mask_r = $green_mask ? 1/$green_mask : 1;
			$blue_mask_r  = $blue_mask  ? 1/$blue_mask  : 1;

			for ($line = 0; $line < $lines; ++$line, $y += $line_step){
				$buf = fread($stream, $line_bytes);
				if ($buf === false){
					imagedestroy($img);
					return false;
				}
				$pos = 0;
				for ($x = 0; $x < $width; ++$x, $pos += $pixel_step){
					list(,$c) = unpack("V", substr($buf, $pos, $pixel_step). "\x00\x00");
					$a_masked = $c & $alpha_mask;
					$r_masked = $c & $red_mask;
					$g_masked = $c & $green_mask;
					$b_masked = $c & $blue_mask;
					$a = $alpha_max - ((($a_masked<<7) - $a_masked) * $alpha_mask_r);
					$r = (($r_masked<<8) - $r_masked) * $red_mask_r;
					$g = (($g_masked<<8) - $g_masked) * $green_mask_r;
					$b = (($b_masked<<8) - $b_masked) * $blue_mask_r;
					imagesetpixel($img, $x, $y, ($a<<24)|($r<<16)|($g<<8)|$b);
				}
			}
			imagealphablending($img, true);
		}
		return $img;
	}
}

ImageThumb.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ImageThumb.class.php'
View Content
<?php 

/**
 * 功能:图片处理
 * 基本参数:$srcFile,$echoType
 * 
 * eg:
 * $cm=new ImageThumb('1.jpg','file');
 * 
 * $cm->Distortion('dis_bei.jpg',150,200);	//生成固定宽高缩略图;
 * $cm->Prorate('pro_bei.jpg',150,200);		//等比缩略图;附带切割
 * $cm->Cut('cut_bei.jpg',150,200);			//等比缩略图;多出部分切割
 * $cm->BackFill('fill_bei.jpg',150,200);	//等比缩略图;多出部分填充
 *
 * $cm->imgRotate('out.jpg',90);			//旋转图片
 */
class ImageThumb {
	var $srcFile = '';	//原图
	var $imgData = '';	//图片信息
	var $echoType;		//输出图片类型,link--不保存为文件;file--保存为文件
	var $im = '';		//临时变量
	var $srcW = '';		//原图宽
	var $srcH = '';		//原图高  

	function __construct($srcFile, $echoType){
		$this->srcFile = $srcFile;
		$this->echoType = $echoType;
		$this->im = self::image($srcFile);
		if(!$this->im){
			return false;
		}

		$info = '';
		$this->imgData = GetImageSize($srcFile, $info);
		$this->srcW = intval(imageSX($this->im));
		$this->srcH = intval(imageSY($this->im));
		return $this;
	}
	
	//生成等比例缩略图; 按宽度处理,兼容长图情况;
	public static function createThumb($from,$to,$width,$height){
		// if(!Cache::get('fileThumb.getConvert')){};// 使用fileThumb插件转换; 
		// 速度对比 2000x2000=>300x300  gd:220ms; imagemagick:360ms; new Imagick: 280ms;
		$cm = new ImageThumb($from,'file');
		if(!$cm) return;
		return $cm->prorate($to,$width,$height);
	}
	
	public static function image($file){
		$info = '';
		$data = GetImageSize($file, $info);
		$img  = false;
		//var_dump($data,$file,memory_get_usage()-$GLOBALS['config']['appMemoryStart']);
		switch ($data[2]) {
			case IMAGETYPE_GIF:
				if (!function_exists('imagecreatefromgif')) {
					break;
				} 
				$img = imagecreatefromgif($file);
				break;
			case IMAGETYPE_JPEG:
				if (!function_exists('imagecreatefromjpeg')) {
					break;
				}
				$img = imagecreatefromjpeg($file);
				break;
			case IMAGETYPE_PNG:
				if (!function_exists('imagecreatefrompng')) {
					break;
				}
				$img = @imagecreatefrompng($file);
				if ($img) imagesavealpha($img,true);
				break;
			case IMAGETYPE_XBM:
				$img = imagecreatefromxbm($file);
				break;
			case IMAGETYPE_WBMP:
				$img = imagecreatefromwbmp($file);
				break;
			case IMAGETYPE_BMP:
				$img = imagecreatefrombmp($file);
				break;
			default:break;
		}
		if (!$img && function_exists('imagecreatefromstring')) {
			$img = @imagecreatefromstring(file_get_contents($file));
		}
		return $img;
	}

	public static function imageSize($file){
		$size = GetImageSize($file);
		if(!$size){
			return false;
		}
		return array('width'=>$size[0],"height"=>$size[1]);
	}

	// 生成扭曲型缩图
	function distortion($toFile, $toW, $toH){
		$toW = intval($toW);$toH = intval($toH);
		$cImg = $this->creatImage($this->im, $toW, $toH, 0, 0, 0, 0, $this->srcW, $this->srcH);
		return $this->echoImage($cImg, $toFile);
	} 
	// 生成按比例缩放的缩图
	function prorate($toFile, $toW, $toH){
		if(!$this->im){return false;}
		$toW = intval($toW);$toH = intval($toH);
		$this->srcH = intval($this->srcH);
		$this->srcW = intval($this->srcW);
		if(!$toW || !$toH || !$this->srcW || !$this->srcH) return false;
		
		$toWH = $toW / $toH;
		$srcWH = $this->srcW / $this->srcH;
		if ($toWH<=$srcWH) {
			$ftoW = $toW;
			$ftoH = $ftoW * ($this->srcH / $this->srcW);
		} else {
			$ftoH = $toH;
			$ftoW = $ftoH * ($this->srcW / $this->srcH);
		} 
		if ($this->srcW > $toW || $this->srcH > $toH) {
			$cImg = $this->creatImage($this->im, $ftoW, $ftoH, 0, 0, 0, 0, $this->srcW, $this->srcH);
		} else {
			$cImg = $this->creatImage($this->im, $this->srcW, $this->srcH, 0, 0, 0, 0, $this->srcW, $this->srcH);
		}
		$cImg = $this->autoRotate($cImg);
		return $this->echoImage($cImg, $toFile);
	}

	// 图片方向自动旋转;https://www.cnblogs.com/whlives/p/4554424.html
	private function autoRotate($im){
		if(!$im || !function_exists('exif_read_data')) return $im;
		$exif = exif_read_data($this->srcFile);
		if(!$exif || !isset($exif['Orientation']) || $exif['Orientation'] == 1) return $im;
		
		$degree = 0;
		if($exif['Orientation'] == 6){$degree = 90;}
		if($exif['Orientation'] == 8){$degree = 270;}
		if($exif['Orientation'] == 3){$degree = 180;}
		return $this->rotateDegree($im,$degree);
	}
	
	// 顺时针旋转图片
	private function rotateDegree($im,$degree){
		if (!$im || $degree % 360 === 0 || !function_exists('imageRotate')) return $im;
		$imResult = imageRotate($im,360-$degree,0);
		imageDestroy($im);
		return $imResult ? $imResult:$im;
	}
		
	// 生成最小裁剪后的缩图
	function cut($toFile, $toW, $toH){
		if(!$this->im){return false;}
		$toW = intval($toW);$toH = intval($toH);
		$toWH = $toW / $toH;
		$srcWH = $this->srcW / $this->srcH;
		if ($toWH<=$srcWH) {
			$ctoH = $toH;
			$ctoW = $ctoH * ($this->srcW / $this->srcH);
		} else {
			$ctoW = $toW;
			$ctoH = $ctoW * ($this->srcH / $this->srcW);
		} 
		$allImg = $this->creatImage($this->im, $ctoW, $ctoH, 0, 0, 0, 0, $this->srcW, $this->srcH);
		$cImg = $this->creatImage($allImg, $toW, $toH, 0, 0, ($ctoW - $toW) / 2, ($ctoH - $toH) / 2, $toW, $toH);
		imageDestroy($allImg);
		return $this->echoImage($cImg, $toFile);		
	} 
	// 生成背景填充的缩图,默认用白色填充剩余空间,传入$isAlpha为真时用透明色填充
	function backFill($toFile, $toW, $toH,$isAlpha=false,$red=255, $green=255, $blue=255){
		$toW = intval($toW);$toH = intval($toH);
		$toWH = $toW / $toH;
		$srcWH = $this->srcW / $this->srcH;
		if ($toWH<=$srcWH) {
			$ftoW = $toW;
			$ftoH = $ftoW * ($this->srcH / $this->srcW);
		} else {
			$ftoH = $toH;
			$ftoW = $ftoH * ($this->srcW / $this->srcH);
		} 
		if (function_exists('imagecreatetruecolor')) {
			@$cImg = imageCreateTrueColor($toW, $toH);
			if (!$cImg) {
				$cImg = imageCreate($toW, $toH);
			} 
		} else {
			$cImg = imageCreate($toW, $toH);
		}

		$fromTop = ($toH - $ftoH)/2;//从正中间填充
		$backcolor = imagecolorallocate($cImg,$red,$green, $blue); //填充的背景颜色
		if ($isAlpha){//填充透明色
			$backcolor=imageColorTransparent($cImg,$backcolor);
			$fromTop = $toH - $ftoH;//从底部填充
		}		

		imageFilledRectangle($cImg, 0, 0, $toW, $toH, $backcolor);
		if ($this->srcW > $toW || $this->srcH > $toH) {
			$proImg = $this->creatImage($this->im, $ftoW, $ftoH, 0, 0, 0, 0, $this->srcW, $this->srcH);
			if ($ftoW < $toW) {
				imageCopy($cImg, $proImg, ($toW - $ftoW) / 2, 0, 0, 0, $ftoW, $ftoH);
			} else if ($ftoH < $toH) {
				imageCopy($cImg, $proImg, 0, $fromTop, 0, 0, $ftoW, $ftoH);
			} else {
				imageCopy($cImg, $proImg, 0, 0, 0, 0, $ftoW, $ftoH);
			} 
		} else {
			imageCopyMerge($cImg, $this->im, ($toW - $ftoW) / 2,$fromTop, 0, 0, $ftoW, $ftoH, 100);
		} 
		return $this->echoImage($cImg, $toFile);
	} 

	function creatImage($img, $creatW, $creatH, $dstX, $dstY, $srcX, $srcY, $srcImgW, $srcImgH){
		if (function_exists('imagecreatetruecolor')) {
			@$creatImg = ImageCreateTrueColor($creatW, $creatH);
			@imagealphablending($creatImg,false);//是不合并颜色,直接用$img图像颜色替换,包括透明色;
			@imagesavealpha($creatImg,true);//不要丢了$thumb图像的透明色;
			if ($creatImg){
				imageCopyResampled($creatImg, $img, $dstX, $dstY, $srcX, $srcY, $creatW, $creatH, $srcImgW, $srcImgH);
			}else {
				$creatImg = ImageCreate($creatW, $creatH);
				imageCopyResized($creatImg, $img, $dstX, $dstY, $srcX, $srcY, $creatW, $creatH, $srcImgW, $srcImgH);
			} 
		} else {
			$creatImg = ImageCreate($creatW, $creatH);
			imageCopyResized($creatImg, $img, $dstX, $dstY, $srcX, $srcY, $creatW, $creatH, $srcImgW, $srcImgH);
		} 
		return $creatImg;
	}


	// Rotate($toFile, 90);
	public function imgRotate($toFile,$degree) {
		if (!$this->im ||
			$degree % 360 === 0 || 
			!function_exists('imageRotate')) {
			return false;
		}
		$rotate  = imageRotate($this->im,360-$degree,0);
		$result  = false;
		switch ($this->imgData[2]) {
			case IMAGETYPE_GIF:
				$result = imagegif($rotate, $toFile);
				break;
			case IMAGETYPE_JPEG:
				$result = imagejpeg($rotate, $toFile,100);//压缩质量
				break;
			case IMAGETYPE_PNG:
				$result = imagePNG($rotate, $toFile);
				break;
			default:break;
		}
		imageDestroy($rotate);
		imageDestroy($this->im);
		return $result;
	}

	// 输出图片,link---只输出,不保存文件。file--保存为文件
	function echoImage($img, $toFile){
		if(!$img) return false;
		ob_get_clean();
		$result = false;
		
		// $quality = 1;//imagePNG; 默认-1; 1~9; 压缩级别;
		$quality = 90;//imagejpeg; 默认100; 0-100; 压缩级别;
		switch ($this->echoType) {
			case 'link':$result = imagejpeg($img,null,$quality);break;
			case 'file':$result = imagejpeg($img,$toFile,$quality);break;
			//return ImageJpeg($img, $to_File);				
		}
		imageDestroy($img);
		imageDestroy($this->im);
		return $result;
	} 
}

if(!function_exists('imageflip')){
	/**
	 * Flip (mirror) an image left to right.
	 *
	 * @param image  resource
	 * @param x      int
	 * @param y      int
	 * @param width  int
	 * @param height int
	 * @return bool
	 * @require PHP 3.0.7 (function_exists), GD1
	 */
	define('IMG_FLIP_HORIZONTAL', 0);
	define('IMG_FLIP_VERTICAL', 1);
	define('IMG_FLIP_BOTH', 2);
	function imageflip($image, $mode) {
		switch ($mode) {
			case IMG_FLIP_HORIZONTAL: {
				$max_x = imagesx($image) - 1;
				$half_x = $max_x / 2;
				$sy = imagesy($image);
				$temp_image = imageistruecolor($image)? imagecreatetruecolor(1, $sy): imagecreate(1, $sy);
				for ($x = 0; $x < $half_x; ++$x) {
					imagecopy($temp_image, $image, 0, 0, $x, 0, 1, $sy);
					imagecopy($image, $image, $x, 0, $max_x - $x, 0, 1, $sy);
					imagecopy($image, $temp_image, $max_x - $x, 0, 0, 0, 1, $sy);
				}
				break;
			}
			case IMG_FLIP_VERTICAL: {
				$sx = imagesx($image);
				$max_y = imagesy($image) - 1;
				$half_y = $max_y / 2;
				$temp_image = imageistruecolor($image)? imagecreatetruecolor($sx, 1): imagecreate($sx, 1);
				for ($y = 0; $y < $half_y; ++$y) {
					imagecopy($temp_image, $image, 0, 0, 0, $y, $sx, 1);
					imagecopy($image, $image, 0, $y, 0, $max_y - $y, $sx, 1);
					imagecopy($image, $temp_image, 0, $max_y - $y, 0, 0, $sx, 1);
				}
				break;
			}
			case IMG_FLIP_BOTH: {
				$sx = imagesx($image);
				$sy = imagesy($image);
				$temp_image = imagerotate($image, 180, 0);
				imagecopy($image, $temp_image, 0, 0, 0, 0, $sx, $sy);
				break;
			}
			default: {
				return;
			}
		}
		imagedestroy($temp_image);
	}
}
if(!function_exists('imagecreatefrombmp')){
	function imagecreatefrombmp( $filename ){
		return ImageGdBMP::load($filename);
	}
}

Input.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Input.class.php'
View Content
<?php 

class Input{
	/**
	 * 检测并获取传入参数;
	 * 
	 * key为字段名; value: 
	 * 		default: 为空时默认值;default 为null时 不返回该key
	 * 		aliasKey:将传入的字段映射为对应别名;
	 * 		msg:   错误时提示内容
	 * 		check: 检测类型; 含有该项并不为空时依照条件进行检测  in/json  param
	 */
	public static function getArray($array){
		global $in;
		$result = array();
		$error  = array();
		$msgCommon	= LNG("common.invalidParam");
		foreach ($array as $key => $value) {
			$msg 	 = _get($value,'msg', $msgCommon.': '.$key ); // 错误提示
			$itemKey = $key;
			if(isset($value['aliasKey']) && $value['aliasKey']){
				$itemKey = $value['aliasKey'];
			}
			//!isset($in[$key])  替换
			if( !array_key_exists($key,$in) ){				
				//设置了默认值则使用默认值;为null时 不返回该key
				if( array_key_exists("default",$value) ){
					if( !is_null($value["default"]) ){
						$result[$itemKey] = $value["default"];
					}
				}else if(isset($value['check'])){//只要设置了check,则该值不为空
					$error[] = $msg;
				}
				continue;
			}
			
			// 长度校验,过长或过短内容报错;
			if(isset($value['lengthMax']) && strlen($in[$key]) > $value['lengthMax']){
				$error[] = $msg;continue;
			}
			if(isset($value['lengthMin']) && strlen($in[$key]) < $value['lengthMin']){
				$error[] = $msg;continue;
			}
			
			// json 单独处理
			if( isset($value['check']) && $value['check'] == 'json' ){
				$decode = json_decode($in[$key],true);
				if( is_array($decode) ){
					$result[$itemKey] = $decode;
				}else{
					if( array_key_exists("default",$value) ){
						if( !is_null($value["default"]) ){
							$result[$itemKey] = $value["default"];
						}				
					}else{
						$error[] = $msg;
					}
				}
				continue;
			}

			$param = _get($value,'param');
			if( isset($value['check']) && !self::check($in[$key],$value['check'],$param) ){
				if( array_key_exists("default",$value) ){
					if( !is_null($value["default"]) ){
						$result[$itemKey] = $value["default"];
					}				
				}else{
					$error[] = $msg;
				}
				continue;
			}
			$result[$itemKey] = $in[$key];
		}
		if(count($error) > 0 ){
			show_json(implode(";\n",$error),false);
		}
		return $result;
	}
	
	public static function reg($type='require',$setReg = false){
		static $checkReg = false;
		if(!$checkReg){
				$checkReg = array(
				'require'	=> ".+",
				'number' 	=> "\d+",
				'hex'		=> "[0-9A-Fa-f]+",
				'integer'   => "\d+",
				'int'	 	=> "[-\+]?\d+",
				'bool'	 	=> "0|1",
				'float' 	=> "[-\+]?\d+(\.\d+)?",
				'english' 	=> "[A-Za-z ]+",
				'chinese'	=> "[\x{4e00}-\x{9fa5}]+",					//全中文
				'hasChinese'=> "/([\x{4e00}-\x{9fa5}]+)/u",				//含有中文
				
				'email' 	=> "\w+([\.\-]\w+)*\@\w+([\.\-]\w+)*\.\w+",	//邮箱
				'phone' 	=> "1[3-9]\d{9}",							//手机号 11位国内手机;
				'telphone'	=> "(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}",	//固定电话,
				'url' 		=> "(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?",	//互联网网址
				'urlFull' 	=> "[a-zA-z]+:\/\/[^\s]*",					//广义网址
				'ip'		=> "(\d{1,3}\.){3}(\d{1,3})",				//ip地址
				'zip' 		=> "[1-9]\d{5}(?!\d)",						//邮编
				'idCard'	=> "(\d{15})|(\d{17}(\d|X|x))",				//身份证 15位或18位身份证,18位末尾可能为x或X
				'color'		=> "#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})",		//16进制颜色 #fff,#fafefe,#fff;
				
				'time'		=> "([0-1]\d|2[0-4]):[0-5]\d",				//时间,	02:23,15:59,24
				'date'		=> "\d{4}[-\/]?(0[1-9]|1[0-2])[-\/]?([0-2]\d|3[0-1])",//年月日,  20150102 2015/01/02 2015-01-02;
				'dateTime'	=> "\d{4}[-\/]?(0[1-9]|1[0-2])[-\/]?([0-2]\d|3[0-1])\s+([0-1]\d|2[0-4]):[0-5]\d",
								//年-月-日时分秒,	2015-01-02 
				
				'password'	=> "(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}",	//强密码,必须包含数字、大小写字符;不能包含特殊字符;8-20位
				'key'		=> "[A-Za-z0-9_\-\.]+",		//数据库字段或表 [A-Za-z0-9_\.]+
				'keyFull'	=> "[A-Za-z0-9_\-\.\s,]+",	//数据库字段或表,支持多key;加入空格逗号
			);
		}
		
		// 覆盖更新规则;
		if($setReg && is_array($setReg)){
			$checkReg = array_merge($checkReg, $setReg);
			return;
		}
		
		
		//多国手机匹配,https://github.com/chriso/validator.js/blob/master/src/lib/isMobilePhone.js
		if(!$type){
			return $checkReg;
		}
		return $checkReg[$type];
	}

	/**
	 * 检查某个数据是否符合某个规则
	 * $check : 正则名称或者正则表达式;
	 */
	public static function check($value,$check,$param = null){
		//其他规则
		switch($check){
			case 'in':		return in_array($value,$param);break;
			case 'bigger':	return floatval($value)>$param;break;
			case 'smaller':	return floatval($value)<$param;break;
			case 'length':	return strlen($value)>=$param[0] && strlen($value)<=$param[1];break;
			case 'length':	
				if(is_array($param)){
					return strlen($value)>=$param[0] && strlen($value)<=$param[1];break;
				}else{
					return strlen($value)==$param;break;
				}
			case 'between':	return floatval($value)>=$param[0] && floatval($value)<=$param[1];break;
		}
		
		// 检查是否有内置的正则表达式
		$reg   = self::reg(false);
		$check = isset($reg[$check]) ? $reg[$check] : $check;
		
		//含有/ 则认为是完整的正则表达式,不自动追加
		if(substr($check,0,1) != '/'){
			$check = '/^'.$check.'$/su';//匹配多行
		}
        return preg_match($check,$value) === 1;
	}

	/**
	 * 获取某个参数值
	 */
	public static function get($key,$check=null,$default=null,$param=null){
		$item = array();
		if(!is_null($default)) $item['default'] = $default;
		if(!is_null($param))   $item['param'] = $param;
		if(!is_null($check))   $item['check'] = $check;
		$data = Input::getArray(array($key => $item));
		return $data[$key];
	}
}

KodArchive.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodArchive.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class KodArchive {
	static function init(){
		if(defined('ARCHIVE_LIB')) return;
		define('ARCHIVE_LIB',SDK_DIR.'/archiveLib/');
		define('PCLZIP_TEMPORARY_DIR',TEMP_FILES);
		define('PCLTAR_TEMPORARY_DIR',TEMP_FILES);
		define('PCLZIP_SEPARATOR',';@@@,');//压缩多个文件,组成字符串分割
		mk_dir(TEMP_FILES);

		require_once ARCHIVE_LIB.'pclerror.lib.php';
		require_once ARCHIVE_LIB.'pcltrace.lib.php';
		require_once ARCHIVE_LIB.'pcltar.lib.php';
		require_once ARCHIVE_LIB.'pclzip.class.php';
		require_once ARCHIVE_LIB.'kodRarArchive.class.php';
		require_once ARCHIVE_LIB.'kodZipArchive.class.php';
	}
	/**
	 * [checkIfType get the app by ext]
	 * @param  [type] $guess [check]
	 * @param  [type] $ext   [file ext]
	 * @return [type]        [description]
	 */
	static function checkIfType($ext,$appType){
		self::init();
		$extArray = array(
			'zip' => array('zip','ipa','apk','epub'),
			'tar' => array('tar','tar.gz','tgz','gz'),
			'rar' => array('rar','7z','xz','bz2','arj','cab','iso')
		);
		$result = in_array($ext,$extArray[$appType]);
		if( $result  &&
			($appType == 'zip' || $appType == 'tar') &&
			(!function_exists('gzopen') || !function_exists('gzinflate'))
		){
			show_tips("[Error] Can't Open; Missing zlib extensions");
		}
		
		if( $result  && $appType == 'rar' &&
			(!function_exists('shell_exec') || !strstr(shell_exec('echo "kodcloud"'),'kodcloud'))
		){
			show_tips("[Error] Can't Open; shell_exec Can't use");
		}
		return $result;
	}

	/**
	 * [listContent description]
	 * @param  [type] $file [archive file]
	 * @return [type]       [array or false]
	 */
	static function listContent($file,$output=true,$ext='') {
		self::init();
		$ext = $ext ? $ext : get_path_ext($file);
		if($ext == 'gz'){$ext = 'tgz';}
		$result = false;
		if( self::checkIfType($ext,'tar') ){
			//TrOn(10);
			$resultOld = PclTarList($file,$ext);
			//TrDisplay();exit;
			$result = array();
			if(!is_array($resultOld)){return array('code'=>false,'data'=>$result);}
			for($i=0; $i < count($resultOld); $i++) {
				$item = $resultOld[$i];
				//http://rpm5.org/docs/api/tar_8c-source.html
				if($item['typeflag'] == 'x' || $item['typeflag'] == 'g'){continue;}
				if($output){$item['filename'] = ltrim($item['filename'],'./');}
				$item['folder'] = $item['typeflag'] == '5' ? true : false;
				$item['index'] = $i;
				$result[] = $item;
			}
		}else if( self::checkIfType($ext,'rar') ){
			$appResult = kodRarArchive::listContent($file,$ext);
			if(!$appResult['code']){return $appResult;}
			$result = $appResult['data'];
		}else{//默认zip
            if(kodZipArchive::support('list')){
                $result = kodZipArchive::listContent($file);
            }else{
                $zip = new PclZip($file);
                $result = $zip->listContent();
            }
		}
		if(!$result){return array('code'=>false,'data'=>$result);}
		
		$charsetAll = unzip_charset_get($result);	// 多个文件可能有多种编码,统一按gbk转码会导致乱码
		$output  = $output && function_exists('iconv');
		for ($i=0; $i < count($result); $i++) {
			//不允许相对路径
			$result[$i]['filename'] = str_replace(array('../','..\\'),"_",$result[$i]['filename']);
			if($output){
				$charset = get_charset($result[$i]['filename']);
				// if($charsetAll != $charset && $charset == 'utf-8'){$charset = $charsetAll;}
				if ($charset == 'big5') {$charset = 'gbk';}	// big5转换乱码,可能编码混杂
				// $encoding = array('GB2312', 'GBK', 'GB18030', 'UTF-8', 'ASCII', 'BIG5', 'EUC-CN');
				// $charset = mb_detect_encoding($result[$i]['filename'],$encoding,true);
				// $charset = mb_detect_encoding($result[$i]['filename'], mb_list_encodings(), false);
				// $result[$i]['filename'] = iconv_to($result[$i]['filename'],$charset,'utf-8');
				if($GLOBALS['config']['systemCharset'] != $charset){
					$result[$i]['filename'] = unzip_pre_name($result[$i]['filename']);//系统编码——需提前调用unzip_charset_get
				}
				unset($result[$i]['stored_filename']);
			}
			if($result[$i]['folder']){
				$result[$i]['filename'] = rtrim($result[$i]['filename'],'/').'/';
			}
		}
		return array('code'=>true,'data'=>$result);
	}

	/**
	 * [extract description]
	 * @param  [type] $file [archive file]
	 * @param  [type] $dest [extract to folder]
	 * @param  string $part [archive file content]
	 * @return [type]       [array]
	 */
	static function extract($file, $dest, $part = '-1',&$partName=false,$ext=''){
		self::init();
		$ext = $ext ? $ext: get_path_ext($file);
		if($ext == 'gz'){$ext = 'tgz';}
		
		$listUtf8 = false;//默认不转码; rar转码;
		if(self::checkIfType($ext,'rar')){$listUtf8 = true;}
		$listContent = self::listContent($file,$listUtf8,$ext);
		if(!$listContent['code']){
			return $listContent;
		}
		if($part != '-1'){//解压部分.则构造 $pathRemove $indexPath
			$indexInfo = self::fileIndex($listContent['data'],$part);
			$partName  = str_replace(array('../','..\\'),'_',$indexInfo['filename']);
			$indexPath = $partName;
			if($GLOBALS['config']['systemCharset'] != 'utf-8'){
				$indexPath = unzip_pre_name($partName);//系统编码
			}

			//$pathRemove = get_path_father($indexPath);
			$pathRemove = get_path_father($partName);//中文情况文件情况兼容
			if($pathRemove == $partName){
			    $pathRemove = '';
			}
			if($indexInfo['folder']){
				$indexPath = rtrim($indexPath,'/').'/';//tar 解压文件夹需要/结尾
				$partName = array($partName);
			}
			
			$tempCheck = str_replace('\\','/',$indexPath);
			if(substr($tempCheck,-1) == '/'){
				//跟目录;需要追加一层文件夹;window a\b\c\  linux a/b/c/
				if( !strstr(trim($tempCheck,'/'),'/') ){
					$dest = $dest.get_path_this($tempCheck).'/';
				}
			}else{
				if($pathRemove == $indexPath){//根目录文件;
					$pathRemove = '';
				}
			}
			//pr($indexInfo,$indexPath,$partName,$pathRemove);exit;
		}
		
		if( self::checkIfType($ext,'tar') ){
			//TrOn(10);
			if($part != '-1'){
				//tar 默认都进行转码;
				$indexPath  = unzip_pre_name($indexPath);
				$pathRemove = unzip_pre_name($pathRemove);
				$result = PclTarExtractList($file,array($indexPath),$dest,$pathRemove,$ext);
			}else{
				$result = PclTarExtract($file,$dest,"",$ext);
			}
			//TrDisplay();exit;
			return array('code'=>$result,'data'=>PclErrorString(true));
		}else if( self::checkIfType($ext,'rar')){ // || $ext == 'zip' 
			return kodRarArchive::extract($file,$dest,$ext,$partName);
		}else if(kodZipArchive::support('extract')){
            return kodZipArchive::extract($file,$dest,$partName);
		}else{
			$zip = new PclZip($file);
			//解压内部的一部分,按文件名或文件夹来
			if($part != '-1'){
				$result = $zip->extract(PCLZIP_OPT_PATH,$dest,
										PCLZIP_OPT_SET_CHMOD,DEFAULT_PERRMISSIONS,
										PCLZIP_CB_PRE_FILE_NAME,'unzip_pre_name',

										PCLZIP_OPT_BY_NAME,$indexInfo['filename'],
										PCLZIP_OPT_REMOVE_PATH,$pathRemove,
										PCLZIP_OPT_REPLACE_NEWER);
			}else{
				$result = $zip->extract(PCLZIP_OPT_PATH,$dest,
										PCLZIP_OPT_SET_CHMOD,DEFAULT_PERRMISSIONS,
										PCLZIP_CB_PRE_FILE_NAME,'unzip_pre_name',
										PCLZIP_OPT_REPLACE_NEWER);//解压到某个地方,覆盖方式
			}
			return array('code'=>$result,'data'=>$zip->errorName(true));
		}
		return array('code'=>false,'data'=>'File Type Not Support');
	}

	static function fileIndex($list,$index,$key=false){
		self::init();
		if(!is_array($list)) return false;
		$len = count($list);
		for ($i=0; $i < $len; $i++) {
			if($index == $list[$i]['index']){
				$item = $list[$i];
				break;
			}
		}
		if(!$item){
			show_tips('KodArchive:fileIndex; index error;file not exists!');
		}
		$result = $item;
		if($key){
			$result = $item[$key];
			if($item['folder']){
				$result = rtrim($result,'/').'/';//tar 解压文件夹需要结尾/
			}
		}
		return $result;
	}

	static function extractZipFile($file,$byName,$cacheName = false){
		self::init();
		$temp = TEMP_FILES.hash_path($file).'/';
		mk_dir($temp);
		$newFile = $temp.md5($file.$byName);
		if($cacheName){
			$newFile = $temp.$cacheName;
		}

		if(file_exists($newFile)){
			return $newFile;
		}
		$zip  = new PclZip($file);
		$outFile = unzip_filter_ext($temp.get_path_this($byName));
		$parent = get_path_father($byName);
		if($parent == $byName){
			$parent = '';
		}
		$result = $zip->extract(
			PCLZIP_OPT_PATH,$temp,
			PCLZIP_CB_PRE_FILE_NAME,'unzip_pre_name',
			PCLZIP_OPT_REMOVE_PATH,$parent,
			PCLZIP_OPT_BY_NAME,$byName);
		if(!file_exists($outFile)){
			return false;
		}
		@rename($outFile,$newFile);
		return $newFile;
	}

	static function createFromFolder($file,$folder){
		$listPath = IO::listPath($folder);
		$listPath = array_merge($listPath['folderList'],$listPath['fileList']);
		$lists = array_to_keyvalue($listPath,'path','path');
		return self::create($file,$lists);
	}
		
	/**
	 * [create description]
	 * @param  [type] $file  [archive file name]
	 * @param  [type] $files [files add;file or folders]
	 * @return [type]        [bool]
	 */
	static function create($file,$files) {
		self::init();
		$ext = get_path_ext($file);
		$result = false;
		if( self::checkIfType($ext,'zip') ){
		    if(kodZipArchive::support('add')){
                return kodZipArchive::create($file,$files);
            }
			$archive = new PclZip($file);
			foreach ($files as $key =>$val) {
				$val = str_replace('//','/',$val);
				$removePathPre = Kodio::clear(get_path_father($val));
				if($key == 0){
					$result = $archive->create($val,
						PCLZIP_OPT_REMOVE_PATH,$removePathPre,
						PCLZIP_CB_PRE_FILE_NAME,'zip_pre_name'
					);
					continue;
				}
				$result = $archive->add($val,
					PCLZIP_OPT_REMOVE_PATH,$removePathPre,
					PCLZIP_CB_PRE_FILE_NAME,'zip_pre_name'
				);
			}
		}else if( self::checkIfType($ext,'tar') ){
			//TrOn(10);
			foreach ($files as $key =>$val) {
				$val = str_replace('//','/',$val);
				$removePathPre = Kodio::clear(get_path_father($val));
				if($key == 0){
					$result = PclTarCreate($file,array($val), $ext,null, $removePathPre);
					continue;
				}
				$result = PclTarAddList($file,array($val),'',$removePathPre,$ext);
			}
			//TrDisplay();exit;
		}
		return $result;
	}
}
KodLog.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodLog.class.php'
View Content
<?php 

/**
 * 用户相关信息处理
 */
class KodLog{
	public static $logLast = array();
	public static function replace($staus=true){self::log('',$staus ? 'replace':'reset');}
	public static function enable ($staus=true){self::log('',$staus ? 'enable':'disable');}
	public static function log($log,$replaceType=''){
		static $replace = false;
		static $disableOut = false;
		if($replaceType){
			if($replaceType == 'disable'){$disableOut = true;return;}
			if($replaceType == 'enable' ){$disableOut = false;return;}
			if($disableOut){return;}
			$replaceNow = $replaceType == 'replace';
			$hasChange  = (!$replace && $replaceNow) || ($replace && !$replaceNow);
			$replace 	= $replaceNow;
			if($hasChange){echoLog('',$replace);}
			return;
		}
		write_log(str_replace('&nbsp;',' ',$log),'log');
		if($disableOut){return;}
		
		self::$logLast = array('log'=>$log,'replace'=>$replace,'time'=>time());
		check_abort_now();
		echoLog($log,$replace);
	}
	
	public static function logTimeShow($timeStart,$index,$total,$logPre='',$logAfter=''){
		$logPre   = $logPre   ? $logPre.' ':'';
		$logAfter = $logAfter ? ';'.$logAfter:'';
		self::log($logPre.self::timeShow($timeStart,$index,$total).$logAfter);
	}	
	// 进度及时间显示;  
	public static function timeShow($timeStart,$index,$total){
		$timeNeed  		= self::timeNeed($timeStart,$index,$total);
		$timeUse 		= timeFloat() - $timeStart;
		
		$charCount 		= 20;$char = '=';
		$pencent 		= $index / $total;
		$pencent 		= $pencent >= 1 ? 1:$pencent;
		$charFinished 	= intval($pencent * $charCount);
		$pencentView 	= '['.str_repeat($char,$charFinished).'>'.str_repeat('&nbsp;',$charCount - $charFinished).']';
		$logIndex 		= str_repeat('&nbsp;',strlen($total.'') - strlen($index.'')).$index;
		$logOut 		= $logIndex.'/'.$total.' '.$pencentView.sprintf(" %.2f",$pencent*100).'%';
		
		$speed   = $index / $timeUse;
		$speed   = $speed < 10 ? round($speed,2) : intval($speed);
		if($speed > 0){$logOut .= '('.$speed.' '.LNG('common.item').'/'.LNG('common.second').')';}
		if($pencent >= 1){
			$timeUsePre  = LNG('admin.backup.timeTaken');
			$timeUseShow = $timeUsePre.self::timeFormat($timeUse);
			if($timeUse < 10){$timeUseShow = $timeUsePre.round($timeUse,2).LNG('common.second');}
			if($timeUse <= 0.2){return $logIndex.'/'.$total.'; '.$timeUseShow;} // 完成时间过短,不显示进度及速度;

			$logOut .= ';'.$timeUseShow;$timeNeed = '';
		}
		if($timeNeed){$logOut .= '; '.LNG('common.task.timeNeed').$timeNeed;}
		return $logOut;
	}
	
	// 计算所需时间;
	public static function timeNeed($timeStart,&$index,$total){
		// $index = $index + 1;
		if($index >= $total){$index = $total;}
		if($index <= 0){return '';}
		$timeRow  = (timeFloat() - $timeStart) / $index;
		return self::timeFormat($timeRow * ($total - $index));
	}
	
	// 时间格式化显示;
	public static function timeFormat($seconds){
		if(!$seconds){return '';}		
		$sep = ' ';$sep1 = ',';
		if(substr(I18n::getType(),0,2) == 'zh'){$sep = '';$sep1 = '';}
		$d = floor($seconds / 86400);$dt = $d.$sep.LNG('common.day');
		$h = floor(($seconds % 86400) / 3600);$ht = $h.$sep.LNG('common.hour');
		$m = floor(($seconds % 3600) / 60);$mt = $m.$sep.LNG('common.minute');
		$s = ceil(fmod($seconds,60));$st = $s.$sep.LNG('common.second');

		if($d){$result = $dt.$sep1.$ht.$sep1.$mt;}
		if(!$d && $h){$result = $ht.$sep1.$mt;}
		if(!$d && !$h && $m){$result = $mt.$sep1.$st;}
		if(!$d && !$h && !$m){$result = $st;}
		return $sep.$result;
	}
}
KodSort.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodSort.class.php'
View Content
<?php 

/**
 * 自然排序相关处理;
 * 
 * 1.数字从小到大(小数位处理,版本号或ip则填充)
 * 2.字母大小写无关
 * 3.中文拼音排序(多音字根据上下文词语自动选择)
 * 4.中文数字排序;(数字>字母>中文数字>中文)
 */
class KodSort{
	/**
	 * 数组自然排序
	 * 按$field进行排序(若$field为空则字符串数组排序),$fieldDefault作为field相等时额外判断字段
	 * 
	 * arraySort([["name":"f2","status":1],...],'status',false,'name'); // 按数组中字段排序;
	 * arraySort(["f2","f10",'f13'],false,true); // 直接数组排序;
	 */
	public static function arraySort($records,$field='',$orderDesc=false,$fieldMore=false,$useLocal=false){
		if(!is_array($records) || !$records) return array();
		if($fieldMore && $fieldMore == $field){$fieldMore = false;}
		if(count($records) == 1) return $records;
		$order   = $orderDesc ? SORT_DESC : SORT_ASC;
		$flag 	 = SORT_STRING;
		$sortBy1 = array();$sortBy2 = array();
		
		// $useLocal = true;
		// 超过一定数量采用自带自然排序;
		if(count($records) >= 100000 || $useLocal){
			return array_sort_by($records,$field,$orderDesc,$fieldMore);
		}
				
		foreach ($records as $item){
			if(!$field){$sortBy1[] = self::makeStr($item); continue;}
			if($field){
				$val = isset($item[$field]) ? $item[$field] :_get($item,$field,'');
				$sortBy1[]  = self::makeStr($val);
			}
			if($fieldMore){
				$val = isset($item[$fieldMore]) ? $item[$fieldMore] :_get($item,$fieldMore,'');
				$sortBy2[] = self::makeStr($val);
			}
		}
		// trace_log(['222',$field,$order,$fieldMore,$sortBy1,$sortBy2]);
		if($fieldMore){ array_multisort($sortBy1,$order,$flag,$sortBy2,$order,$flag,$records);}
		if(!$fieldMore){array_multisort($sortBy1,$order,$flag,$records);}
		return $records;
	}
	public static function strSort($a,$b){
		$strA = self::makeStr($a);
		$strB = self::makeStr($b);
		return $strA > $strB ? 1 : ($strA == $strB ? 0 :-1);
	}
	
	// 转为可自然排序的字符串
	public static function makeStr($str){
		if($str === '' || $str === null ) return '';
		if(strlen($str) > 128) return $str;
		$strResult = '';$padLength = 15;$start = 0;

		if(preg_match_all("/\d+/",$str,$matchRes)){
			$numberPadArr = self::numberPadArr($str);
			$padPoseMin   = $numberPadArr ? $numberPadArr[0][0]:false;
			$padPoseMax   = $numberPadArr ? $numberPadArr[count($numberPadArr) - 1][1]:false;
		}else{
			$matchRes = array(array());$strResult = $str;
		}
		foreach($matchRes[0] as $find){
			$pose  = strpos($str,$find,$start);
			$end   = $pose + strlen($find);
			$findPad    = str_pad($find,$padLength,"0",STR_PAD_LEFT);
			$strNow     = $strResult.substr($str,$start,$pose - $start);
			$strResult  = $strNow.$findPad;
			// pr("start=$start;find=($find);pose=$pose; pre=(".substr($str,$pose-2,2).")");

			// 前面不是小数,则填充;  前面是小数:
			$start = $end;
			if($pose < 2 || !preg_match("/\d\./",substr($str,$pose-2,2)) ){continue;}
			if(!$numberPadArr){$strResult = $strNow.$find;continue;}
			if($end < $padPoseMin || $pose > $padPoseMax){$strResult = $strNow.$find;continue;}
			
			$needPad = false;
			foreach ($numberPadArr as $range){
				if($pose >= $range[0] && $end <= $range[1]){$needPad = true;break;}
			}
			if(!$needPad){$strResult = $strNow.$find;}
		};
		if($start > 0){$strResult .= substr($str,$start);}
		$match = preg_match("/[\x{4e00}-\x{9fa5}]/u",$strResult,$matchRes);
		if(!$match) return strtolower($strResult);
		
		$regChineseNum = "/(零|一|二|三|四|五|六|七|八|九|十|百|千|万|壹|贰|叁|肆|伍|陆|柒|捌|玖|拾|佰|仟|万|亿)+/";
		$strResult = preg_replace_callback($regChineseNum,'self::chineseNumberMake',$strResult);
		$strResult = Pinyin::get($strResult,'','','~');
		return strtolower($strResult);
	}
	public static function chineseNumberMake($match){
		static $oneChar = array('零'=>1,'拾'=>1,'百'=>1,'千'=>1,'万'=>1,'亿'=>1,'佰'=>1,'仟'=>1);
		$find = $match[0];
		if($oneChar[$find]) return $find;
		$number = self::chineseToNumber($find);
		if($number === -1) return $find;
		return '~'.str_pad($number,15,"0",STR_PAD_LEFT);
	}
	
	// 匹配计算需要填充的片段进行缓存; 版本号或ip则填充; 否则检测小数;
	public static function numberPadArr($str){
		$arr = array();$start = 0; // [[2,5],[20,24],...]
		$regMatch = "/\d+(\.\d+){2,}/i";
		$match = preg_match_all($regMatch,$str,$matchRes);
		if(!$match || !$matchRes[0]) return $arr;
		
		foreach($matchRes[0] as $find){
			$pose  = stripos($str,$find,$start);
			$end   = $pose + strlen($find);
			$arr[] = array($pose,$end,$find);$start = $end;
		}
		return $arr;
	}
	public static function split($str){
		$arr = @preg_split("//u",$str,-1,PREG_SPLIT_NO_EMPTY);
		return is_array($arr) ? $arr : array();
	}
	
	// 阿拉伯数字转中文数字; 不支持小数和正负数, 最长16位(一亿亿-1);
	public static function numberToChinese($str){
		$str    = preg_replace("/[+-]|(\.\d*)/",'',$str.'');
		$result = array('@');$unitNo = 0;$resultStr='';$len = strlen($str);
		$unit   = array('十','百','千','万','十','百','千','亿','十','百','千');
		$numChar= array('零','一','二','三','四','五','六','七','八', '九');
		if($str !== '0'){$str = ltrim($str,'0');}
		if($str == '' || $len > 16) return '';
		if($len == 1){return $numChar[intval($str)] ? $numChar[intval($str)]:'';}
		
		if($len >= 9){
			$halfLeft  = substr($str,0,-8);
			$halfRight = ltrim(substr($str,-8),'0');
			$halfAdd   = '亿';
			if($halfRight && $halfLeft[strlen($halfLeft) - 1] === '0'){$halfAdd = '亿零';}
			if($halfRight && strlen($halfRight) < 8){$halfAdd = '亿零';}
			$resultStr = self::numberToChinese($halfLeft) .$halfAdd. self::numberToChinese($halfRight);
			$resultStr = preg_replace("/亿(一)*亿/","亿",$resultStr);
			return $resultStr;
		}
				
		for ($i = $len - 1;;$i--){
			array_unshift($result,$numChar[$str[$i]]);
			if ($i <= 0) {break;}
			array_unshift($result,$unit[$unitNo]);;$unitNo++;
		}
		$resultStr = implode('',$result);
		$resultStr = preg_replace("/(零(千|百|十)){1,3}/","零",$resultStr);
		$resultStr = preg_replace("/(零){2,}/","零",$resultStr);
		$resultStr = preg_replace("/零(万|亿)/","$1零",$resultStr);
		$resultStr = preg_replace("/(零){2,}/","零",$resultStr);
		$resultStr = preg_replace("/亿万/","亿",$resultStr);
		$resultStr = preg_replace("/^一十/","十",$resultStr);
		$resultStr = preg_replace("/(零)*@/","",$resultStr);
		return $resultStr;
	}
	
	public static $chineseToNumberMap = array(
		"零"=>0,
		"一"=>1,"壹"=>1,
		"二"=>2,"贰"=>2,"两"=>2,
		"三"=>3,"叁"=>3,
		"四"=>4,"肆"=>4,
		"五"=>5,"伍"=>5,
		"六"=>6,"陆"=>6,
		"七"=>7,"柒"=>7,
		"八"=>8,"捌"=>8,
		"九"=>9,"玖"=>9,
		"十"=>10,"拾"=>10,
		"百"=>100,"佰"=>100,
		"千"=>1000,"仟"=>1000,		
		"万"=>10000,"十万"=>100000,"百万"=>1000000,"千万"=>10000000,"亿"=>100000000
	);
	
	// 中文数字转阿拉伯数字; 不支持小数和正负数, 最长16位(一亿亿-1);
	public static function chineseToNumber($str){
		$str = $str.'';if($str != '零'){$str = ltrim($str,'零');}
		$strArr = self::split($str);$len = count($strArr);
		$summary = 0; $map = &self::$chineseToNumberMap;
        if ($len == 0) return -1;
        if ($len == 1) return ($map[$str] <= 10) ? $map[$str] : -1;
        if ($map[$strArr[0]] == 10) {
			$str    = "一".$str;
			$strArr = self::split($str);$len = count($strArr);
		}
        if ($len >= 3 && $map[$strArr[$len-1]] < 10) {
            $lastSecondNum = $map[$strArr[$len-2]];
            if ($lastSecondNum == 100 || $lastSecondNum == 1000 || 
				$lastSecondNum == 10000 || $lastSecondNum == 100000000) {
                foreach ($map as $key) {
                    if ($map[$key] == $lastSecondNum / 10) {
						$str    = $str.$key;
						$strArr = self::split($str);$len = count($strArr);
						break;
                    }
                }
            }
        }
		if(strpos($str,'亿亿') > 0) return -1;
        $splited = explode("亿",$str);
        if ($splited && count($splited) == 2) {
            $rest = $splited[1] == "" ? 0 : self::chineseToNumber($splited[1]);
            return $summary + self::chineseToNumber($splited[0]) * 100000000 + $rest;
        }
		$splited = explode("万",$str);
        if ($splited && count($splited) == 2) {
            $rest = $splited[1] == "" ? 0 : self::chineseToNumber($splited[1]);
            return $summary + self::chineseToNumber($splited[0]) * 10000 + $rest;
        }
        $i = 0;
        while ($i < $len) {
            $firstCharNum = $map[$strArr[$i]];$secondCharNum = $map[$strArr[$i + 1]];
            if ($secondCharNum > 9){$summary += $firstCharNum * $secondCharNum;}
            $i++;
            if ($i == $len){$summary += $firstCharNum <= 9 ? $firstCharNum : 0;}
        }
        return $summary;
	}
}
KodUser.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodUser.class.php'
View Content
<?php 

/**
 * 用户相关信息处理
 */
class KodUser{
	public static function id($setUserID=-1){
		static $_userID = -1; // 管理员--切换用户身份;获取配置等操作;
		if($setUserID === 'clear'){$_userID = -1;return;}
		if($setUserID !== -1){$_userID = intval($setUserID);return;}
		if($_userID   !== -1){return $_userID;}
		
		return defined('USER_ID') ? USER_ID:0;
	}
	public static function isLogin(){
		$user = Session::get('kodUser');
		return (is_array($user) && isset($user['userID'])) ? 1 : 0;
	}
	public static function checkLogin(){
		$user = Session::get('kodUser');
		$code = (is_array($user) && isset($user['userID'])) ? 1 : 0;
		if(!$code){show_json(LNG('user.loginFirst'),false);}
		return $user;
	}
	
	public static function isRoot(){
		return (isset($GLOBALS['isRoot']) && $GLOBALS['isRoot'] == 1) ? 1:0;
	}
	public static function checkRoot(){
		$code = (isset($GLOBALS['isRoot']) && $GLOBALS['isRoot'] == 1) ? 1:0;
		if(!$code){show_json(LNG('explorer.noPermissionAction'),false);}
	}

	/**
	 * 解析加盐密码
	 * @param [type] $pass
	 * @param integer $salt
	 * @return void
	 */
	public static function parsePass($pass, $salt=0){
		$pass = trim($pass);
		if (!$pass) return $pass;
		// $pass = rawurldecode($pass);	// 后台接受的参数已经过解码,有特殊字符时再次解码会导致错误
		if (_get($GLOBALS, 'in.salt', 0) != '1' && $salt != '1') return $pass;
		$key  = substr($pass,0,5)."2&$%@(*@(djfhj1923";
		$pass = Mcrypt::decode(substr($pass,5),$key);
		return trim($pass);
	}

}
Mcrypt.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Mcrypt.class.php'
View Content
<?php

/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*------
* 字符串加解密类;
* 一次一密;且定时解密有效
* 可用于加密&动态key生成
* demo:	
* 加密:echo Mcrypt::encode('abc','123');
* 解密:echo Mcrypt::decode('9f843I0crjv5y0dWE_-uwzL_mZRyRb1ynjGK4I_IACQ','123');
*/

class Mcrypt{
	public static $defaultKey = 'a!takA:dlmcldEv,e';
	
	/**
	 * 字符加解密,一次一密,可定时解密有效
	 * 
	 * @param string $string 原文或者密文
	 * @param string $operation 操作(encode | decode)
	 * @param string $key 密钥
	 * @param int $expiry 密文有效期,单位s,0 为永久有效
	 * @return string 处理后的 原文或者 经过 base64_encode 处理后的密文
	 */
	public static function encode($string,$key = '', $expiry = 0,$cKeySet='',$encode=true){
		if($encode){$string = rawurlencode($string);}
		$ckeyLength = 4;
		
		$key = md5($key ? $key : self::$defaultKey); //解密密匙
		$keya = md5(substr($key, 0, 16));		 //做数据完整性验证  
		$keyb = md5(substr($key, 16, 16));		 //用于变化生成的密文 (初始化向量IV)		
		$cKeySet = $cKeySet ? $cKeySet: md5(microtime());
		$keyc = substr($cKeySet, - $ckeyLength);
		$cryptkey = $keya . md5($keya . $keyc);  
		$keyLength = strlen($cryptkey);
		$string = sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string . $keyb), 0, 16) . $string;
		$stringLength = strlen($string);

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

		$box = range(0, 255);	
		// 打乱密匙簿,增加随机性
		for($j = $i = 0; $i < 256; $i++) {
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;
			$tmp = $box[$i];
			$box[$i] = $box[$j];
			$box[$j] = $tmp;
		}	
		// 加解密,从密匙簿得出密匙进行异或,再转成字符
		$result = '';
		for($a = $j = $i = 0; $i < $stringLength; $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]));
		}
		$result = $keyc . str_replace('=', '', base64_encode($result));
		$result = str_replace(array('+', '/', '='),array('-', '_', '.'), $result);
		return $result;
	}

	/**
	 * 字符加解密,一次一密,可定时解密有效
	 * 
	 * @param string $string 原文或者密文
	 * @param string $operation 操作(encode | decode)
	 * @param string $key 密钥
	 * @param int $expiry 密文有效期,单位s,0 为永久有效
	 * @return string 处理后的 原文或者 经过 base64_encode 处理后的密文
	 */
	public static function decode($string,$key = '',$encode=true){
		$string = str_replace(array('-', '_', '.'),array('+', '/', '='), $string);
		$ckeyLength = 4;
		$key = md5($key ? $key : self::$defaultKey); //解密密匙
		$keya = md5(substr($key, 0, 16));		 //做数据完整性验证  
		$keyb = md5(substr($key, 16, 16));		 //用于变化生成的密文 (初始化向量IV)
		$keyc = substr($string, 0, $ckeyLength);
		$cryptkey = $keya . md5($keya . $keyc);  
		$keyLength = strlen($cryptkey);
		$string = base64_decode(substr($string, $ckeyLength));
		$stringLength = strlen($string);

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

		$box = range(0, 255);
		// 打乱密匙簿,增加随机性
		for($j = $i = 0; $i < 256; $i++) {
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;
			$tmp = $box[$i];
			$box[$i] = $box[$j];
			$box[$j] = $tmp;
		}
		// 加解密,从密匙簿得出密匙进行异或,再转成字符
		$result = '';
		for($a = $j = $i = 0; $i < $stringLength; $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]));
		}
		$theTime = intval(substr($result, 0, 10));
		$resultStr  = '';
		if (($theTime == 0 || $theTime - time() > 0)
		&& substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)
		) {
			$resultStr = substr($result, 26);
			if($encode){$resultStr = rawurldecode($resultStr);}
		}
		return $resultStr;
	}
}
Services_JSON.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Services_JSON.class.php'
View Content
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Converts to and from JSON format.
 *
 * JSON (JavaScript Object Notation) is a lightweight data-interchange
 * format. It is easy for humans to read and write. It is easy for machines
 * to parse and generate. It is based on a subset of the JavaScript
 * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
 * This feature can also be found in  Python. JSON is a text format that is
 * completely language independent but uses conventions that are familiar
 * to programmers of the C-family of languages, including C, C++, C#, Java,
 * JavaScript, Perl, TCL, and many others. These properties make JSON an
 * ideal data-interchange language.
 *
 * This package provides a simple encoder and decoder for JSON notation. It
 * is intended for use with client-side Javascript applications that make
 * use of HTTPRequest to perform server communication functions - data can
 * be encoded into JSON notation for use in a client-side javascript, or
 * decoded from incoming Javascript requests. JSON format is native to
 * Javascript, and can be directly eval()'ed with no further parsing
 * overhead
 *
 * All strings should be in ASCII or UTF-8 format!
 *
 * LICENSE: Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met: Redistributions of source code must retain the
 * above copyright notice, this list of conditions and the following
 * disclaimer. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * @category
 * @package     Services_JSON
 * @author      Michal Migurski <mike-json@teczno.com>
 * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
 * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
 * @copyright   2005 Michal Migurski
 * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
 * @license     http://www.opensource.org/licenses/bsd-license.php
 * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
 */

/**
 * Marker constant for Services_JSON::decode(), used to flag stack state
 */
define('SERVICES_JSON_SLICE',   1);

/**
 * Marker constant for Services_JSON::decode(), used to flag stack state
 */
define('SERVICES_JSON_IN_STR',  2);

/**
 * Marker constant for Services_JSON::decode(), used to flag stack state
 */
define('SERVICES_JSON_IN_ARR',  3);

/**
 * Marker constant for Services_JSON::decode(), used to flag stack state
 */
define('SERVICES_JSON_IN_OBJ',  4);

/**
 * Marker constant for Services_JSON::decode(), used to flag stack state
 */
define('SERVICES_JSON_IN_CMT', 5);

/**
 * Behavior switch for Services_JSON::decode()
 */
define('SERVICES_JSON_LOOSE_TYPE', 16);

/**
 * Behavior switch for Services_JSON::decode()
 */
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);

/**
 * Converts to and from JSON format.
 *
 * Brief example of use:
 *
 * <code>
 * // create a new instance of Services_JSON
 * $json = new Services_JSON();
 *
 * // convert a complexe value to JSON notation, and send it to the browser
 * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
 * $output = $json->encode($value);
 *
 * print($output);
 * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
 *
 * // accept incoming POST data, assumed to be in JSON notation
 * $input = file_get_contents('php://input', 1000000);
 * $value = $json->decode($input);
 * </code>
 */
class Services_JSON
{
   /**
    * constructs a new JSON instance
    *
    * @param    int     $use    object behavior flags; combine with boolean-OR
    *
    *                           possible values:
    *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
    *                                   "{...}" syntax creates associative arrays
    *                                   instead of objects in decode().
    *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
    *                                   Values which can't be encoded (e.g. resources)
    *                                   appear as NULL instead of throwing errors.
    *                                   By default, a deeply-nested resource will
    *                                   bubble up with an error, so all return values
    *                                   from encode() should be checked with isError()
    */
    function __construct($use = 0)
    {
        $this->use = $use;
    }

   /**
    * convert a string from one UTF-16 char to one UTF-8 char
    *
    * Normally should be handled by mb_convert_encoding, but
    * provides a slower PHP-only method for installations
    * that lack the multibye string extension.
    *
    * @param    string  $utf16  UTF-16 character
    * @return   string  UTF-8 character
    * @access   private
    */
    function utf162utf8($utf16)
    {
        // oh please oh please oh please oh please oh please
        if(function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
        }

        $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);

        switch(true) {
            case ((0x7F & $bytes) == $bytes):
                // this case should never be reached, because we are in ASCII range
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return chr(0x7F & $bytes);

            case (0x07FF & $bytes) == $bytes:
                // return a 2-byte UTF-8 character
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return chr(0xC0 | (($bytes >> 6) & 0x1F))
                     . chr(0x80 | ($bytes & 0x3F));

            case (0xFFFF & $bytes) == $bytes:
                // return a 3-byte UTF-8 character
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return chr(0xE0 | (($bytes >> 12) & 0x0F))
                     . chr(0x80 | (($bytes >> 6) & 0x3F))
                     . chr(0x80 | ($bytes & 0x3F));
        }

        // ignoring UTF-32 for now, sorry
        return '';
    }

   /**
    * convert a string from one UTF-8 char to one UTF-16 char
    *
    * Normally should be handled by mb_convert_encoding, but
    * provides a slower PHP-only method for installations
    * that lack the multibye string extension.
    *
    * @param    string  $utf8   UTF-8 character
    * @return   string  UTF-16 character
    * @access   private
    */
    function utf82utf16($utf8)
    {
        // oh please oh please oh please oh please oh please
        if(function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
        }

        switch(strlen($utf8)) {
            case 1:
                // this case should never be reached, because we are in ASCII range
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return $utf8;

            case 2:
                // return a UTF-16 character from a 2-byte UTF-8 char
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return chr(0x07 & (ord($utf8[0]) >> 2))
                     . chr((0xC0 & (ord($utf8[0]) << 6))
                         | (0x3F & ord($utf8[1])));

            case 3:
                // return a UTF-16 character from a 3-byte UTF-8 char
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                return chr((0xF0 & (ord($utf8[0]) << 4))
                         | (0x0F & (ord($utf8[1]) >> 2)))
                     . chr((0xC0 & (ord($utf8[1]) << 6))
                         | (0x7F & ord($utf8[2])));
        }

        // ignoring UTF-32 for now, sorry
        return '';
    }

   /**
    * encodes an arbitrary variable into JSON format
    *
    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
    *                           if var is a strng, note that encode() always expects it
    *                           to be in ASCII or UTF-8 format!
    *
    * @return   mixed   JSON string representation of input var or an error if a problem occurs
    * @access   public
    */
    function encode($var)
    {
        switch (gettype($var)) {
            case 'boolean':
                return $var ? 'true' : 'false';

            case 'NULL':
                return 'null';

            case 'integer':
                return (int) $var;

            case 'double':
            case 'float':
                return (float) $var;

            case 'string':
                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
                $ascii = '';
                $strlen_var = strlen($var);

               /*
                * Iterate over every character in the string,
                * escaping with a slash or encoding to UTF-8 where necessary
                */
                for ($c = 0; $c < $strlen_var; ++$c) {

                    $ord_var_c = ord($var[$c]);

                    switch (true) {
                        case $ord_var_c == 0x08:
                            $ascii .= '\b';
                            break;
                        case $ord_var_c == 0x09:
                            $ascii .= '\t';
                            break;
                        case $ord_var_c == 0x0A:
                            $ascii .= '\n';
                            break;
                        case $ord_var_c == 0x0C:
                            $ascii .= '\f';
                            break;
                        case $ord_var_c == 0x0D:
                            $ascii .= '\r';
                            break;

                        case $ord_var_c == 0x22:
                        case $ord_var_c == 0x2F:
                        case $ord_var_c == 0x5C:
                            // double quote, slash, slosh
                            $ascii .= '\\'.$var[$c];
                            break;

                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
                            // characters U-00000000 - U-0000007F (same as ASCII)
                            $ascii .= $var[$c];
                            break;

                        case (($ord_var_c & 0xE0) == 0xC0):
                            // characters U-00000080 - U-000007FF, mask 110XXXXX
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                            $char = pack('C*', $ord_var_c, ord($var[$c + 1]));
                            $c += 1;
                            $utf16 = $this->utf82utf16($char);
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
                            break;

                        case (($ord_var_c & 0xF0) == 0xE0):
                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                            $char = pack('C*', $ord_var_c,
                                         ord($var[$c + 1]),
                                         ord($var[$c + 2]));
                            $c += 2;
                            $utf16 = $this->utf82utf16($char);
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
                            break;

                        case (($ord_var_c & 0xF8) == 0xF0):
                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                            $char = pack('C*', $ord_var_c,
                                         ord($var[$c + 1]),
                                         ord($var[$c + 2]),
                                         ord($var[$c + 3]));
                            $c += 3;
                            $utf16 = $this->utf82utf16($char);
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
                            break;

                        case (($ord_var_c & 0xFC) == 0xF8):
                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                            $char = pack('C*', $ord_var_c,
                                         ord($var[$c + 1]),
                                         ord($var[$c + 2]),
                                         ord($var[$c + 3]),
                                         ord($var[$c + 4]));
                            $c += 4;
                            $utf16 = $this->utf82utf16($char);
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
                            break;

                        case (($ord_var_c & 0xFE) == 0xFC):
                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                            $char = pack('C*', $ord_var_c,
                                         ord($var[$c + 1]),
                                         ord($var[$c + 2]),
                                         ord($var[$c + 3]),
                                         ord($var[$c + 4]),
                                         ord($var[$c + 5]));
                            $c += 5;
                            $utf16 = $this->utf82utf16($char);
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
                            break;
                    }
                }

                return '"'.$ascii.'"';

            case 'array':
               /*
                * As per JSON spec if any array key is not an integer
                * we must treat the the whole array as an object. We
                * also try to catch a sparsely populated associative
                * array with numeric keys here because some JS engines
                * will create an array with empty indexes up to
                * max_index which can cause memory issues and because
                * the keys, which may be relevant, will be remapped
                * otherwise.
                *
                * As per the ECMA and JSON specification an object may
                * have any string as a property. Unfortunately due to
                * a hole in the ECMA specification if the key is a
                * ECMA reserved word or starts with a digit the
                * parameter is only accessible using ECMAScript's
                * bracket notation.
                */

                // treat as a JSON object
                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
                    $properties = array_map(array($this, 'name_value'),
                                            array_keys($var),
                                            array_values($var));

                    foreach($properties as $property) {
                        if(Services_JSON::isError($property)) {
                            return $property;
                        }
                    }

                    return '{' . join(',', $properties) . '}';
                }

                // treat it like a regular array
                $elements = array_map(array($this, 'encode'), $var);

                foreach($elements as $element) {
                    if(Services_JSON::isError($element)) {
                        return $element;
                    }
                }

                return '[' . join(',', $elements) . ']';

            case 'object':
                $vars = get_object_vars($var);

                $properties = array_map(array($this, 'name_value'),
                                        array_keys($vars),
                                        array_values($vars));

                foreach($properties as $property) {
                    if(Services_JSON::isError($property)) {
                        return $property;
                    }
                }

                return '{' . join(',', $properties) . '}';

            default:
                return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
                    ? 'null'
                    : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
        }
    }

   /**
    * array-walking function for use in generating JSON-formatted name-value pairs
    *
    * @param    string  $name   name of key to use
    * @param    mixed   $value  reference to an array element to be encoded
    *
    * @return   string  JSON-formatted name-value pair, like '"name":value'
    * @access   private
    */
    function name_value($name, $value)
    {
        $encoded_value = $this->encode($value);

        if(Services_JSON::isError($encoded_value)) {
            return $encoded_value;
        }

        return $this->encode(strval($name)) . ':' . $encoded_value;
    }

   /**
    * reduce a string by removing leading and trailing comments and whitespace
    *
    * @param    $str    string      string value to strip of comments and whitespace
    *
    * @return   string  string value stripped of comments and whitespace
    * @access   private
    */
    function reduce_string($str){
        $str = preg_replace(array(

                // eliminate single line comments in '// ...' form
                '#^\s*//(.+)$#m',

                // eliminate multi-line comments in '/* ... */' form, at start of string
                '#^\s*/\*(.+)\*/#Us',

                // eliminate multi-line comments in '/* ... */' form, at end of string
                '#/\*(.+)\*/\s*$#Us'

            ), '', $str);

        // eliminate extraneous space
        return trim($str);
    }

   /**
    * decodes a JSON string into appropriate variable
    *
    * @param    string  $str    JSON-formatted string
    *
    * @return   mixed   number, boolean, string, array, or object
    *                   corresponding to given JSON input string.
    *                   See argument 1 to Services_JSON() above for object-output behavior.
    *                   Note that decode() always returns strings
    *                   in ASCII or UTF-8 format!
    * @access   public
    */
    function decode($str)
    {
        $str = $this->reduce_string($str);

        switch (strtolower($str)) {
            case 'true':
                return true;

            case 'false':
                return false;

            case 'null':
                return null;

            default:
                $m = array();

                if (is_numeric($str)) {
                    // Lookie-loo, it's a number

                    // This would work on its own, but I'm trying to be
                    // good about returning integers where appropriate:
                    // return (float)$str;

                    // Return float or int, as appropriate
                    return ((float)$str == (integer)$str)
                        ? (integer)$str
                        : (float)$str;

                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
                    // STRINGS RETURNED IN UTF-8 FORMAT
                    $delim = substr($str, 0, 1);
                    $chrs = substr($str, 1, -1);
                    $utf8 = '';
                    $strlen_chrs = strlen($chrs);

                    for ($c = 0; $c < $strlen_chrs; ++$c) {

                        $substr_chrs_c_2 = substr($chrs, $c, 2);
                        $ord_chrs_c = ord($chrs[$c]);

                        switch (true) {
                            case $substr_chrs_c_2 == '\b':
                                $utf8 .= chr(0x08);
                                ++$c;
                                break;
                            case $substr_chrs_c_2 == '\t':
                                $utf8 .= chr(0x09);
                                ++$c;
                                break;
                            case $substr_chrs_c_2 == '\n':
                                $utf8 .= chr(0x0A);
                                ++$c;
                                break;
                            case $substr_chrs_c_2 == '\f':
                                $utf8 .= chr(0x0C);
                                ++$c;
                                break;
                            case $substr_chrs_c_2 == '\r':
                                $utf8 .= chr(0x0D);
                                ++$c;
                                break;

                            case $substr_chrs_c_2 == '\\"':
                            case $substr_chrs_c_2 == '\\\'':
                            case $substr_chrs_c_2 == '\\\\':
                            case $substr_chrs_c_2 == '\\/':
                                if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
                                   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
                                    $utf8 .= $chrs[++$c];
                                }
                                break;

                            case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
                                // single, escaped unicode character
                                $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
                                       . chr(hexdec(substr($chrs, ($c + 4), 2)));
                                $utf8 .= $this->utf162utf8($utf16);
                                $c += 5;
                                break;

                            case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
                                $utf8 .= $chrs[$c];
                                break;

                            case ($ord_chrs_c & 0xE0) == 0xC0:
                                // characters U-00000080 - U-000007FF, mask 110XXXXX
                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                                $utf8 .= substr($chrs, $c, 2);
                                ++$c;
                                break;

                            case ($ord_chrs_c & 0xF0) == 0xE0:
                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX
                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                                $utf8 .= substr($chrs, $c, 3);
                                $c += 2;
                                break;

                            case ($ord_chrs_c & 0xF8) == 0xF0:
                                // characters U-00010000 - U-001FFFFF, mask 11110XXX
                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                                $utf8 .= substr($chrs, $c, 4);
                                $c += 3;
                                break;

                            case ($ord_chrs_c & 0xFC) == 0xF8:
                                // characters U-00200000 - U-03FFFFFF, mask 111110XX
                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                                $utf8 .= substr($chrs, $c, 5);
                                $c += 4;
                                break;

                            case ($ord_chrs_c & 0xFE) == 0xFC:
                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X
                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
                                $utf8 .= substr($chrs, $c, 6);
                                $c += 5;
                                break;

                        }

                    }

                    return $utf8;

                } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
                    // array, or object notation

                    if ($str[0]== '[') {
                        $stk = array(SERVICES_JSON_IN_ARR);
                        $arr = array();
                    } else {
                        if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
                            $stk = array(SERVICES_JSON_IN_OBJ);
                            $obj = array();
                        } else {
                            $stk = array(SERVICES_JSON_IN_OBJ);
                            $obj = new stdClass();
                        }
                    }

                    array_push($stk, array('what'  => SERVICES_JSON_SLICE,
                                           'where' => 0,
                                           'delim' => false));

                    $chrs = substr($str, 1, -1);
                    $chrs = $this->reduce_string($chrs);

                    if ($chrs == '') {
                        if (reset($stk) == SERVICES_JSON_IN_ARR) {
                            return $arr;

                        } else {
                            return $obj;

                        }
                    }

                    //print("\nparsing {$chrs}\n");

                    $strlen_chrs = strlen($chrs);

                    for ($c = 0; $c <= $strlen_chrs; ++$c) {

                        $top = end($stk);
                        $substr_chrs_c_2 = substr($chrs, $c, 2);

                        if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
                            // found a comma that is not inside a string, array, etc.,
                            // OR we've reached the end of the character list
                            $slice = substr($chrs, $top['where'], ($c - $top['where']));
                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
                            //print("Found split at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");

                            if (reset($stk) == SERVICES_JSON_IN_ARR) {
                                // we are in an array, so just push an element onto the stack
                                array_push($arr, $this->decode($slice));

                            } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
                                // we are in an object, so figure
                                // out the property name and set an
                                // element in an associative array,
                                // for now
                                $parts = array();
                                
                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
                                    // "name":value pair
                                    $key = $this->decode($parts[1]);
                                    $val = $this->decode($parts[2]);

                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
                                        $obj[$key] = $val;
                                    } else {
                                        $obj->$key = $val;
                                    }
                                } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
                                    // name:value pair, where name is unquoted
                                    $key = $parts[1];
                                    $val = $this->decode($parts[2]);

                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
                                        $obj[$key] = $val;
                                    } else {
                                        $obj->$key = $val;
                                    }
                                }

                            }

                        } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
                            // found a quote, and we are not inside a string
                            array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
                            //print("Found start of string at [$c]\n");

                        } elseif (($chrs[$c] == $top['delim']) &&
                                 ($top['what'] == SERVICES_JSON_IN_STR) &&
                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
                            // found a quote, we're in a string, and it's not escaped
                            // we know that it's not escaped becase there is _not_ an
                            // odd number of backslashes at the end of the string so far
                            array_pop($stk);
                            //print("Found end of string at [$c]: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");

                        } elseif (($chrs[$c] == '[') &&
                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
                            // found a left-bracket, and we are in an array, object, or slice
                            array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
                            //print("Found start of array at [$c]\n");

                        } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
                            // found a right-bracket, and we're in an array
                            array_pop($stk);
                            //print("Found end of array at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");

                        } elseif (($chrs[$c] == '{') &&
                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
                            // found a left-brace, and we are in an array, object, or slice
                            array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
                            //print("Found start of object at [$c]\n");

                        } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
                            // found a right-brace, and we're in an object
                            array_pop($stk);
                            //print("Found end of object at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");

                        } elseif (($substr_chrs_c_2 == '/*') &&
                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
                            // found a comment start, and we are in an array, object, or slice
                            array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
                            $c++;
                            //print("Found start of comment at [$c]\n");

                        } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
                            // found a comment end, and we're in one now
                            array_pop($stk);
                            $c++;

                            for ($i = $top['where']; $i <= $c; ++$i)
                                $chrs = substr_replace($chrs, ' ', $i, 1);

                            //print("Found end of comment at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");

                        }

                    }

                    if (reset($stk) == SERVICES_JSON_IN_ARR) {
                        return $arr;

                    } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
                        return $obj;

                    }
                }
        }
    }

    /**
     * @todo Ultimately, this should just call PEAR::isError()
     */
    function isError($data, $code = null){
        if (class_exists('pear')) {
            return PEAR::isError($data, $code);
        } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
                                 is_subclass_of($data, 'services_json_error'))) {
            return true;
        }
        return false;
    }
}

if (class_exists('PEAR_Error')) {
    class Services_JSON_Error extends PEAR_Error{
        function __construct($message = 'unknown error', $code = null,
                                     $mode = null, $options = null, $userinfo = null){
            parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
        }
    }
} else {
    class Services_JSON_Error{
        function __construct($message = 'unknown error', $code = null,
                                     $mode = null, $options = null, $userinfo = null){
        }
    }
}
Uploader.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Uploader.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class Uploader{
	public $fileName;
	public $uploadFile;
	public $tempFile;

	public function __construct(){
		global $in;
		$this->in = &$in;
		if (!empty($_FILES)) {
			$files  = $_FILES['file'];
			$this->uploadFile = $files["tmp_name"];
			if(!$this->uploadFile && $files['error']> 0){
				show_json($this->errorInfo($files['error']),false);
			}
		}else if (isset($in["name"])) {
			$this->uploadFile = "php://input";
		}
		// $fp  = @fopen("php://input","rb");$head = fread($fp, 1024*10);fclose($fp);$S = $_SERVER;
		// write_log([$in,$this->uploadFile,@filesize($this->uploadFile),$S['CONTENT_LENGTH'],$S['CONTENT_TYPE'],$head],'test');
		
		if(isset($in['base64Upload']) && isset($in['base64str']) && $in['base64str']){
			$this->uploadFile = 'base64';
		}

		$this->fileName = self::fileName();
		$this->statusData = false;
		$this->checkSize();
		$this->tempPathInit();
	}

	/**
	 * 文件上传处理。大文件支持分片上传
	 * post上传:base64Upload=1;file=base64str;name=filename
	 * 
	 * chunk,chunks; 选填; 没有或chunks小于等于1则认为不分片;
	 * 分片则 size 必须传入
	 */
	public function upload(){
		if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' ){exit;};//跨域请求时,浏览器发送options接口会影响正常流程;
		$chunk  = isset($this->in["chunk"]) ? intval($this->in["chunk"]) : 0;
		$chunks = isset($this->in["chunks"]) ? intval($this->in["chunks"]) : 1;
		$dest   = $this->tempFile.'.part'.$chunk;
		$size   = isset($this->in["size"]) ? intval($this->in["size"]) : 0;
		$chunkSize  = isset($this->in["chunkSize"]) ? intval($this->in["chunkSize"]) : 0;
		if(	$chunks > 1 && $chunkSize <= 0 ){
			show_json('chunkSize error!',false);
		}
		
		if($chunkSize > $size){$chunks = 1;}
		if($chunks <= 1) {// 没有分片;
			// 清除秒传or断点续传请求checkChunk写入的数据;
			$this->statusSet(false);
			$this->tempFile = $this->tempFile.rand_string(5);
			$result = $this->moveUploadedFile($this->tempFile);
			return $this->uploadResult($result);
		}
		
		$fileHashSimple = '';
		$fileChunkSize  = ($chunk < $chunks - 1) ? $chunkSize : ($size - ($chunks - 1) * $chunkSize);
		//分片上传必须携带文件大小,及分片切分大小;
		CacheLock::lock($this->tempFile,30);
		$this->initFileTemp();
		CacheLock::unlock($this->tempFile);

		// 流式上传性能优化;不写入临时文件;直接写入到目标占位文件中;
		if($this->uploadFile == "php://input"){
			$chunkFile = $this->uploadFile; 
		}else{
			$chunkFile = $this->moveUploadedFile($dest);
			if($size > 0 && filesize($chunkFile) == 0){$this->showJson('0 Byte upload',false);}
			if(!$chunkFile){$this->showJson(LNG('explorer.moveError'),false);}
			$fileHashSimple = IO::hashSimple($chunkFile);
		}
		$offset = $chunk * $chunkSize;
		if(!$outFp = @fopen($this->tempFile, "r+")){
			$this->showJson(LNG('explorer.upload.fopenError').':'.$this->tempFile,false);
		}
		fseek_64($outFp,$offset);
		$success = $this->writeTo($chunkFile,$outFp,$this->tempFile);
		if(!$fileHashSimple){
			$outFp = fopen($this->tempFile,'r');
			if(!$outFp){$this->showJson(LNG('explorer.upload.fopenError').':'.$this->tempFile,false);}
			fseek_64($outFp,$offset);
			$fileHashSimple = PathDriverStream::hash($outFp,$fileChunkSize);
			fclose($outFp);
		}
		@unlink($chunkFile);
		if(!$success){$this->showJson('chunk_error:'.$chunk,false);}
		
		//分片成功:
		CacheLock::lock($this->tempFile.'.statusGet',30);
		$data = $this->statusGet();
		$data['chunkTotal'] = $chunks;
		$data['chunkArray']['chunk'.$chunk] = array(
			'offset'	=> $offset,
			'index' 	=> $chunk,
			'size'		=> $fileChunkSize,
			'hashSimple'=> $fileHashSimple,
		);
		$this->statusSet($data);
		CacheLock::unlock($this->tempFile.'.statusGet');
		
		if(count($data['chunkArray']) != $data['chunkTotal'] ){
			$this->showJson('chunk_success_'.$chunk,true);
		}
		
		// 所有分片完成,检测分片hash值一致性;
		if(!$this->checkChunkHash($data)){
			$this->showJson(LNG('explorer.upload.error')."[chunk hash error!]",false);
		}
		return $this->uploadResult($this->tempFile);
	}
	
	// 上传完成临时文件检测;
	private function uploadResult($file){
		$this->statusSet(false);//上传成功,清空相关配置;
		$size = isset($this->in["size"]) ? intval($this->in["size"]) : 0;
		$hash = isset($this->in["checkHashSimple"]) ? $this->in["checkHashSimple"] : '';
		if(!file_exists($file)){
			show_json(LNG('explorer.upload.error').' [temp not exist!]',false);
		}
		if($size && $size != filesize($file)){
			@unlink($file);
			show_json(LNG('explorer.upload.error').'[temp size error]',false);
		}
		return $file;
	}
	
	private function checkSize(){
		if(phpBuild64() || $this->in['size'] < PHP_INT_MAX) return;
		show_json(LNG('explorer.uploadSizeError'),false);
	}
	private function showJson($data,$code){
		CacheLock::unlock($this->tempFile);
		if(!$code){$this->clearData();}
		show_json($data,$code);
	}
	
	public function clearData(){
		$this->statusSet(false);
		if(file_exists($this->tempFile)){
			@unlink($this->tempFile);return;
		}
	}
	
	// 上传临时目录; 优化: 默认存储io为本地时,临时目录切换到对应目录的temp/下;(减少从头temp读取->写入到存储i)
	private function tempPathGet(){
		// return '/mnt/usb/tmp/';
		$tempPath = TEMP_FILES;
		$path = isset($GLOBALS['in']['path']) ? $GLOBALS['in']['path']:'';
		$driverInfo = KodIO::pathDriverType($path);
		if($driverInfo && $driverInfo['type'] == 'local'){
			$truePath = rtrim($driverInfo['path'],'/').'/';
			$isSame = KodIO::isSameDisk($truePath,TEMP_FILES);
			if(!$isSame && file_exists($truePath)){$tempPath = $truePath;}
		}
		
		if(!file_exists($tempPath)){
			@mk_dir($tempPath);
			touch($tempPath.'index.html');
		}
		return $tempPath;
	}
	
	// 临时文件;
	private function tempPathInit(){
		$tempPath = $this->tempPathGet();
		$tempName = isset($this->in['checkHashSimple']) ? $this->in['checkHashSimple']:false;
		if(strlen($tempName) < 30){ //32+大小;
			$tempName = md5(USER_ID.$this->in['path'].$this->fileName.$this->in['size']);
		}
		$name = ".upload_".md5($tempName.$this->in['chunkSize']);
		$this->tempFile = $this->tempPathGet().$name;
	}
	// 兼容move_uploaded_file 和 流的方式上传
	private function moveUploadedFile($dest){
		$fromPath = $this->uploadFile;
		if($fromPath == "base64"){
			@file_put_contents($dest,base64_decode($_REQUEST['base64str']));
		}else if($fromPath == "php://input"){
			$out = @fopen($dest, "wb");
			$this->writeTo($fromPath,$out,$dest);
		}else{
			if(!move_uploaded_file($fromPath,$dest)){return false;}
		}
		return $dest;
	}

	private function writeTo($from,$outFp,$outName){
		$lockKey = 'Uploader.writeTo'.$outName;
		//$isLock= CacheLock::lock($lockKey,1); //不用锁;[多个进程独立写入]
		$in  = @fopen($from,"rb");
		if(!$in || !$outFp){return false;}
		while (!feof($in)) {
			fwrite($outFp, fread($in, 1024*500));
		}
		fclose($in);fclose($outFp);
		return true;
	}

	private function statusGet(){
		if( is_array($this->statusData)) return $this->statusData;
		$file = $this->tempFile.'.cfg';
		$content = false;
		if(file_exists($file)){
			$content = @file_get_contents($file);
		}
		if($content){
			$this->statusData = json_decode($content,true);
		}
		if(!$this->statusData){
			$defaultData = array(
				'name'		=> $this->fileName,
				'chunkTotal'=> 0,
				'chunkArray'=> array(), // chunk2=>{offset:xxx,index:2,hashSimple:xxx}
			);
			$this->statusSet($defaultData);
		}
		return $this->statusData;
	}	
	public function statusSet($data){
		$file = $this->tempFile.'.cfg';
		if(!$data){
			return @unlink($file);
		}
		$this->statusData = $data;
		return file_put_contents($file,json_encode($data));
	}
	
	// 生成占位文件;
	private function initFileTemp(){
		if( file_exists($this->tempFile) ) return;
		if(!$fp = fopen($this->tempFile,'wb+')){
			$this->showJson(LNG('explorer.upload.fopenError').':'.$this->tempFile,false);
		}
		fseek_64($fp,$this->in['size']-1,SEEK_SET);
		fwrite($fp,'0');
		fclose($fp);
	}
		
	//文件分块检测是否已上传,已上传则忽略;断点续传
	public function checkChunk(){
		$result = array();
		CacheLock::lock($this->tempFile);
		$data  = $this->statusGet();
		CacheLock::unlock($this->tempFile);
		foreach ($data['chunkArray'] as $item) {
			$hash = $item['hashSimple'];
			if($hash){
				$result['part_'.$item['index']] = $hash;
			}
		}
		return $result;
	}
	
	// 所有分片完成,检测simpleHash 及md5;
	private function checkChunkHash($data){
		if(count($data['chunkArray']) != $data['chunkTotal'] ){return false;}
		$fileHash = _get($this->in,'checkHashSimple');
		if($fileHash){return IO::hashSimple($this->tempFile) == $fileHash;}

		if(!$fp = fopen($this->tempFile,'r')) return false;
		$success = true;
		foreach ($data['chunkArray'] as $item) {
			fseek_64($fp,$item['offset']);
			$chunkHash = PathDriverStream::hash($fp,$item['size']);
			if($item['hashSimple'] != $chunkHash){
				$success = false;break;
			}
		}
		fclose($fp);
		return $success;
	}
	
	//拍照上传等情况兼容处理;
	public static function fileName(){
		global $in;
		$fileName = isset($in['name']) ? $in['name'] :'';
		if (!empty($_FILES)) {
			$fileName = $fileName ? $fileName : $_FILES['file']["name"];
		}
		if(!is_wap()) return KodIO::clear($fileName);
		
		//ios 上传没有文件名问题处理
		$time = strtotime($in['lastModifiedDate']);
		$time = $time ? $time : time();
		$beforeName = strtolower($fileName);
		if($beforeName == "image.jpg" || $beforeName == "image.jpeg"){
			$fileName =  date('Ymd',$time).'_'.$in['size'].'.jpg';
		}else if($beforeName == "capturedvideo.mov"){
			$fileName =  date('Ymd',$time).'_'.$in['size'].'.mov';
		}
		return KodIO::clear($fileName);
	}
	
	private function errorInfo($error){
		$status = array(
			'UPLOAD_ERR_OK',        //0 没有错误发生,文件上传成功。
			'UPLOAD_ERR_INI_SIZE',  //1 上传的文件超过了php.ini 中 upload_max_filesize 选项限制的值。
			'UPLOAD_ERR_FORM_SIZE', //2 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。
			'UPLOAD_ERR_PARTIAL',   //3 文件只有部分被上传。
			'UPLOAD_ERR_NO_FILE',   //4 没有文件被上传。
			'UPLOAD_UNKNOW',		//5 未定义
			'UPLOAD_ERR_NO_TMP_DIR',//6 找不到临时文件夹。php 4.3.10 和 php 5.0.3 引进。
			'UPLOAD_ERR_CANT_WRITE',//7 文件写入失败。php 5.1.0 引进。
		);
		return $error.':'.$status[$error];
	}
}
ZipMake.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ZipMake.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/


/**
 * zip解压缩处理类
 * 
 * 
 * http://localhost/kod/kod_dev/?editor&project=/Library/WebServer/Documents/localhost/works/zip64/
 * https://blog.csdn.net/a200710716/article/details/51644421
 * https://github.com/brokencube/ZipStream64/blob/14087549a4914bfc441a396ca02849569145a273/src/ZipStream.php#L808
 * https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.2.0.txt
 */
class ZipMake{
    const VERSION	        = '0.2.0';
	const ZIP_VERSION	    = 0x000A;
	const ZIP_VERSION_64	= 0x002D;
	const METHOD_STORE	    = 0x00;

    const FILE_HEADER_SIGNATURE         = 0x04034b50;   //'504b0304'
	const CDR_FILE_SIGNATURE            = 0x02014b50;   //'504b0102'
	const CDR_EOF_SIGNATURE             = 0x06054b50;   //'504b0506'
	const DATA_DESCRIPTOR_SIGNATURE     = 0x08074b50;   //'504b0708'
	const ZIP64_CDR_EOF_SIGNATURE       = 0x06064b50;   //'504b0606'
	const ZIP64_CDR_LOCATOR_SIGNATURE	= 0x07064b50;   //'504b0607'

	public $files = array();
	public $cdrOffset = 0;
    public $ofs = 0;
    
	protected $needHeaders;
	protected $outputName;
	public function __construct($name = null){
        $this->outputStream = fopen('php://output', 'w');	
		$this->outputName   = $name;
		$this->needHeaders  = true;
	}

	/**
	 * addFileFromPath
	 * add a file at path to the archive.
	 */
	public function addFile($name, $path){
        $name = $this->filterFilename($name);
		$zipMethod = static::METHOD_STORE;
        $headerLength = $this->addFileHeader($name,$zipMethod);

        $zipLength = $fileLength = filesize_64($path);
        $fh = fopen($path, 'rb');
        while (!feof($fh)) {
			$data = fread($fh, 1048576);
			$this->send($data);
		}
        fclose($fh);
        $crc = hexdec(hash_file('crc32b', $path));
		$this->addFileFooter($name,$zipMethod, $crc, $zipLength, $fileLength, $headerLength);
	}

	/**
	 * addFile_from_stream
	 */
	public function addFileFromStream($name, $stream){
		$name = $this->filterFilename($name);		
		$zipMethod  = static::METHOD_STORE;
        $headerLength = $this->addFileHeader($name,$zipMethod);

        fseek($stream, 0, SEEK_END);
		$zipLength = $fileLength = ftell($stream);	
		rewind($stream);
		$hashCtx = hash_init('crc32b');
		while (!feof($stream)) {
			$data = fread($stream, 1048576);
			hash_update($hashCtx, $data);
			$this->send($data);
		}
		$crc = hexdec(hash_final($hashCtx));
		$this->addFileFooter($name,$zipMethod, $crc, $zipLength, $fileLength, $headerLength);
	}
	
	public function finish(){
		foreach ($this->files as $file){
            $this->addCdrFile($file);
        } 
        $this->addCdr64Eof();
        $this->addCdr64Locator();
		$this->addCdrEof();
		$this->clear();
	}

	/**
	 * Create and send zip header for this file.
	 *
	 * @param String  $name
	 * @param Integer $zipMethod
	 * @return void
	 */
	protected function addFileHeader($name,$zipMethod){
		$name = preg_replace('/^\\/+/', '', $name);
		$nlen = strlen($name);
		$time   = $this->dosTime(time());
        $fields = array(// v=2byte,V=4byte,P=8byte
            array('V', static::FILE_HEADER_SIGNATURE),
            array('v', static::ZIP_VERSION_64),			// 压缩版本
            array('v', 0b00001000),						// General purpose bit flags - data descriptor flag set
            array('v', $zipMethod),						// Compression method
            array('V', $time),							// Timestamp (DOS Format)  time=2byte/date/2byte
            array('V', 0x00000000),						// CRC32 of data (0 -> moved to data descriptor footer)
            array('V', 0xFFFFFFFF),						// zip64时全1
            array('V', 0xFFFFFFFF),						// Length of original data (Forced to 0xFFFFFFFF for 64bit extension)
            array('v', $nlen),							// Length of filename
            array('v', 32),								// Extra data (32 bytes)
        );	
        $fields64 = array(// 32Byte;
            array('v', 0x0001),							// 64Bit Extension
            array('v', 28),								// 28bytes of data follows 
            array('P', 0x0000000000000000),				// Length of original data (0 -> moved to data descriptor footer)
            array('P', 0x0000000000000000),				// Length of compressed data (0 -> moved to data descriptor footer)
            array('P', 0x0000000000000000),				// Relative Header Offset
            array('V', 0x00000000)						// Disk number
        );
		$header = $this->packFields($fields);
		$header64 = $this->packFields($fields64);
		$this->send($header . $name . $header64);
		return strlen($header) + $nlen + strlen($header64);
	}

	/**
	 * Create and send data descriptor footer for this file.
	 */	
	protected function addFileFooter($name,$zipMethod, $crc, $zipLength, $fileLength, $headerLength){
        $fields = array(
            array('V', static::DATA_DESCRIPTOR_SIGNATURE),
            array('V', $crc),							// CRC32
            array('P', $zipLength),						// 压缩后大小
            array('P', $fileLength),					// 原始大小
        );

		$footer = $this->packFields($fields);		
		$this->send($footer);
		$totalLength = $headerLength + $zipLength + $flen;		
		$this->addToCdr($name,$zipMethod, $crc, $zipLength, $fileLength, $totalLength);		
	}
   
	/**
	 * Save file attributes for trailing CDR record.
	 *
	 * @param String  $name
	 * @param Integer $zipMethod
	 * @param string  $crc
	 * @param Integer $zipLength
	 * @param Integer $len
	 * @param Integer $rec_len
	 * @return void
	 * @return void
	 */
	private function addToCdr($name,$zipMethod, $crc, $zipLength, $len, $rec_len) {
		$this->files[] = array(
			$name,
			$zipMethod,
			$crc,
			$zipLength,
			$len,
			$this->ofs
		);
		$this->ofs += $rec_len;
	}
	
	/**
	 * Send CDR record for specified file.
	 */
	protected function addCdrFile($args){
		list($name,$zipMethod, $crc, $zipLength, $len, $offset) = $args;
		$comment = '';
		$time = $this->dosTime(time());		
        $fields = array(
            array('V', static::CDR_FILE_SIGNATURE),		// Central file header signature
            array('v', static::ZIP_VERSION_64),			// Made by version
            array('v', static::ZIP_VERSION_64),			// Extract by version
            array('v', 0b00001000),						// General purpose bit flags - data descriptor flag set
            array('v', $zipMethod),						// Compression method
            array('V', $time),							// Timestamp (DOS Format)
            array('V', $crc),							// CRC32
            array('V', 0xFFFFFFFF),						// Compressed Data Length (Forced to 0xFFFFFFFF for 64bit Extension)
            array('V', 0xFFFFFFFF),						// Original Data Length (Forced to 0xFFFFFFFF for 64bit Extension)
            array('v', strlen($name)),					// Length of filename
            array('v', 32),								// Extra data len (32bytes of 64bit Extension)
            array('v', strlen($comment)),				// Length of comment
            array('v', 0),								// Disk number
            array('v', 0),								// Internal File Attributes
            array('V', 32),								// External File Attributes
            array('V', 0xFFFFFFFF)						// Relative offset of local header (Forced to 0xFFFFFFFF for 64bit Extension)
        );			
        $fields64 = array(
            array('v', 0x0001),							// 64Bit Extension
            array('v', 28),								// 28bytes of data follows 
            array('P', $len),							// Length of original data (0 -> moved to data descriptor footer)
            array('P', $zipLength),						// Length of compressed data (0 -> moved to data descriptor footer)
            array('P', $offset),						// Relative Header Offset
            array('V', 0)								// Disk number
        );
		$header = $this->packFields($fields);
		$footer = $this->packFields($fields64);
		
		$ret = $header . $name . $comment . $footer;
		$this->send($ret);
		$this->cdr_ofs += strlen($ret);
	}
	
	/**
	 * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
	 */
	protected function addCdr64Eof(){
		$num     = count($this->files);
		$cdrLength = $this->cdr_ofs;
		$cdrOffset = $this->ofs;
		
		$fields = array(
			array('V', static::ZIP64_CDR_EOF_SIGNATURE), 	// ZIP64 end of central file header signature
			array('P', 44),									// Length of data below this header (length of block - 12) = 44
			array('v', static::ZIP_VERSION_64),				// Made by version
			array('v', static::ZIP_VERSION_64),				// Extract by version
			array('V', 0x00), 								// disk number
			array('V', 0x00), 								// no of disks
			array('P', $num),								// no of entries on disk
			array('P', $num),								// no of entries in cdr
			array('P', $cdrLength),							// CDR size
			array('P', $cdrOffset),							// CDR offset
		);
		$ret = $this->packFields($fields);
		$this->send($ret);
	}

	/**
	 * Send ZIP64 CDR Locator (Central Directory Record Locator) record.
	 */
	protected function addCdr64Locator(){
		$num     = count($this->files);
		$cdrLength = $this->cdr_ofs;
		$cdrOffset = $this->ofs;
		$fields = array(
			array('V', static::ZIP64_CDR_LOCATOR_SIGNATURE), // ZIP64 end of central file header signature
			array('V', 0x00),								// Disc number containing CDR64EOF
			array('P', $cdrOffset + $cdrLength),			// CDR offset
			array('V', 1),									// Total number of disks
		);		
		$ret = $this->packFields($fields);
		$this->send($ret);
	}

	/**
	 * Send CDR EOF (Central Directory Record End-of-File) record.
	 */
	protected function addCdrEof(){
		$num     = count($this->files);
		$cdrLength = $this->cdr_ofs;
		$cdrOffset = $this->ofs;
		$comment = '';
        $fields = array(
            array('V', static::CDR_EOF_SIGNATURE), 	// end of central file header signature
            array('v', 0x00), 						// disk number
            array('v', 0x00), 						// no of disks
            array('v', $num),						// no of entries on disk
            array('v', $num),						// no of entries in cdr
            array('V', 0xFFFFFFFF),					// CDR size (Force to 0xFFFFFFFF for Zip64)
            array('V', 0xFFFFFFFF),					// CDR offset (Force to 0xFFFFFFFF for Zip64)
            array('v', strlen($comment)),			// Zip Comment size
        );
		$ret = $this->packFields($fields) . $comment;
		$this->send($ret);
	}
	
	/**
	 * Add CDR (Central Directory Record) footer.
	 */
	protected function addCdr(){
		foreach ($this->files as $file){
            $this->addCdrFile($file);
        }
		$this->addCdrEof();
	}
	
	protected function clear(){
		$this->files   = array();
		$this->ofs     = 0;
		$this->cdr_ofs = 0;
	}

	protected function sendHttpHeaders(){
		$disposition = 'attachment';
		if ($this->outputName) {
			$safeOutput = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->outputName));
            $urlencoded = rawurlencode($safeOutput);
			$disposition .= "; filename*=utf-8''{$urlencoded}";
		}		
		$headers = array(
			'Content-Type'              => 'application/x-zip',
			'Content-Disposition'       => $disposition,
			'Pragma'                    => 'public',
			'Cache-Control'             => 'public, must-revalidate',
			'Content-Transfer-Encoding' => 'binary'
        );
        foreach ($headers as $key => $value) {
            header($key.': '.$value);
        }
	}
	
	/**
	 * Send string, sending HTTP headers if necessary.
	 */
	protected function send($str){
		if ($this->needHeaders) {
			$this->sendHttpHeaders();
		}
		$this->needHeaders = false;		
		fwrite($this->outputStream, $str);
	}
	
	/**
	 * 转换时间戳为dos时间
	 */
	protected final function dosTime($when){
		$d = getdate($when);
		if ($d['year'] < 1980) {
			$d = array(
				'year'      => 1980,
				'mon'       => 1,
				'mday'      => 1,
				'hours'     => 0,
				'minutes'   => 0,
				'seconds'   => 0
			);
		}
		$d['year'] -= 1980;
		return ($d['year'] << 25)  | ($d['mon'] << 21) | ($d['mday'] << 16) | 
			   ($d['hours'] << 11) | ($d['minutes'] << 5) | ($d['seconds'] >> 1);
	}
	protected function packFields($fields){
		$fmt = '';
		$args = array();
		foreach ($fields as $field) {
			$fmt .= $field[0];
			$args[] = $field[1];
		}
		array_unshift($args, $fmt);
		return call_user_func_array('pack', $args);
	}
	protected function filterFilename($filename){
		return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
	}
}
ZipStream.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ZipStream.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class ZipStream{
	
}

kodDiff.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/kodDiff.class.php'
View Content
<?php

class kodDiff{
	// 生成差异对象
    public static function diffMake($objFrom,$objTo,$objLike){
        if(self::isArray($objLike)){
            return self::diffArray($objFrom,$objTo,$objLike[0]);
        }elseif(self::isObject($objLike)){
            return self::diffObject($objFrom,$objTo,$objLike);
        }
        return false;
    }
    // 应用差异对象
    public static function diffApply($objFrom,$diff,$objLike){
        if(!$diff){return $objFrom;}
        if(self::isArray($objLike)){
            return self::applyArray($objFrom,$diff,$objLike[0]);
        }elseif(self::isObject($objLike)){
            return self::applyObject($objFrom,$diff,$objLike);
        }
        return $objFrom;
    }
	
	
	
    // 对象差异比较
    private static function diffObject($objFrom,$objTo,$objStruct){
        $diff = array();
        $objStruct = self::isObject($objStruct) ? $objStruct : false;
        if(self::isEqual($objFrom,$objTo)){return false;}
        self::compareObj($objFrom,$objTo,true,$diff,$objStruct);
        self::compareObj($objTo,$objFrom,false,$diff,$objStruct);
        return $diff;
    }
	private static function compareObj($obj1,$obj2,$isFromTo, &$diff,$objStruct){
		$obj1 = is_array($obj1) ? $obj1 : array();
		$obj2 = is_array($obj2) ? $obj2 : array();
		foreach($obj1 as $k=>$v){
			$objStructSub = _get($objStruct,$k,false);
			$subFrom = $isFromTo ? $v:_get($obj2,$k,array()); $subTo = $isFromTo ? _get($obj2,$k,array()):$v;
			
			// 都为空情况处理; $objFrom包含key且为空,同时$objTo不包含key时处理清空key;  $objTo包含key且为空,同时$objFrom不包含key时保留;
			if(!$subFrom && !$subTo && is_array($subFrom) && is_array($subTo)){
				if(!$isFromTo && !isset($obj2[$k])){$diff[$k] = array('type'=>'diff','val'=>array());}
				if($isFromTo  && !isset($obj2[$k])){$diff[$k] = array('type'=>'diff','val'=>array(),'_clearAll'=>true);}
				continue;
			}
			if(self::isEqual($subFrom,$subTo)){continue;}
			if(self::isArray($objStructSub)){
				$diffNow  = self::diffArray($subFrom,$subTo,$objStructSub[0]);
				if($diffNow){$diff[$k] = array('type'=>'diffArr','val'=>$diffNow);}
				if($isFromTo && $diffNow && !isset($obj2[$k])){$diff[$k]['_clearAll'] = true;}
			}elseif(self::isObject($objStructSub) || self::allowMergeObj($subFrom,$subTo)){
				$diffNow  = self::diffObject($subFrom,$subTo,$objStructSub);
				if($diffNow){$diff[$k] = array('type'=>'diff','val'=>$diffNow);}
				if($isFromTo && $diffNow && !isset($obj2[$k])){$diff[$k]['_clearAll'] = true;}
			}else{
				if(!isset($obj2[$k])){
					$diff[$k] = $isFromTo ? array('type'=>'remove') : array('type'=>'edit', 'val'=>$v);
					continue;
				}
				$diff[$k] = array('type'=>'edit', 'val'=>$v);
			}
		}		
	}
	
    // 数组差异比较
    private static function diffArray($arrFrom,$arrTo,$objStruct){
		$arrFrom = self::isArray($arrFrom) ? $arrFrom : array();
        $arrTo   = self::isArray($arrTo) ? $arrTo : array();
		if(self::isEqual($arrFrom,$arrTo)){return false;} // 相等则忽略;
        if(!self::isObject($objStruct) || (empty($arrFrom) && empty($arrTo))){return false;}
		
		$idKey = _get($objStruct,'_idKey_','id');
        $diff  = array('add'=>array(), 'remove'=>array(), 'edit'=>array(), 'sort'=>array('isChange'=>false, 'idArr'=>array()));
		$arrFromMap = array();$arrFromSort = array();$arrToMap = array();$arrToSort = array();$lastID = '';
		foreach($arrFrom as $v){
			if(!is_array($v)){continue;}
			$id = isset($v[$idKey]) ? $v[$idKey].'' : '';
			if($id === ''){continue;}
			$arrFromMap[$id] = $v;$arrFromSort[] = $id;
		}
		
		// 创建 arrTo 的映射和排序数组, 并处理新增和变更的项
		foreach($arrTo as $v){
			if(!is_array($v)){continue;}
			$id = isset($v[$idKey]) ? $v[$idKey].'' : '';
			if($id === '' || !isset($arrFromMap[$id])){// id 为空,或来源不存在时为新增项
				$diff['add'][] = array('beforeID' => $lastID,'val' => $v);
				if($id === ''){continue;}
			}
			if(isset($arrFromMap[$id])){
				$diffNow = self::diffObject($arrFromMap[$id], $v, $objStruct);
				if($diffNow){$diff['edit'][$id] = $diffNow;}
			}
			$arrToMap[$id] = $v;$arrToSort[] = $id;$lastID = $id;
		}
		foreach($arrFromMap as $id => $v){
			if(!isset($arrToMap[$id])){$diff['remove'][] = $id;}
		}
		// 检查排序是否有变化
		if($arrFromSort !== $arrToSort){
			$diff['sort'] = array('isChange' => true,'idArr' => $arrToSort);
		}
		return $diff;
    }
	
    // 应用对象差异
    private static function applyObject($obj,$diff,$objStruct){
        $newObj = $obj;
        foreach($diff as $key=>$change){
            switch ($change['type']){
                case 'edit':$newObj[$key] = $change['val'];break;
                case 'remove':unset($newObj[$key]);break;
                case 'diff':
                    $newObj[$key] = self::applyObject(_get($newObj,$key,array()),$change['val'],_get($objStruct,$key,array()));
                    if(isset($change['_clearAll']) && empty($newObj[$key])){unset($newObj[$key]);}
                    break;
                case 'diffArr':
                    $newObj[$key] = self::applyArray(_get($newObj,$key,array()),$change['val'],_get($objStruct,$key.'.0',array()));
					if(isset($change['_clearAll']) && empty($newObj[$key])){unset($newObj[$key]);}
                    break;
				default:break;
            }
        }
        return $newObj;
    }
    // 应用数组差异
    private static function applyArray($arr,$diff,$objStruct){
        $newArr = $arr;$arrMap = array();$arrSort = array();
        $arrResultID = array();$arrResult = array();
        $idKey = self::isObject($objStruct) ? _get($objStruct,'_idKey_','id'):'';
        if(!$diff){return $newArr;}

        foreach($newArr as $i=>$item){
            if(!is_array($item)){continue;}
            $id = isset($item[$idKey]) ? $item[$idKey].'' : '';
            if(!$id){continue;}
            if(in_array($id,$diff['remove'])){
                $newArr[$i] = false;
                continue;
            }
            if(isset($diff['edit'][$id])){
                $newArr[$i] = self::applyObject($newArr[$i],$diff['edit'][$id],$objStruct);
            }
            $arrMap[$id] = $newArr[$i];
            $arrSort[] = $id;
        }

        foreach($diff['add'] as $addItem){
            if(!self::isObject($addItem['val'])){continue;}
            $id = isset($addItem['val'][$idKey]) ? $addItem['val'][$idKey].'' : '';
            if($id && !isset($arrMap[$id])){
                $arrMap[$id] = $addItem['val'];
            }
        }
		
        $hasPushedID = array();
        $arrSortID = $diff['sort']['isChange'] ? $diff['sort']['idArr'] : $arrSort;
        foreach($arrSortID as $id){
            if(isset($arrMap[$id])){
                $arrResultID[] = $id;
                $hasPushedID[$id] = true;
            }
        }
        foreach($arrMap as $id=>$v){
            if(!isset($hasPushedID[$id])){$arrResultID[] = $id;}
        }

        $hasAdd = array();
        self::pushAdd('',$diff,$arrResult,$hasAdd,$idKey);
        foreach($arrResultID as $id){
            if(isset($arrMap[$id]) && !isset($hasAdd[$id])){
                $arrResult[] = $arrMap[$id];
            }
            self::pushAdd($id,$diff,$arrResult,$hasAdd,$idKey);
        }
		
		$autoIDType = _get($objStruct,'_autoID_');  
		if($idKey && $autoIDType){
			self::arrayAutoID($arrResult,$idKey,$autoIDType);
		}
        return $arrResult;
    }
	
	private static function pushAdd($beforeID,$diff,&$arrResult,&$hasAdd,$idKey){
		foreach($diff['add'] as $addItem){
			if(!$addItem || !self::isObject($addItem['val'])){continue;}
			if($addItem['beforeID'] != $beforeID){continue;}
			$id = isset($addItem['val'][$idKey]) ? $addItem['val'][$idKey].'' : '';
			if($id && isset($hasAdd[$id])){continue;}
			$arrResult[] = $addItem['val'];$hasAdd[$id] = true;
		}
	}
	
	public static function isEqual($a, $b){
        if(gettype($a) !== gettype($b)){return false;}
        if(is_array($a)){
            if(count($a) !== count($b)){return false;}
            foreach($a as $k => $v){
                if(!array_key_exists($k, $b) || !self::isEqual($v, $b[$k])) return false;
            }
            return true;
        }
        return $a === $b;
    }
    private static function isObject($v){
		return is_array($v) && (!isset($v[0]) && count($v) > 0);
    }
    private static function isArray($v){
		return is_array($v) && (isset($v[0]) || count($v) == 0);
    }
    private static function allowMergeObj($a,$b){
        return self::isObject($a) || self::isObject($b);
    }
	
	
	// 构造id; 区分数组中唯一id;
	public static function makeID($idArr,$type='string'){
		if($type != 'string'){
			$maxID = 1;
			foreach($idArr as $id){
				$maxID = max($maxID,intval($id));
			}
			return ($maxID + 1).'';
		}
		
		$loop = 1;
		while($loop++ <= 500){
			$uid = strtolower(substr(md5(rand_string(30).time()),0,6));
			if(!$idArr || !in_array($uid,$idArr)){return $uid;}
		}
		return rand_string(6);
	}
	public static function arrayAutoID(&$arr,$idKey = 'id',$type='number'){
		$idArr = array_to_keyvalue($arr,'',$idKey);
		foreach($arr as $i=>$v){
			if($arr[$i][$idKey]){continue;}
			$id = self::makeID($idArr,$type);$idArr[] = $id;
			$arr[$i][$idKey] = $id;
		}
	}
}

/*
示例使用
$a = array(
    'user' => array('userID' => '1', 'name' => 'admin', 'x' => '1'),'a' => array(2, 3, 4),'pose2' => array('x' => 2, 'y' => 3),'pose3' => array(),
	'menu1' => array(),
    'menu2' => array(
        array('id' => 1, 'name' => 'a1'),
        array('id' => 2, 'name' => 'a2', 'op' => array('a' => 1, 'b' => 2))
    ),
    'menu3' => array(
        array('id' => 1, 'name' => 'a1'),
        array('id' => 2, 'name' => 'a2', 'op' => array('a' => 1, 'b' => array('x' => 1, 'y' => 2)))
    )
);
$b = array(
    'user' => array('userID' => '2', 'name' => 'admin', 'y' => '2'),'a' => array(3, 5),'pose2' => array(),'pose4' => array(),
    'menu1' => array(
        array('id' => 2, 'name' => 'a2'),
        array('id' => 1, 'name' => 'a1')
    ),
    'menu2' => array(
        array('id' => 2, 'name' => 'a26', 'x' => 3),
        array('id' => 1, 'name' => 'a1')
    ),
    'menu3' => array(
        array('name' => 'a1', 'id' => 1),
        array('id' => 13, 'name' => 'a12'),
        array('id' => 2, 'name' => 'a2', 'op' => array('a' => 1, 'b' => array()))
    )
);
$like = array(
    'menu1' => array(array('_idKey_' => 'id')),
    'menu2' => array(array('_idKey_' => 'id')),
    'menu3' => array(array('_idKey_' => 'id'))
);
// 计算差异
$diff   = kodDiff::diffMake($a, $b, $like);
$diffTo = kodDiff::diffApply($a, $like, $diff);
pr(kodDiff::isEqual($b, $diffTo),$a,$b,$diffTo,$diff);
**/