1. 常用公共方法
1 <?php 2 // +---------------------------------------------------------------------- 3 // | OneThink [ WE CAN DO IT JUST THINK IT ] 4 // +---------------------------------------------------------------------- 5 // | Copyright (c) 2013 http://www.onethink.cn All rights reserved. 6 // +---------------------------------------------------------------------- 7 // | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn> 8 // +---------------------------------------------------------------------- 9 10 // OneThink常量定义 11 const ONETHINK_VERSION = '1.1.141101'; 12 const ONETHINK_ADDON_PATH = './Addons/'; 13 14 /** 15 * 系统公共库文件 16 * 主要定义系统公共函数库 17 */ 18 19 /** 20 * 检测用户是否登录 21 * @return integer 0-未登录,大于0-当前登录用户ID 22 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 23 */ 24 function is_login(){ 25 $user = session('user_auth'); 26 if (empty($user)) { 27 return 0; 28 } else { 29 return session('user_auth_sign') == data_auth_sign($user) ? $user['uid'] : 0; 30 } 31 } 32 33 /** 34 * 检测当前用户是否为管理员 35 * @return boolean true-管理员,false-非管理员 36 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 37 */ 38 function is_administrator($uid = null){ 39 $uid = is_null($uid) ? is_login() : $uid; 40 return $uid && (intval($uid) === C('USER_ADMINISTRATOR')); 41 } 42 43 /** 44 * 字符串转换为数组,主要用于把分隔符调整到第二个参数 45 * @param string $str 要分割的字符串 46 * @param string $glue 分割符 47 * @return array 48 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 49 */ 50 function str2arr($str, $glue = ','){ 51 return explode($glue, $str); 52 } 53 54 /** 55 * 数组转换为字符串,主要用于把分隔符调整到第二个参数 56 * @param array $arr 要连接的数组 57 * @param string $glue 分割符 58 * @return string 59 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 60 */ 61 function arr2str($arr, $glue = ','){ 62 return implode($glue, $arr); 63 } 64 65 /** 66 * 字符串截取,支持中文和其他编码 67 * @static 68 * @access public 69 * @param string $str 需要转换的字符串 70 * @param string $start 开始位置 71 * @param string $length 截取长度 72 * @param string $charset 编码格式 73 * @param string $suffix 截断显示字符 74 * @return string 75 */ 76 function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true) { 77 if(function_exists("mb_substr")) 78 $slice = mb_substr($str, $start, $length, $charset); 79 elseif(function_exists('iconv_substr')) { 80 $slice = iconv_substr($str,$start,$length,$charset); 81 if(false === $slice) { 82 $slice = ''; 83 } 84 }else{ 85 $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/"; 86 $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/"; 87 $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/"; 88 $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/"; 89 preg_match_all($re[$charset], $str, $match); 90 $slice = join("",array_slice($match[0], $start, $length)); 91 } 92 return $suffix ? $slice.'...' : $slice; 93 } 94 95 /** 96 * 系统加密方法 97 * @param string $data 要加密的字符串 98 * @param string $key 加密密钥 99 * @param int $expire 过期时间 单位 秒 100 * @return string 101 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 102 */ 103 function think_encrypt($data, $key = '', $expire = 0) { 104 $key = md5(empty($key) ? C('DATA_AUTH_KEY') : $key); 105 $data = base64_encode($data); 106 $x = 0; 107 $len = strlen($data); 108 $l = strlen($key); 109 $char = ''; 110 111 for ($i = 0; $i < $len; $i++) { 112 if ($x == $l) $x = 0; 113 $char .= substr($key, $x, 1); 114 $x++; 115 } 116 117 $str = sprintf('%010d', $expire ? $expire + time():0); 118 119 for ($i = 0; $i < $len; $i++) { 120 $str .= chr(ord(substr($data, $i, 1)) + (ord(substr($char, $i, 1)))%256); 121 } 122 return str_replace(array('+','/','='),array('-','_',''),base64_encode($str)); 123 } 124 125 /** 126 * 系统解密方法 127 * @param string $data 要解密的字符串 (必须是think_encrypt方法加密的字符串) 128 * @param string $key 加密密钥 129 * @return string 130 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 131 */ 132 function think_decrypt($data, $key = ''){ 133 $key = md5(empty($key) ? C('DATA_AUTH_KEY') : $key); 134 $data = str_replace(array('-','_'),array('+','/'),$data); 135 $mod4 = strlen($data) % 4; 136 if ($mod4) { 137 $data .= substr('====', $mod4); 138 } 139 $data = base64_decode($data); 140 $expire = substr($data,0,10); 141 $data = substr($data,10); 142 143 if($expire > 0 && $expire < time()) { 144 return ''; 145 } 146 $x = 0; 147 $len = strlen($data); 148 $l = strlen($key); 149 $char = $str = ''; 150 151 for ($i = 0; $i < $len; $i++) { 152 if ($x == $l) $x = 0; 153 $char .= substr($key, $x, 1); 154 $x++; 155 } 156 157 for ($i = 0; $i < $len; $i++) { 158 if (ord(substr($data, $i, 1))<ord(substr($char, $i, 1))) { 159 $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1))); 160 }else{ 161 $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1))); 162 } 163 } 164 return base64_decode($str); 165 } 166 167 /** 168 * 数据签名认证 169 * @param array $data 被认证的数据 170 * @return string 签名 171 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 172 */ 173 function data_auth_sign($data) { 174 //数据类型检测 175 if(!is_array($data)){ 176 $data = (array)$data; 177 } 178 ksort($data); //排序 179 $code = http_build_query($data); //url编码并生成query字符串 180 $sign = sha1($code); //生成签名 181 return $sign; 182 } 183 184 /** 185 * 对查询结果集进行排序 186 * @access public 187 * @param array $list 查询结果 188 * @param string $field 排序的字段名 189 * @param array $sortby 排序类型 190 * asc正向排序 desc逆向排序 nat自然排序 191 * @return array 192 */ 193 function list_sort_by($list,$field, $sortby='asc') { 194 if(is_array($list)){ 195 $refer = $resultSet = array(); 196 foreach ($list as $i => $data) 197 $refer[$i] = &$data[$field]; 198 switch ($sortby) { 199 case 'asc': // 正向排序 200 asort($refer); 201 break; 202 case 'desc':// 逆向排序 203 arsort($refer); 204 break; 205 case 'nat': // 自然排序 206 natcasesort($refer); 207 break; 208 } 209 foreach ( $refer as $key=> $val) 210 $resultSet[] = &$list[$key]; 211 return $resultSet; 212 } 213 return false; 214 } 215 216 /** 217 * 把返回的数据集转换成Tree 218 * @param array $list 要转换的数据集 219 * @param string $pid parent标记字段 220 * @param string $level level标记字段 221 * @return array 222 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 223 */ 224 function list_to_tree($list, $pk='id', $pid = 'pid', $child = '_child', $root = 0) { 225 // 创建Tree 226 $tree = array(); 227 if(is_array($list)) { 228 // 创建基于主键的数组引用 229 $refer = array(); 230 foreach ($list as $key => $data) { 231 $refer[$data[$pk]] =& $list[$key]; 232 } 233 foreach ($list as $key => $data) { 234 // 判断是否存在parent 235 $parentId = $data[$pid]; 236 if ($root == $parentId) { 237 $tree[] =& $list[$key]; 238 }else{ 239 if (isset($refer[$parentId])) { 240 $parent =& $refer[$parentId]; 241 $parent[$child][] =& $list[$key]; 242 } 243 } 244 } 245 } 246 return $tree; 247 } 248 249 /** 250 * 将list_to_tree的树还原成列表 251 * @param array $tree 原来的树 252 * @param string $child 孩子节点的键 253 * @param string $order 排序显示的键,一般是主键 升序排列 254 * @param array $list 过渡用的中间数组, 255 * @return array 返回排过序的列表数组 256 * @author yangweijie <yangweijiester@gmail.com> 257 */ 258 function tree_to_list($tree, $child = '_child', $order='id', &$list = array()){ 259 if(is_array($tree)) { 260 foreach ($tree as $key => $value) { 261 $reffer = $value; 262 if(isset($reffer[$child])){ 263 unset($reffer[$child]); 264 tree_to_list($value[$child], $child, $order, $list); 265 } 266 $list[] = $reffer; 267 } 268 $list = list_sort_by($list, $order, $sortby='asc'); 269 } 270 return $list; 271 } 272 273 /** 274 * 格式化字节大小 275 * @param number $size 字节数 276 * @param string $delimiter 数字和单位分隔符 277 * @return string 格式化后的带单位的大小 278 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 279 */ 280 function format_bytes($size, $delimiter = '') { 281 $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB'); 282 for ($i = 0; $size >= 1024 && $i < 5; $i++) $size /= 1024; 283 return round($size, 2) . $delimiter . $units[$i]; 284 } 285 286 /** 287 * 设置跳转页面URL 288 * 使用函数再次封装,方便以后选择不同的存储方式(目前使用cookie存储) 289 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 290 */ 291 function set_redirect_url($url){ 292 cookie('redirect_url', $url); 293 } 294 295 /** 296 * 获取跳转页面URL 297 * @return string 跳转页URL 298 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 299 */ 300 function get_redirect_url(){ 301 $url = cookie('redirect_url'); 302 return empty($url) ? __APP__ : $url; 303 } 304 305 /** 306 * 处理插件钩子 307 * @param string $hook 钩子名称 308 * @param mixed $params 传入参数 309 * @return void 310 */ 311 function hook($hook,$params=array()){ 312 \Think\Hook::listen($hook,$params); 313 } 314 315 /** 316 * 获取插件类的类名 317 * @param strng $name 插件名 318 */ 319 function get_addon_class($name){ 320 $class = "Addons\\{$name}\\{$name}Addon"; 321 return $class; 322 } 323 324 /** 325 * 获取插件类的配置文件数组 326 * @param string $name 插件名 327 */ 328 function get_addon_config($name){ 329 $class = get_addon_class($name); 330 if(class_exists($class)) { 331 $addon = new $class(); 332 return $addon->getConfig(); 333 }else { 334 return array(); 335 } 336 } 337 338 /** 339 * 插件显示内容里生成访问插件的url 340 * @param string $url url 341 * @param array $param 参数 342 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 343 */ 344 function addons_url($url, $param = array()){ 345 $url = parse_url($url); 346 $case = C('URL_CASE_INSENSITIVE'); 347 $addons = $case ? parse_name($url['scheme']) : $url['scheme']; 348 $controller = $case ? parse_name($url['host']) : $url['host']; 349 $action = trim($case ? strtolower($url['path']) : $url['path'], '/'); 350 351 /* 解析URL带的参数 */ 352 if(isset($url['query'])){ 353 parse_str($url['query'], $query); 354 $param = array_merge($query, $param); 355 } 356 357 /* 基础参数 */ 358 $params = array( 359 '_addons' => $addons, 360 '_controller' => $controller, 361 '_action' => $action, 362 ); 363 $params = array_merge($params, $param); //添加额外参数 364 365 return U('Addons/execute', $params); 366 } 367 368 /** 369 * 时间戳格式化 370 * @param int $time 371 * @return string 完整的时间显示 372 * @author huajie <banhuajie@163.com> 373 */ 374 function time_format($time = NULL,$format='Y-m-d H:i'){ 375 $time = $time === NULL ? NOW_TIME : intval($time); 376 return date($format, $time); 377 } 378 379 /** 380 * 根据用户ID获取用户名 381 * @param integer $uid 用户ID 382 * @return string 用户名 383 */ 384 function get_username($uid = 0){ 385 static $list; 386 if(!($uid && is_numeric($uid))){ //获取当前登录用户名 387 return session('user_auth.username'); 388 } 389 390 /* 获取缓存数据 */ 391 if(empty($list)){ 392 $list = S('sys_active_user_list'); 393 } 394 395 /* 查找用户信息 */ 396 $key = "u{$uid}"; 397 if(isset($list[$key])){ //已缓存,直接使用 398 $name = $list[$key]; 399 } else { //调用接口获取用户信息 400 $User = new User\Api\UserApi(); 401 $info = $User->info($uid); 402 if($info && isset($info[1])){ 403 $name = $list[$key] = $info[1]; 404 /* 缓存用户 */ 405 $count = count($list); 406 $max = C('USER_MAX_CACHE'); 407 while ($count-- > $max) { 408 array_shift($list); 409 } 410 S('sys_active_user_list', $list); 411 } else { 412 $name = ''; 413 } 414 } 415 return $name; 416 } 417 418 /** 419 * 根据用户ID获取用户昵称 420 * @param integer $uid 用户ID 421 * @return string 用户昵称 422 */ 423 function get_nickname($uid = 0){ 424 static $list; 425 if(!($uid && is_numeric($uid))){ //获取当前登录用户名 426 return session('user_auth.username'); 427 } 428 429 /* 获取缓存数据 */ 430 if(empty($list)){ 431 $list = S('sys_user_nickname_list'); 432 } 433 434 /* 查找用户信息 */ 435 $key = "u{$uid}"; 436 if(isset($list[$key])){ //已缓存,直接使用 437 $name = $list[$key]; 438 } else { //调用接口获取用户信息 439 $info = M('Member')->field('nickname')->find($uid); 440 if($info !== false && $info['nickname'] ){ 441 $nickname = $info['nickname']; 442 $name = $list[$key] = $nickname; 443 /* 缓存用户 */ 444 $count = count($list); 445 $max = C('USER_MAX_CACHE'); 446 while ($count-- > $max) { 447 array_shift($list); 448 } 449 S('sys_user_nickname_list', $list); 450 } else { 451 $name = ''; 452 } 453 } 454 return $name; 455 } 456 457 /** 458 * 获取分类信息并缓存分类 459 * @param integer $id 分类ID 460 * @param string $field 要获取的字段名 461 * @return string 分类信息 462 */ 463 function get_category($id, $field = null){ 464 static $list; 465 466 /* 非法分类ID */ 467 if(empty($id) || !is_numeric($id)){ 468 return ''; 469 } 470 471 /* 读取缓存数据 */ 472 if(empty($list)){ 473 $list = S('sys_category_list'); 474 } 475 476 /* 获取分类名称 */ 477 if(!isset($list[$id])){ 478 $cate = M('Category')->find($id); 479 if(!$cate || 1 != $cate['status']){ //不存在分类,或分类被禁用 480 return ''; 481 } 482 $list[$id] = $cate; 483 S('sys_category_list', $list); //更新缓存 484 } 485 return is_null($field) ? $list[$id] : $list[$id][$field]; 486 } 487 488 /* 根据ID获取分类标识 */ 489 function get_category_name($id){ 490 return get_category($id, 'name'); 491 } 492 493 /* 根据ID获取分类名称 */ 494 function get_category_title($id){ 495 return get_category($id, 'title'); 496 } 497 498 /** 499 * 获取顶级模型信息 500 */ 501 function get_top_model($model_id=null){ 502 $map = array('status' => 1, 'extend' => 0); 503 if(!is_null($model_id)){ 504 $map['id'] = array('neq',$model_id); 505 } 506 $model = M('Model')->where($map)->field(true)->select(); 507 foreach ($model as $value) { 508 $list[$value['id']] = $value; 509 } 510 return $list; 511 } 512 513 /** 514 * 获取文档模型信息 515 * @param integer $id 模型ID 516 * @param string $field 模型字段 517 * @return array 518 */ 519 function get_document_model($id = null, $field = null){ 520 static $list; 521 522 /* 非法分类ID */ 523 if(!(is_numeric($id) || is_null($id))){ 524 return ''; 525 } 526 527 /* 读取缓存数据 */ 528 if(empty($list)){ 529 $list = S('DOCUMENT_MODEL_LIST'); 530 } 531 532 /* 获取模型名称 */ 533 if(empty($list)){ 534 $map = array('status' => 1, 'extend' => 1); 535 $model = M('Model')->where($map)->field(true)->select(); 536 foreach ($model as $value) { 537 $list[$value['id']] = $value; 538 } 539 S('DOCUMENT_MODEL_LIST', $list); //更新缓存 540 } 541 542 /* 根据条件返回数据 */ 543 if(is_null($id)){ 544 return $list; 545 } elseif(is_null($field)){ 546 return $list[$id]; 547 } else { 548 return $list[$id][$field]; 549 } 550 } 551 552 /** 553 * 解析UBB数据 554 * @param string $data UBB字符串 555 * @return string 解析为HTML的数据 556 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 557 */ 558 function ubb($data){ 559 //TODO: 待完善,目前返回原始数据 560 return $data; 561 } 562 563 /** 564 * 记录行为日志,并执行该行为的规则 565 * @param string $action 行为标识 566 * @param string $model 触发行为的模型名 567 * @param int $record_id 触发行为的记录id 568 * @param int $user_id 执行行为的用户id 569 * @return boolean 570 * @author huajie <banhuajie@163.com> 571 */ 572 function action_log($action = null, $model = null, $record_id = null, $user_id = null){ 573 574 //参数检查 575 if(empty($action) || empty($model) || empty($record_id)){ 576 return '参数不能为空'; 577 } 578 if(empty($user_id)){ 579 $user_id = is_login(); 580 } 581 582 //查询行为,判断是否执行 583 $action_info = M('Action')->getByName($action); 584 if($action_info['status'] != 1){ 585 return '该行为被禁用或删除'; 586 } 587 588 //插入行为日志 589 $data['action_id'] = $action_info['id']; 590 $data['user_id'] = $user_id; 591 $data['action_ip'] = ip2long(get_client_ip()); 592 $data['model'] = $model; 593 $data['record_id'] = $record_id; 594 $data['create_time'] = NOW_TIME; 595 596 //解析日志规则,生成日志备注 597 if(!empty($action_info['log'])){ 598 if(preg_match_all('/\[(\S+?)\]/', $action_info['log'], $match)){ 599 $log['user'] = $user_id; 600 $log['record'] = $record_id; 601 $log['model'] = $model; 602 $log['time'] = NOW_TIME; 603 $log['data'] = array('user'=>$user_id,'model'=>$model,'record'=>$record_id,'time'=>NOW_TIME); 604 foreach ($match[1] as $value){ 605 $param = explode('|', $value); 606 if(isset($param[1])){ 607 $replace[] = call_user_func($param[1],$log[$param[0]]); 608 }else{ 609 $replace[] = $log[$param[0]]; 610 } 611 } 612 $data['remark'] = str_replace($match[0], $replace, $action_info['log']); 613 }else{ 614 $data['remark'] = $action_info['log']; 615 } 616 }else{ 617 //未定义日志规则,记录操作url 618 $data['remark'] = '操作url:'.$_SERVER['REQUEST_URI']; 619 } 620 621 M('ActionLog')->add($data); 622 623 if(!empty($action_info['rule'])){ 624 //解析行为 625 $rules = parse_action($action, $user_id); 626 627 //执行行为 628 $res = execute_action($rules, $action_info['id'], $user_id); 629 } 630 } 631 632 /** 633 * 解析行为规则 634 * 规则定义 table:$table|field:$field|condition:$condition|rule:$rule[|cycle:$cycle|max:$max][;......] 635 * 规则字段解释:table->要操作的数据表,不需要加表前缀; 636 * field->要操作的字段; 637 * condition->操作的条件,目前支持字符串,默认变量{$self}为执行行为的用户 638 * rule->对字段进行的具体操作,目前支持四则混合运算,如:1+score*2/2-3 639 * cycle->执行周期,单位(小时),表示$cycle小时内最多执行$max次 640 * max->单个周期内的最大执行次数($cycle和$max必须同时定义,否则无效) 641 * 单个行为后可加 ; 连接其他规则 642 * @param string $action 行为id或者name 643 * @param int $self 替换规则里的变量为执行用户的id 644 * @return boolean|array: false解析出错 , 成功返回规则数组 645 * @author huajie <banhuajie@163.com> 646 */ 647 function parse_action($action = null, $self){ 648 if(empty($action)){ 649 return false; 650 } 651 652 //参数支持id或者name 653 if(is_numeric($action)){ 654 $map = array('id'=>$action); 655 }else{ 656 $map = array('name'=>$action); 657 } 658 659 //查询行为信息 660 $info = M('Action')->where($map)->find(); 661 if(!$info || $info['status'] != 1){ 662 return false; 663 } 664 665 //解析规则:table:$table|field:$field|condition:$condition|rule:$rule[|cycle:$cycle|max:$max][;......] 666 $rules = $info['rule']; 667 $rules = str_replace('{$self}', $self, $rules); 668 $rules = explode(';', $rules); 669 $return = array(); 670 foreach ($rules as $key=>&$rule){ 671 $rule = explode('|', $rule); 672 foreach ($rule as $k=>$fields){ 673 $field = empty($fields) ? array() : explode(':', $fields); 674 if(!empty($field)){ 675 $return[$key][$field[0]] = $field[1]; 676 } 677 } 678 //cycle(检查周期)和max(周期内最大执行次数)必须同时存在,否则去掉这两个条件 679 if(!array_key_exists('cycle', $return[$key]) || !array_key_exists('max', $return[$key])){ 680 unset($return[$key]['cycle'],$return[$key]['max']); 681 } 682 } 683 684 return $return; 685 } 686 687 /** 688 * 执行行为 689 * @param array $rules 解析后的规则数组 690 * @param int $action_id 行为id 691 * @param array $user_id 执行的用户id 692 * @return boolean false 失败 , true 成功 693 * @author huajie <banhuajie@163.com> 694 */ 695 function execute_action($rules = false, $action_id = null, $user_id = null){ 696 if(!$rules || empty($action_id) || empty($user_id)){ 697 return false; 698 } 699 700 $return = true; 701 foreach ($rules as $rule){ 702 703 //检查执行周期 704 $map = array('action_id'=>$action_id, 'user_id'=>$user_id); 705 $map['create_time'] = array('gt', NOW_TIME - intval($rule['cycle']) * 3600); 706 $exec_count = M('ActionLog')->where($map)->count(); 707 if($exec_count > $rule['max']){ 708 continue; 709 } 710 711 //执行数据库操作 712 $Model = M(ucfirst($rule['table'])); 713 $field = $rule['field']; 714 $res = $Model->where($rule['condition'])->setField($field, array('exp', $rule['rule'])); 715 716 if(!$res){ 717 $return = false; 718 } 719 } 720 return $return; 721 } 722 723 //基于数组创建目录和文件 724 function create_dir_or_files($files){ 725 foreach ($files as $key => $value) { 726 if(substr($value, -1) == '/'){ 727 mkdir($value); 728 }else{ 729 @file_put_contents($value, ''); 730 } 731 } 732 } 733 734 if(!function_exists('array_column')){ 735 function array_column(array $input, $columnKey, $indexKey = null) { 736 $result = array(); 737 if (null === $indexKey) { 738 if (null === $columnKey) { 739 $result = array_values($input); 740 } else { 741 foreach ($input as $row) { 742 $result[] = $row[$columnKey]; 743 } 744 } 745 } else { 746 if (null === $columnKey) { 747 foreach ($input as $row) { 748 $result[$row[$indexKey]] = $row; 749 } 750 } else { 751 foreach ($input as $row) { 752 $result[$row[$indexKey]] = $row[$columnKey]; 753 } 754 } 755 } 756 return $result; 757 } 758 } 759 760 /** 761 * 获取表名(不含表前缀) 762 * @param string $model_id 763 * @return string 表名 764 * @author huajie <banhuajie@163.com> 765 */ 766 function get_table_name($model_id = null){ 767 if(empty($model_id)){ 768 return false; 769 } 770 $Model = M('Model'); 771 $name = ''; 772 $info = $Model->getById($model_id); 773 if($info['extend'] != 0){ 774 $name = $Model->getFieldById($info['extend'], 'name').'_'; 775 } 776 $name .= $info['name']; 777 return $name; 778 } 779 780 /** 781 * 获取属性信息并缓存 782 * @param integer $id 属性ID 783 * @param string $field 要获取的字段名 784 * @return string 属性信息 785 */ 786 function get_model_attribute($model_id, $group = true,$fields=true){ 787 static $list; 788 789 /* 非法ID */ 790 if(empty($model_id) || !is_numeric($model_id)){ 791 return ''; 792 } 793 794 /* 获取属性 */ 795 if(!isset($list[$model_id])){ 796 $map = array('model_id'=>$model_id); 797 $extend = M('Model')->getFieldById($model_id,'extend'); 798 799 if($extend){ 800 $map = array('model_id'=> array("in", array($model_id, $extend))); 801 } 802 $info = M('Attribute')->where($map)->field($fields)->select(); 803 $list[$model_id] = $info; 804 } 805 806 $attr = array(); 807 if($group){ 808 foreach ($list[$model_id] as $value) { 809 $attr[$value['id']] = $value; 810 } 811 $model = M("Model")->field("field_sort,attribute_list,attribute_alias")->find($model_id); 812 $attribute = explode(",", $model['attribute_list']); 813 if (empty($model['field_sort'])) { //未排序 814 $group = array(1 => array_merge($attr)); 815 } else { 816 $group = json_decode($model['field_sort'], true); 817 818 $keys = array_keys($group); 819 foreach ($group as &$value) { 820 foreach ($value as $key => $val) { 821 $value[$key] = $attr[$val]; 822 unset($attr[$val]); 823 } 824 } 825 826 if (!empty($attr)) { 827 foreach ($attr as $key => $val) { 828 if (!in_array($val['id'], $attribute)) { 829 unset($attr[$key]); 830 } 831 } 832 $group[$keys[0]] = array_merge($group[$keys[0]], $attr); 833 } 834 } 835 if (!empty($model['attribute_alias'])) { 836 $alias = preg_split('/[;\r\n]+/s', $model['attribute_alias']); 837 $fields = array(); 838 foreach ($alias as &$value) { 839 $val = explode(':', $value); 840 $fields[$val[0]] = $val[1]; 841 } 842 foreach ($group as &$value) { 843 foreach ($value as $key => $val) { 844 if (!empty($fields[$val['name']])) { 845 $value[$key]['title'] = $fields[$val['name']]; 846 } 847 } 848 } 849 } 850 $attr = $group; 851 }else{ 852 foreach ($list[$model_id] as $value) { 853 $attr[$value['name']] = $value; 854 } 855 } 856 return $attr; 857 } 858 859 /** 860 * 调用系统的API接口方法(静态方法) 861 * api('User/getName','id=5'); 调用公共模块的User接口的getName方法 862 * api('Admin/User/getName','id=5'); 调用Admin模块的User接口 863 * @param string $name 格式 [模块名]/接口名/方法名 864 * @param array|string $vars 参数 865 */ 866 function api($name,$vars=array()){ 867 $array = explode('/',$name); 868 $method = array_pop($array); 869 $classname = array_pop($array); 870 $module = $array? array_pop($array) : 'Common'; 871 $callback = $module.'\\Api\\'.$classname.'Api::'.$method; 872 if(is_string($vars)) { 873 parse_str($vars,$vars); 874 } 875 return call_user_func_array($callback,$vars); 876 } 877 878 /** 879 * 根据条件字段获取指定表的数据 880 * @param mixed $value 条件,可用常量或者数组 881 * @param string $condition 条件字段 882 * @param string $field 需要返回的字段,不传则返回整个数据 883 * @param string $table 需要查询的表 884 * @author huajie <banhuajie@163.com> 885 */ 886 function get_table_field($value = null, $condition = 'id', $field = null, $table = null){ 887 if(empty($value) || empty($table)){ 888 return false; 889 } 890 891 //拼接参数 892 $map[$condition] = $value; 893 $info = M(ucfirst($table))->where($map); 894 if(empty($field)){ 895 $info = $info->field(true)->find(); 896 }else{ 897 $info = $info->getField($field); 898 } 899 return $info; 900 } 901 902 /** 903 * 获取链接信息 904 * @param int $link_id 905 * @param string $field 906 * @return 完整的链接信息或者某一字段 907 * @author huajie <banhuajie@163.com> 908 */ 909 function get_link($link_id = null, $field = 'url'){ 910 $link = ''; 911 if(empty($link_id)){ 912 return $link; 913 } 914 $link = M('Url')->getById($link_id); 915 if(empty($field)){ 916 return $link; 917 }else{ 918 return $link[$field]; 919 } 920 } 921 922 /** 923 * 获取文档封面图片 924 * @param int $cover_id 925 * @param string $field 926 * @return 完整的数据 或者 指定的$field字段值 927 * @author huajie <banhuajie@163.com> 928 */ 929 function get_cover($cover_id, $field = null){ 930 if(empty($cover_id)){ 931 return false; 932 } 933 $picture = M('Picture')->where(array('status'=>1))->getById($cover_id); 934 if($field == 'path'){ 935 if(!empty($picture['url'])){ 936 $picture['path'] = $picture['url']; 937 }else{ 938 $picture['path'] = __ROOT__.$picture['path']; 939 } 940 } 941 return empty($field) ? $picture : $picture[$field]; 942 } 943 944 /** 945 * 检查$pos(推荐位的值)是否包含指定推荐位$contain 946 * @param number $pos 推荐位的值 947 * @param number $contain 指定推荐位 948 * @return boolean true 包含 , false 不包含 949 * @author huajie <banhuajie@163.com> 950 */ 951 function check_document_position($pos = 0, $contain = 0){ 952 if(empty($pos) || empty($contain)){ 953 return false; 954 } 955 956 //将两个参数进行按位与运算,不为0则表示$contain属于$pos 957 $res = $pos & $contain; 958 if($res !== 0){ 959 return true; 960 }else{ 961 return false; 962 } 963 } 964 965 /** 966 * 获取数据的所有子孙数据的id值 967 * @author 朱亚杰 <xcoolcc@gmail.com> 968 */ 969 970 function get_stemma($pids,Model &$model, $field='id'){ 971 $collection = array(); 972 973 //非空判断 974 if(empty($pids)){ 975 return $collection; 976 } 977 978 if( is_array($pids) ){ 979 $pids = trim(implode(',',$pids),','); 980 } 981 $result = $model->field($field)->where(array('pid'=>array('IN',(string)$pids)))->select(); 982 $child_ids = array_column ((array)$result,'id'); 983 984 while( !empty($child_ids) ){ 985 $collection = array_merge($collection,$result); 986 $result = $model->field($field)->where( array( 'pid'=>array( 'IN', $child_ids ) ) )->select(); 987 $child_ids = array_column((array)$result,'id'); 988 } 989 return $collection; 990 } 991 992 /** 993 * 验证分类是否允许发布内容 994 * @param integer $id 分类ID 995 * @return boolean true-允许发布内容,false-不允许发布内容 996 */ 997 function check_category($id){ 998 if (is_array($id)) { 999 $id['type'] = !empty($id['type'])?$id['type']:2; 1000 $type = get_category($id['category_id'], 'type'); 1001 $type = explode(",", $type); 1002 return in_array($id['type'], $type); 1003 } else { 1004 $publish = get_category($id, 'allow_publish'); 1005 return $publish ? true : false; 1006 } 1007 } 1008 1009 /** 1010 * 检测分类是否绑定了指定模型 1011 * @param array $info 模型ID和分类ID数组 1012 * @return boolean true-绑定了模型,false-未绑定模型 1013 */ 1014 function check_category_model($info){ 1015 $cate = get_category($info['category_id']); 1016 $array = explode(',', $info['pid'] ? $cate['model_sub'] : $cate['model']); 1017 return in_array($info['model_id'], $array); 1018 }
2. 安装公共方法
1 <?php 2 // +---------------------------------------------------------------------- 3 // | OneThink [ WE CAN DO IT JUST THINK IT ] 4 // +---------------------------------------------------------------------- 5 // | Copyright (c) 2013 http://www.onethink.cn All rights reserved. 6 // +---------------------------------------------------------------------- 7 // | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn> 8 // +---------------------------------------------------------------------- 9 10 // 检测环境是否支持可写 11 define('IS_WRITE',APP_MODE !== 'sae'); 12 13 /** 14 * 系统环境检测 15 * @return array 系统环境数据 16 */ 17 function check_env(){ 18 $items = array( 19 'os' => array('操作系统', '不限制', '类Unix', PHP_OS, 'success'), 20 'php' => array('PHP版本', '5.3', '5.3+', PHP_VERSION, 'success'), 21 'upload' => array('附件上传', '不限制', '2M+', '未知', 'success'), 22 'gd' => array('GD库', '2.0', '2.0+', '未知', 'success'), 23 'disk' => array('磁盘空间', '5M', '不限制', '未知', 'success'), 24 ); 25 26 //PHP环境检测 27 if($items['php'][3] < $items['php'][1]){ 28 $items['php'][4] = 'error'; 29 session('error', true); 30 } 31 32 //附件上传检测 33 if(@ini_get('file_uploads')) 34 $items['upload'][3] = ini_get('upload_max_filesize'); 35 36 //GD库检测 37 $tmp = function_exists('gd_info') ? gd_info() : array(); 38 if(empty($tmp['GD Version'])){ 39 $items['gd'][3] = '未安装'; 40 $items['gd'][4] = 'error'; 41 session('error', true); 42 } else { 43 $items['gd'][3] = $tmp['GD Version']; 44 } 45 unset($tmp); 46 47 //磁盘空间检测 48 if(function_exists('disk_free_space')) { 49 $items['disk'][3] = floor(disk_free_space(INSTALL_APP_PATH) / (1024*1024)).'M'; 50 } 51 52 return $items; 53 } 54 55 /** 56 * 目录,文件读写检测 57 * @return array 检测数据 58 */ 59 function check_dirfile(){ 60 $items = array( 61 array('dir', '可写', 'success', './Uploads/Download'), 62 array('dir', '可写', 'success', './Uploads/Picture'), 63 array('dir', '可写', 'success', './Uploads/Editor'), 64 array('dir', '可写', 'success', './Runtime'), 65 array('dir', '可写', 'success', './Data'), 66 array('dir', '可写', 'success', './Application/User/Conf'), 67 array('file', '可写', 'success', './Application/Common/Conf'), 68 69 ); 70 71 foreach ($items as &$val) { 72 $item = INSTALL_APP_PATH . $val[3]; 73 if('dir' == $val[0]){ 74 if(!is_writable($item)) { 75 if(is_dir($items)) { 76 $val[1] = '可读'; 77 $val[2] = 'error'; 78 session('error', true); 79 } else { 80 $val[1] = '不存在'; 81 $val[2] = 'error'; 82 session('error', true); 83 } 84 } 85 } else { 86 if(file_exists($item)) { 87 if(!is_writable($item)) { 88 $val[1] = '不可写'; 89 $val[2] = 'error'; 90 session('error', true); 91 } 92 } else { 93 if(!is_writable(dirname($item))) { 94 $val[1] = '不存在'; 95 $val[2] = 'error'; 96 session('error', true); 97 } 98 } 99 } 100 } 101 102 return $items; 103 } 104 105 /** 106 * 函数检测 107 * @return array 检测数据 108 */ 109 function check_func(){ 110 $items = array( 111 array('pdo','支持','success','类'), 112 array('pdo_mysql','支持','success','模块'), 113 array('file_get_contents', '支持', 'success','函数'), 114 array('mb_strlen', '支持', 'success','函数'), 115 ); 116 117 foreach ($items as &$val) { 118 if(('类'==$val[3] && !class_exists($val[0])) 119 || ('模块'==$val[3] && !extension_loaded($val[0])) 120 || ('函数'==$val[3] && !function_exists($val[0])) 121 ){ 122 $val[1] = '不支持'; 123 $val[2] = 'error'; 124 session('error', true); 125 } 126 } 127 128 return $items; 129 } 130 131 /** 132 * 写入配置文件 133 * @param array $config 配置信息 134 */ 135 function write_config($config, $auth){ 136 if(is_array($config)){ 137 //读取配置内容 138 $conf = file_get_contents(MODULE_PATH . 'Data/conf.tpl'); 139 $user = file_get_contents(MODULE_PATH . 'Data/user.tpl'); 140 //替换配置项 141 foreach ($config as $name => $value) { 142 $conf = str_replace("[{$name}]", $value, $conf); 143 $user = str_replace("[{$name}]", $value, $user); 144 } 145 146 $conf = str_replace('[AUTH_KEY]', $auth, $conf); 147 $user = str_replace('[AUTH_KEY]', $auth, $user); 148 149 //写入应用配置文件 150 if(!IS_WRITE){ 151 return '由于您的环境不可写,请复制下面的配置文件内容覆盖到相关的配置文件,然后再登录后台。<p>'.realpath(APP_PATH).'/Common/Conf/config.php</p> 152 <textarea name="" style="650px;height:185px">'.$conf.'</textarea> 153 <p>'.realpath(APP_PATH).'/User/Conf/config.php</p> 154 <textarea name="" style="650px;height:125px">'.$user.'</textarea>'; 155 }else{ 156 if(file_put_contents(APP_PATH . 'Common/Conf/config.php', $conf) && 157 file_put_contents(APP_PATH . 'User/Conf/config.php', $user)){ 158 show_msg('配置文件写入成功'); 159 } else { 160 show_msg('配置文件写入失败!', 'error'); 161 session('error', true); 162 } 163 return ''; 164 } 165 166 } 167 } 168 169 /** 170 * 创建数据表 171 * @param resource $db 数据库连接资源 172 */ 173 function create_tables($db, $prefix = ''){ 174 //读取SQL文件 175 $sql = file_get_contents(MODULE_PATH . 'Data/install.sql'); 176 $sql = str_replace("\r", "\n", $sql); 177 $sql = explode(";\n", $sql); 178 179 //替换表前缀 180 $orginal = C('ORIGINAL_TABLE_PREFIX'); 181 $sql = str_replace(" `{$orginal}", " `{$prefix}", $sql); 182 183 //开始安装 184 show_msg('开始安装数据库...'); 185 foreach ($sql as $value) { 186 $value = trim($value); 187 if(empty($value)) continue; 188 if(substr($value, 0, 12) == 'CREATE TABLE') { 189 $name = preg_replace("/^CREATE TABLE `(\w+)` .*/s", "\\1", $value); 190 $msg = "创建数据表{$name}"; 191 if(false !== $db->execute($value)){ 192 show_msg($msg . '...成功'); 193 } else { 194 show_msg($msg . '...失败!', 'error'); 195 session('error', true); 196 } 197 } else { 198 $db->execute($value); 199 } 200 201 } 202 } 203 204 function register_administrator($db, $prefix, $admin, $auth){ 205 show_msg('开始注册创始人帐号...'); 206 $sql = "INSERT INTO `[PREFIX]ucenter_member` VALUES " . 207 "('1', '[NAME]', '[PASS]', '[EMAIL]', '', '[TIME]', '[IP]', 0, 0, '[TIME]', '1')"; 208 209 $password = user_md5($admin['password'], $auth); 210 $sql = str_replace( 211 array('[PREFIX]', '[NAME]', '[PASS]', '[EMAIL]', '[TIME]', '[IP]'), 212 array($prefix, $admin['username'], $password, $admin['email'], NOW_TIME, get_client_ip(1)), 213 $sql); 214 //执行sql 215 $db->execute($sql); 216 217 $sql = "INSERT INTO `[PREFIX]member` VALUES ". 218 "('1', '[NAME]', '0', '0000-00-00', '', '0', '1', '0', '[TIME]', '0', '[TIME]', '1');"; 219 $sql = str_replace( 220 array('[PREFIX]', '[NAME]', '[TIME]'), 221 array($prefix, $admin['username'], NOW_TIME), 222 $sql); 223 $db->execute($sql); 224 show_msg('创始人帐号注册完成!'); 225 } 226 227 /** 228 * 更新数据表 229 * @param resource $db 数据库连接资源 230 * @author lyq <605415184@qq.com> 231 */ 232 function update_tables($db, $prefix = ''){ 233 //读取SQL文件 234 $sql = file_get_contents(MODULE_PATH . 'Data/update.sql'); 235 $sql = str_replace("\r", "\n", $sql); 236 $sql = explode(";\n", $sql); 237 238 //替换表前缀 239 $sql = str_replace(" `onethink_", " `{$prefix}", $sql); 240 241 //开始安装 242 show_msg('开始升级数据库...'); 243 foreach ($sql as $value) { 244 $value = trim($value); 245 if(empty($value)) continue; 246 if(substr($value, 0, 12) == 'CREATE TABLE') { 247 $name = preg_replace("/^CREATE TABLE `(\w+)` .*/s", "\\1", $value); 248 $msg = "创建数据表{$name}"; 249 if(false !== $db->execute($value)){ 250 show_msg($msg . '...成功'); 251 } else { 252 show_msg($msg . '...失败!', 'error'); 253 session('error', true); 254 } 255 } else { 256 if(substr($value, 0, 8) == 'UPDATE `') { 257 $name = preg_replace("/^UPDATE `(\w+)` .*/s", "\\1", $value); 258 $msg = "更新数据表{$name}"; 259 } else if(substr($value, 0, 11) == 'ALTER TABLE'){ 260 $name = preg_replace("/^ALTER TABLE `(\w+)` .*/s", "\\1", $value); 261 $msg = "修改数据表{$name}"; 262 } else if(substr($value, 0, 11) == 'INSERT INTO'){ 263 $name = preg_replace("/^INSERT INTO `(\w+)` .*/s", "\\1", $value); 264 $msg = "写入数据表{$name}"; 265 } 266 if(($db->execute($value)) !== false){ 267 show_msg($msg . '...成功'); 268 } else{ 269 show_msg($msg . '...失败!', 'error'); 270 session('error', true); 271 } 272 } 273 } 274 } 275 276 /** 277 * 及时显示提示信息 278 * @param string $msg 提示信息 279 */ 280 function show_msg($msg, $class = ''){ 281 echo "<script type=\"text/javascript\">showmsg(\"{$msg}\", \"{$class}\")</script>"; 282 flush(); 283 ob_flush(); 284 } 285 286 /** 287 * 生成系统AUTH_KEY 288 * @author 麦当苗儿 <zuojiazi@vip.qq.com> 289 */ 290 function build_auth_key(){ 291 $chars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 292 $chars .= '`~!@#$%^&*()_+-=[]{};:"|,.<>/?'; 293 $chars = str_shuffle($chars); 294 return substr($chars, 0, 40); 295 } 296 297 /** 298 * 系统非常规MD5加密方法 299 * @param string $str 要加密的字符串 300 * @return string 301 */ 302 function user_md5($str, $key = ''){ 303 return '' === $str ? '' : md5(sha1($str) . $key); 304 }
3. 加密公共方法
1 <?php 2 // +---------------------------------------------------------------------- 3 // | OneThink [ WE CAN DO IT JUST THINK IT ] 4 // +---------------------------------------------------------------------- 5 // | Copyright (c) 2013 http://www.onethink.cn All rights reserved. 6 // +---------------------------------------------------------------------- 7 // | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn> 8 // +---------------------------------------------------------------------- 9 10 /** 11 * 系统非常规MD5加密方法 12 * @param string $str 要加密的字符串 13 * @return string 14 */ 15 function think_ucenter_md5($str, $key = 'ThinkUCenter'){ 16 return '' === $str ? '' : md5(sha1($str) . $key); 17 } 18 19 /** 20 * 系统加密方法 21 * @param string $data 要加密的字符串 22 * @param string $key 加密密钥 23 * @param int $expire 过期时间 (单位:秒) 24 * @return string 25 */ 26 function think_ucenter_encrypt($data, $key, $expire = 0) { 27 $key = md5($key); 28 $data = base64_encode($data); 29 $x = 0; 30 $len = strlen($data); 31 $l = strlen($key); 32 $char = ''; 33 for ($i = 0; $i < $len; $i++) { 34 if ($x == $l) $x=0; 35 $char .= substr($key, $x, 1); 36 $x++; 37 } 38 $str = sprintf('%010d', $expire ? $expire + time() : 0); 39 for ($i = 0; $i < $len; $i++) { 40 $str .= chr(ord(substr($data,$i,1)) + (ord(substr($char,$i,1)))%256); 41 } 42 return str_replace('=', '', base64_encode($str)); 43 } 44 45 /** 46 * 系统解密方法 47 * @param string $data 要解密的字符串 (必须是think_encrypt方法加密的字符串) 48 * @param string $key 加密密钥 49 * @return string 50 */ 51 function think_ucenter_decrypt($data, $key){ 52 $key = md5($key); 53 $x = 0; 54 $data = base64_decode($data); 55 $expire = substr($data, 0, 10); 56 $data = substr($data, 10); 57 if($expire > 0 && $expire < time()) { 58 return ''; 59 } 60 $len = strlen($data); 61 $l = strlen($key); 62 $char = $str = ''; 63 for ($i = 0; $i < $len; $i++) { 64 if ($x == $l) $x = 0; 65 $char .= substr($key, $x, 1); 66 $x++; 67 } 68 for ($i = 0; $i < $len; $i++) { 69 if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) { 70 $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1))); 71 }else{ 72 $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1))); 73 } 74 } 75 return base64_decode($str); 76 }