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`).
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Downloader.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/FileParsePdf.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/GetInfo.class.php'
<?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;
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Hook.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/HttpAuth.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/HttpHeader.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/I18n.class.php'
<?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;
}
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ImageGdBMP.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ImageThumb.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Input.class.php'
<?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];
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodArchive.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodLog.class.php'
<?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(' ',' ',$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(' ',$charCount - $charFinished).']';
$logIndex = str_repeat(' ',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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodSort.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/KodUser.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Mcrypt.class.php'
<?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;
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Services_JSON.class.php'
<?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){
}
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/Uploader.class.php'
<?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];
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ZipMake.class.php'
<?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);
}
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/ZipStream.class.php'
<?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{
}
wget 'https://sme10.lists2.roe3.org/kodbox/app/kod/kodDiff.class.php'
<?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);
**/