PageRenderTime 54ms CodeModel.GetById 14ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/ThinkPHP/Mode/Api/functions.php

https://github.com/wujingke/thinkphp
PHP | 1065 lines | 878 code | 21 blank | 166 comment | 65 complexity | 057daf25e6c0747e4603debf23d928a8 MD5 | raw file
   1<?php
   2// +----------------------------------------------------------------------
   3// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
   4// +----------------------------------------------------------------------
   5// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
   6// +----------------------------------------------------------------------
   7// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
   8// +----------------------------------------------------------------------
   9// | Author: liu21st <liu21st@gmail.com>
  10// +----------------------------------------------------------------------
  11
  12/**
  13 * Think API模式函数库
  14 */
  15
  16/**
  17 * 获取和设置配置参数 支持批量定义
  18 * @param string|array $name 配置变量
  19 * @param mixed $value 配置值
  20 * @param mixed $default 默认值
  21 * @return mixed
  22 */
  23function C($name=null, $value=null,$default=null) {
  24    static $_config = array();
  25    // 无参数时获取所有
  26    if (empty($name)) {
  27        return $_config;
  28    }
  29    // 优先执行设置获取或赋值
  30    if (is_string($name)) {
  31        if (!strpos($name, '.')) {
  32            $name = strtolower($name);
  33            if (is_null($value))
  34                return isset($_config[$name]) ? $_config[$name] : $default;
  35            $_config[$name] = $value;
  36            return;
  37        }
  38        // 二维数组设置和获取支持
  39        $name = explode('.', $name);
  40        $name[0]   =  strtolower($name[0]);
  41        if (is_null($value))
  42            return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
  43        $_config[$name[0]][$name[1]] = $value;
  44        return;
  45    }
  46    // 批量设置
  47    if (is_array($name)){
  48        $_config = array_merge($_config, array_change_key_case($name));
  49        return;
  50    }
  51    return null; // 避免非法参数
  52}
  53
  54/**
  55 * 加载配置文件 支持格式转换 仅支持一级配置
  56 * @param string $file 配置文件名
  57 * @param string $parse 配置解析方法 有些格式需要用户自己解析
  58 * @return void
  59 */
  60function load_config($file,$parse=CONF_PARSE){
  61    $ext  = pathinfo($file,PATHINFO_EXTENSION);
  62    switch($ext){
  63        case 'php':
  64            return include $file;
  65        case 'ini':
  66            return parse_ini_file($file);
  67        case 'yaml':
  68            return yaml_parse_file($file);
  69        case 'xml': 
  70            return (array)simplexml_load_file($file);
  71        case 'json':
  72            return json_decode(file_get_contents($file), true);
  73        default:
  74            if(function_exists($parse)){
  75                return $parse($file);
  76            }else{
  77                E(L('_NOT_SUPPERT_').':'.$ext);
  78            }
  79    }
  80}
  81
  82/**
  83 * 抛出异常处理
  84 * @param string $msg 异常消息
  85 * @param integer $code 异常代码 默认为0
  86 * @return void
  87 */
  88function E($msg, $code=0) {
  89    throw new Think\Exception($msg, $code);
  90}
  91
  92/**
  93 * 记录和统计时间(微秒)和内存使用情况
  94 * 使用方法:
  95 * <code>
  96 * G('begin'); // 记录开始标记位
  97 * // ... 区间运行代码
  98 * G('end'); // 记录结束标签位
  99 * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位
 100 * echo G('begin','end','m'); // 统计区间内存使用情况
 101 * 如果end标记位没有定义,则会自动以当前作为标记位
 102 * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
 103 * </code>
 104 * @param string $start 开始标签
 105 * @param string $end 结束标签
 106 * @param integer|string $dec 小数位或者m
 107 * @return mixed
 108 */
 109function G($start,$end='',$dec=4) {
 110    static $_info       =   array();
 111    static $_mem        =   array();
 112    if(is_float($end)) { // 记录时间
 113        $_info[$start]  =   $end;
 114    }elseif(!empty($end)){ // 统计时间和内存使用
 115        if(!isset($_info[$end])) $_info[$end]       =  microtime(TRUE);
 116        if(MEMORY_LIMIT_ON && $dec=='m'){
 117            if(!isset($_mem[$end])) $_mem[$end]     =  memory_get_usage();
 118            return number_format(($_mem[$end]-$_mem[$start])/1024);
 119        }else{
 120            return number_format(($_info[$end]-$_info[$start]),$dec);
 121        }
 122
 123    }else{ // 记录时间和内存使用
 124        $_info[$start]  =  microtime(TRUE);
 125        if(MEMORY_LIMIT_ON) $_mem[$start]           =  memory_get_usage();
 126    }
 127}
 128
 129/**
 130 * 获取和设置语言定义(不区分大小写)
 131 * @param string|array $name 语言变量
 132 * @param string $value 语言值
 133 * @return mixed
 134 */
 135function L($name=null, $value=null) {
 136    static $_lang = array();
 137    // 空参数返回所有定义
 138    if (empty($name))
 139        return $_lang;
 140    // 判断语言获取(或设置)
 141    // 若不存在,直接返回全大写$name
 142    if (is_string($name)) {
 143        $name = strtoupper($name);
 144        if (is_null($value))
 145            return isset($_lang[$name]) ? $_lang[$name] : $name;
 146        $_lang[$name] = $value; // 语言定义
 147        return;
 148    }
 149    // 批量定义
 150    if (is_array($name))
 151        $_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
 152    return;
 153}
 154
 155/**
 156 * 添加和获取页面Trace记录
 157 * @param string $value 变量
 158 * @param string $label 标签
 159 * @param string $level 日志级别
 160 * @param boolean $record 是否记录日志
 161 * @return void
 162 */
 163function trace($value='[think]',$label='',$level='DEBUG',$record=false) {
 164    return Think\Think::trace($value,$label,$level,$record);
 165}
 166
 167/**
 168 * 编译文件
 169 * @param string $filename 文件名
 170 * @return string
 171 */
 172function compile($filename) {
 173    $content    =   php_strip_whitespace($filename);
 174    $content    =   trim(substr($content, 5));
 175    // 替换预编译指令
 176    $content    =   preg_replace('/\/\/\[RUNTIME\](.*?)\/\/\[\/RUNTIME\]/s', '', $content);
 177    if(0===strpos($content,'namespace')){
 178        $content    =   preg_replace('/namespace\s(.*?);/','namespace \\1{',$content,1);
 179    }else{
 180        $content    =   'namespace {'.$content;
 181    }
 182    if ('?>' == substr($content, -2))
 183        $content    = substr($content, 0, -2);
 184    return $content.'}';
 185}
 186
 187/**
 188 * 获取输入参数 支持过滤和默认值
 189 * 使用方法:
 190 * <code>
 191 * I('id',0); 获取id参数 自动判断get或者post
 192 * I('post.name','','htmlspecialchars'); 获取$_POST['name']
 193 * I('get.'); 获取$_GET
 194 * </code>
 195 * @param string $name 变量的名称 支持指定类型
 196 * @param mixed $default 不存在的时候默认值
 197 * @param mixed $filter 参数过滤方法
 198 * @return mixed
 199 */
 200function I($name,$default='',$filter=null) {
 201    if(strpos($name,'.')) { // 指定参数来源
 202        list($method,$name) =   explode('.',$name,2);
 203    }else{ // 默认为自动判断
 204        $method =   'param';
 205    }
 206    switch(strtolower($method)) {
 207        case 'get'     :   $input =& $_GET;break;
 208        case 'post'    :   $input =& $_POST;break;
 209        case 'put'     :   parse_str(file_get_contents('php://input'), $input);break;
 210        case 'param'   :
 211            switch($_SERVER['REQUEST_METHOD']) {
 212                case 'POST':
 213                    $input  =  $_POST;
 214                    break;
 215                case 'PUT':
 216                    parse_str(file_get_contents('php://input'), $input);
 217                    break;
 218                default:
 219                    $input  =  $_GET;
 220            }
 221            break;
 222        case 'request' :   $input =& $_REQUEST;   break;
 223        case 'session' :   $input =& $_SESSION;   break;
 224        case 'cookie'  :   $input =& $_COOKIE;    break;
 225        case 'server'  :   $input =& $_SERVER;    break;
 226        case 'globals' :   $input =& $GLOBALS;    break;
 227        default:
 228            return NULL;
 229    }
 230    if(empty($name)) { // 获取全部变量
 231        $data       =   $input;
 232        array_walk_recursive($data,'filter_exp');
 233        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
 234        if($filters) {
 235            $filters    =   explode(',',$filters);
 236            foreach($filters as $filter){
 237                $data   =   array_map_recursive($filter,$data); // 参数过滤
 238            }
 239        }
 240    }elseif(isset($input[$name])) { // 取值操作
 241        $data       =   $input[$name];
 242        is_array($data) && array_walk_recursive($data,'filter_exp');
 243        $filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
 244        if($filters) {
 245            $filters    =   explode(',',$filters);
 246            foreach($filters as $filter){
 247                if(function_exists($filter)) {
 248                    $data   =   is_array($data)?array_map_recursive($filter,$data):$filter($data); // 参数过滤
 249                }else{
 250                    $data   =   filter_var($data,is_int($filter)?$filter:filter_id($filter));
 251                    if(false === $data) {
 252                        return   isset($default)?$default:NULL;
 253                    }
 254                }
 255            }
 256        }
 257    }else{ // 变量默认值
 258        $data       =    isset($default)?$default:NULL;
 259    }
 260    return $data;
 261}
 262
 263function array_map_recursive($filter, $data) {
 264     $result = array();
 265     foreach ($data as $key => $val) {
 266         $result[$key] = is_array($val)
 267             ? array_map_recursive($filter, $val)
 268             : call_user_func($filter, $val);
 269     }
 270     return $result;
 271 }
 272
 273/**
 274 * 设置和获取统计数据
 275 * 使用方法:
 276 * <code>
 277 * N('db',1); // 记录数据库操作次数
 278 * N('read',1); // 记录读取次数
 279 * echo N('db'); // 获取当前页面数据库的所有操作次数
 280 * echo N('read'); // 获取当前页面读取次数
 281 * </code>
 282 * @param string $key 标识位置
 283 * @param integer $step 步进值
 284 * @return mixed
 285 */
 286function N($key, $step=0,$save=false) {
 287    static $_num    = array();
 288    if (!isset($_num[$key])) {
 289        $_num[$key] = (false !== $save)? S('N_'.$key) :  0;
 290    }
 291    if (empty($step))
 292        return $_num[$key];
 293    else
 294        $_num[$key] = $_num[$key] + (int) $step;
 295    if(false !== $save){ // 保存结果
 296        S('N_'.$key,$_num[$key],$save);
 297    }
 298}
 299
 300/**
 301 * 字符串命名风格转换
 302 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
 303 * @param string $name 字符串
 304 * @param integer $type 转换类型
 305 * @return string
 306 */
 307function parse_name($name, $type=0) {
 308    if ($type) {
 309        return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function($match){return strtoupper($match[1]);}, $name));
 310    } else {
 311        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
 312    }
 313}
 314
 315/**
 316 * 优化的require_once
 317 * @param string $filename 文件地址
 318 * @return boolean
 319 */
 320function require_cache($filename) {
 321    static $_importFiles = array();
 322    if (!isset($_importFiles[$filename])) {
 323        if (file_exists_case($filename)) {
 324            require $filename;
 325            $_importFiles[$filename] = true;
 326        } else {
 327            $_importFiles[$filename] = false;
 328        }
 329    }
 330    return $_importFiles[$filename];
 331}
 332
 333/**
 334 * 区分大小写的文件存在判断
 335 * @param string $filename 文件地址
 336 * @return boolean
 337 */
 338function file_exists_case($filename) {
 339    if (is_file($filename)) {
 340        if (IS_WIN && APP_DEBUG) {
 341            if (basename(realpath($filename)) != basename($filename))
 342                return false;
 343        }
 344        return true;
 345    }
 346    return false;
 347}
 348
 349/**
 350 * 导入所需的类库 同java的Import 本函数有缓存功能
 351 * @param string $class 类库命名空间字符串
 352 * @param string $baseUrl 起始路径
 353 * @param string $ext 导入的文件扩展名
 354 * @return boolean
 355 */
 356function import($class, $baseUrl = '', $ext=EXT) {
 357    static $_file = array();
 358    $class = str_replace(array('.', '#'), array('/', '.'), $class);
 359    if (isset($_file[$class . $baseUrl]))
 360        return true;
 361    else
 362        $_file[$class . $baseUrl] = true;
 363    $class_strut     = explode('/', $class);
 364    if (empty($baseUrl)) {
 365        if ('@' == $class_strut[0] || MODULE_NAME == $class_strut[0]) {
 366            //加载当前模块的类库
 367            $baseUrl = MODULE_PATH;
 368            $class   = substr_replace($class, '', 0, strlen($class_strut[0]) + 1);
 369        }elseif (in_array($class_strut[0],array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$class_strut[0])) {
 370            // 系统类库包和第三方类库包
 371            $baseUrl = LIB_PATH;
 372        }else { // 加载其他模块的类库
 373            $baseUrl = APP_PATH;
 374        }
 375    }
 376    if (substr($baseUrl, -1) != '/')
 377        $baseUrl    .= '/';
 378    $classfile       = $baseUrl . $class . $ext;
 379    if (!class_exists(basename($class),false)) {
 380        // 如果类不存在 则导入类库文件
 381        return require_cache($classfile);
 382    }
 383}
 384
 385/**
 386 * 基于命名空间方式导入函数库
 387 * load('@.Util.Array')
 388 * @param string $name 函数库命名空间字符串
 389 * @param string $baseUrl 起始路径
 390 * @param string $ext 导入的文件扩展名
 391 * @return void
 392 */
 393function load($name, $baseUrl='', $ext='.php') {
 394    $name = str_replace(array('.', '#'), array('/', '.'), $name);
 395    if (empty($baseUrl)) {
 396        if (0 === strpos($name, '@/')) {//加载当前模块函数库
 397            $baseUrl    =   MODULE_PATH.'Common/';
 398            $name       =   substr($name, 2);
 399        } else { //加载其他模块函数库
 400            $array      =   explode('/', $name);
 401            $baseUrl    =   APP_PATH . array_shift($array).'/Common/';
 402            $name       =   implode('/',$array);
 403        }
 404    }
 405    if (substr($baseUrl, -1) != '/')
 406        $baseUrl       .= '/';
 407    require_cache($baseUrl . $name . $ext);
 408}
 409
 410/**
 411 * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
 412 * @param string $class 类库
 413 * @param string $baseUrl 基础目录
 414 * @param string $ext 类库后缀
 415 * @return boolean
 416 */
 417function vendor($class, $baseUrl = '', $ext='.php') {
 418    if (empty($baseUrl))
 419        $baseUrl = VENDOR_PATH;
 420    return import($class, $baseUrl, $ext);
 421}
 422
 423/**
 424 * D函数用于实例化模型类 格式 [资源://][模块/]模型
 425 * @param string $name 资源地址
 426 * @param string $layer 模型层名称
 427 * @return Model
 428 */
 429function D($name='',$layer='') {
 430    if(empty($name)) return new Think\Model;
 431    static $_model  =   array();
 432    $layer          =   $layer? : C('DEFAULT_M_LAYER');
 433    if(isset($_model[$name.$layer]))
 434        return $_model[$name.$layer];
 435    $class          =   parse_res_name($name,$layer);
 436    if(class_exists($class)) {
 437        $model      =   new $class(basename($name));
 438    }elseif(false === strpos($name,'/')){
 439        // 自动加载公共模块下面的模型
 440        $class      =   '\\Common\\'.$layer.'\\'.$name.$layer;
 441        $model      =   class_exists($class)? new $class($name) : new Think\Model($name);
 442    }else {
 443        Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE);
 444        $model      =   new Think\Model(basename($name));
 445    }
 446    $_model[$name.$layer]  =  $model;
 447    return $model;
 448}
 449
 450/**
 451 * M函数用于实例化一个没有模型文件的Model
 452 * @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
 453 * @param string $tablePrefix 表前缀
 454 * @param mixed $connection 数据库连接信息
 455 * @return Model
 456 */
 457function M($name='', $tablePrefix='',$connection='') {
 458    static $_model  = array();
 459    if(strpos($name,':')) {
 460        list($class,$name)    =  explode(':',$name);
 461    }else{
 462        $class      =   'Think\\Model';
 463    }
 464    $guid           =   (is_array($connection)?implode('',$connection):$connection).$tablePrefix . $name . '_' . $class;
 465    if (!isset($_model[$guid]))
 466        $_model[$guid] = new $class($name,$tablePrefix,$connection);
 467    return $_model[$guid];
 468}
 469
 470/**
 471 * 解析资源地址并导入类库文件
 472 * 例如 module/controller addon://module/behavior
 473 * @param string $name 资源地址 格式:[扩展://][模块/]资源名
 474 * @param string $layer 分层名称
 475 * @return string
 476 */
 477function parse_res_name($name,$layer,$level=1){
 478    if(strpos($name,'://')) {// 指定扩展资源
 479        list($extend,$name)  =   explode('://',$name);
 480    }else{
 481        $extend  =   '';
 482    }
 483    if(strpos($name,'/') && substr_count($name, '/')>=$level){ // 指定模块
 484        list($module,$name) =  explode('/',$name,2);
 485    }else{
 486        $module =   MODULE_NAME;
 487    }
 488    $array  =   explode('/',$name);
 489    $class  =   $module.'\\'.$layer;
 490    foreach($array as $name){
 491        $class  .=   '\\'.parse_name($name, 1);
 492    }
 493    // 导入资源类库
 494    if($extend){ // 扩展资源
 495        $class      =   $extend.'\\'.$class;
 496    }
 497    return $class.$layer;
 498}
 499
 500/**
 501 * A函数用于实例化控制器 格式:[资源://][模块/]控制器
 502 * @param string $name 资源地址
 503 * @param string $layer 控制层名称
 504 * @param integer $level 控制器层次
 505 * @return Controller|false
 506 */
 507function A($name,$layer='',$level='') {
 508    static $_action = array();
 509    $layer  =   $layer? : C('DEFAULT_C_LAYER');
 510    $level  =   $level? : ($layer == C('DEFAULT_C_LAYER')?C('CONTROLLER_LEVEL'):1);
 511    if(isset($_action[$name.$layer]))
 512        return $_action[$name.$layer];
 513    $class  =   parse_res_name($name,$layer,$level);
 514    if(class_exists($class)) {
 515        $action             =   new $class();
 516        $_action[$name.$layer]     =   $action;
 517        return $action;
 518    }else {
 519        return false;
 520    }
 521}
 522
 523/**
 524 * 远程调用控制器的操作方法 URL 参数格式 [资源://][模块/]控制器/操作
 525 * @param string $url 调用地址
 526 * @param string|array $vars 调用参数 支持字符串和数组
 527 * @param string $layer 要调用的控制层名称
 528 * @return mixed
 529 */
 530function R($url,$vars=array(),$layer='') {
 531    $info   =   pathinfo($url);
 532    $action =   $info['basename'];
 533    $module =   $info['dirname'];
 534    $class  =   A($module,$layer);
 535    if($class){
 536        if(is_string($vars)) {
 537            parse_str($vars,$vars);
 538        }
 539        return call_user_func_array(array(&$class,$action.C('ACTION_SUFFIX')),$vars);
 540    }else{
 541        return false;
 542    }
 543}
 544
 545/**
 546 * 执行某个行为
 547 * @param string $name 行为名称
 548 * @param Mixed $params 传入的参数
 549 * @return void
 550 */
 551function B($name, &$params=NULL) {
 552    if(strpos($name,'/')){
 553        list($name,$tag) = explode('/',$name);
 554    }else{
 555        $tag     =   'run';
 556    }
 557    return \Think\Hook::exec($name,$tag,$params);
 558}
 559
 560/**
 561 * 去除代码中的空白和注释
 562 * @param string $content 代码内容
 563 * @return string
 564 */
 565function strip_whitespace($content) {
 566    $stripStr   = '';
 567    //分析php源码
 568    $tokens     = token_get_all($content);
 569    $last_space = false;
 570    for ($i = 0, $j = count($tokens); $i < $j; $i++) {
 571        if (is_string($tokens[$i])) {
 572            $last_space = false;
 573            $stripStr  .= $tokens[$i];
 574        } else {
 575            switch ($tokens[$i][0]) {
 576                //过滤各种PHP注释
 577                case T_COMMENT:
 578                case T_DOC_COMMENT:
 579                    break;
 580                //过滤空格
 581                case T_WHITESPACE:
 582                    if (!$last_space) {
 583                        $stripStr  .= ' ';
 584                        $last_space = true;
 585                    }
 586                    break;
 587                case T_START_HEREDOC:
 588                    $stripStr .= "<<<THINK\n";
 589                    break;
 590                case T_END_HEREDOC:
 591                    $stripStr .= "THINK;\n";
 592                    for($k = $i+1; $k < $j; $k++) {
 593                        if(is_string($tokens[$k]) && $tokens[$k] == ';') {
 594                            $i = $k;
 595                            break;
 596                        } else if($tokens[$k][0] == T_CLOSE_TAG) {
 597                            break;
 598                        }
 599                    }
 600                    break;
 601                default:
 602                    $last_space = false;
 603                    $stripStr  .= $tokens[$i][1];
 604            }
 605        }
 606    }
 607    return $stripStr;
 608}
 609
 610/**
 611 * 浏览器友好的变量输出
 612 * @param mixed $var 变量
 613 * @param boolean $echo 是否输出 默认为True 如果为false 则返回输出字符串
 614 * @param string $label 标签 默认为空
 615 * @param boolean $strict 是否严谨 默认为true
 616 * @return void|string
 617 */
 618function dump($var, $echo=true, $label=null, $strict=true) {
 619    $label = ($label === null) ? '' : rtrim($label) . ' ';
 620    if (!$strict) {
 621        if (ini_get('html_errors')) {
 622            $output = print_r($var, true);
 623            $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
 624        } else {
 625            $output = $label . print_r($var, true);
 626        }
 627    } else {
 628        ob_start();
 629        var_dump($var);
 630        $output = ob_get_clean();
 631        if (!extension_loaded('xdebug')) {
 632            $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
 633            $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
 634        }
 635    }
 636    if ($echo) {
 637        echo($output);
 638        return null;
 639    }else
 640        return $output;
 641}
 642
 643/**
 644 * URL重定向
 645 * @param string $url 重定向的URL地址
 646 * @param integer $time 重定向的等待时间(秒)
 647 * @param string $msg 重定向前的提示信息
 648 * @return void
 649 */
 650function redirect($url, $time=0, $msg='') {
 651    //多行URL地址支持
 652    $url        = str_replace(array("\n", "\r"), '', $url);
 653    if (empty($msg))
 654        $msg    = "系统将在{$time}秒之后自动跳转到{$url}";
 655    if (!headers_sent()) {
 656        // redirect
 657        if (0 === $time) {
 658            header('Location: ' . $url);
 659        } else {
 660            header("refresh:{$time};url={$url}");
 661            echo($msg);
 662        }
 663        exit();
 664    } else {
 665        $str    = "<meta http-equiv='Refresh' content='{$time};URL={$url}'>";
 666        if ($time != 0)
 667            $str .= $msg;
 668        exit($str);
 669    }
 670}
 671
 672/**
 673 * 缓存管理
 674 * @param mixed $name 缓存名称,如果为数组表示进行缓存设置
 675 * @param mixed $value 缓存值
 676 * @param mixed $options 缓存参数
 677 * @return mixed
 678 */
 679function S($name,$value='',$options=null) {
 680    static $cache   =   '';
 681    if(is_array($options) && empty($cache)){
 682        // 缓存操作的同时初始化
 683        $type       =   isset($options['type'])?$options['type']:'';
 684        $cache      =   Think\Cache::getInstance($type,$options);
 685    }elseif(is_array($name)) { // 缓存初始化
 686        $type       =   isset($name['type'])?$name['type']:'';
 687        $cache      =   Think\Cache::getInstance($type,$name);
 688        return $cache;
 689    }elseif(empty($cache)) { // 自动初始化
 690        $cache      =   Think\Cache::getInstance();
 691    }
 692    if(''=== $value){ // 获取缓存
 693        return $cache->get($name);
 694    }elseif(is_null($value)) { // 删除缓存
 695        return $cache->rm($name);
 696    }else { // 缓存数据
 697        if(is_array($options)) {
 698            $expire     =   isset($options['expire'])?$options['expire']:NULL;
 699        }else{
 700            $expire     =   is_numeric($options)?$options:NULL;
 701        }
 702        return $cache->set($name, $value, $expire);
 703    }
 704}
 705
 706/**
 707 * 快速文件数据读取和保存 针对简单类型数据 字符串、数组
 708 * @param string $name 缓存名称
 709 * @param mixed $value 缓存值
 710 * @param string $path 缓存路径
 711 * @return mixed
 712 */
 713function F($name, $value='', $path=DATA_PATH) {
 714    static $_cache  =   array();
 715    $filename       =   $path . $name . '.php';
 716    if ('' !== $value) {
 717        if (is_null($value)) {
 718            // 删除缓存
 719            if(false !== strpos($name,'*')){
 720                return false; // TODO 
 721            }else{
 722                unset($_cache[$name]);
 723                return Think\Storage::unlink($filename,'F');
 724            }
 725        } else {
 726            Think\Storage::put($filename,serialize($value),'F');
 727            // 缓存数据
 728            $_cache[$name]  =   $value;
 729            return ;
 730        }
 731    }
 732    // 获取缓存数据
 733    if (isset($_cache[$name]))
 734        return $_cache[$name];
 735    if (Think\Storage::has($filename,'F')){
 736        $value      =   unserialize(Think\Storage::read($filename,'F'));
 737        $_cache[$name]  =   $value;
 738    } else {
 739        $value          =   false;
 740    }
 741    return $value;
 742}
 743
 744/**
 745 * 根据PHP各种类型变量生成唯一标识号
 746 * @param mixed $mix 变量
 747 * @return string
 748 */
 749function to_guid_string($mix) {
 750    if (is_object($mix)) {
 751        return spl_object_hash($mix);
 752    } elseif (is_resource($mix)) {
 753        $mix = get_resource_type($mix) . strval($mix);
 754    } else {
 755        $mix = serialize($mix);
 756    }
 757    return md5($mix);
 758}
 759
 760/**
 761 * XML编码
 762 * @param mixed $data 数据
 763 * @param string $root 根节点名
 764 * @param string $item 数字索引的子节点名
 765 * @param string $attr 根节点属性
 766 * @param string $id   数字索引子节点key转换的属性名
 767 * @param string $encoding 数据编码
 768 * @return string
 769 */
 770function xml_encode($data, $root='think', $item='item', $attr='', $id='id', $encoding='utf-8') {
 771    if(is_array($attr)){
 772        $_attr = array();
 773        foreach ($attr as $key => $value) {
 774            $_attr[] = "{$key}=\"{$value}\"";
 775        }
 776        $attr = implode(' ', $_attr);
 777    }
 778    $attr   = trim($attr);
 779    $attr   = empty($attr) ? '' : " {$attr}";
 780    $xml    = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
 781    $xml   .= "<{$root}{$attr}>";
 782    $xml   .= data_to_xml($data, $item, $id);
 783    $xml   .= "</{$root}>";
 784    return $xml;
 785}
 786
 787/**
 788 * 数据XML编码
 789 * @param mixed  $data 数据
 790 * @param string $item 数字索引时的节点名称
 791 * @param string $id   数字索引key转换为的属性名
 792 * @return string
 793 */
 794function data_to_xml($data, $item='item', $id='id') {
 795    $xml = $attr = '';
 796    foreach ($data as $key => $val) {
 797        if(is_numeric($key)){
 798            $id && $attr = " {$id}=\"{$key}\"";
 799            $key  = $item;
 800        }
 801        $xml    .=  "<{$key}{$attr}>";
 802        $xml    .=  (is_array($val) || is_object($val)) ? data_to_xml($val, $item, $id) : $val;
 803        $xml    .=  "</{$key}>";
 804    }
 805    return $xml;
 806}
 807
 808/**
 809 * session管理函数
 810 * @param string|array $name session名称 如果为数组则表示进行session设置
 811 * @param mixed $value session值
 812 * @return mixed
 813 */
 814function session($name,$value='') {
 815    $prefix   =  C('SESSION_PREFIX');
 816    if(is_array($name)) { // session初始化 在session_start 之前调用
 817        if(isset($name['prefix'])) C('SESSION_PREFIX',$name['prefix']);
 818        if(C('VAR_SESSION_ID') && isset($_REQUEST[C('VAR_SESSION_ID')])){
 819            session_id($_REQUEST[C('VAR_SESSION_ID')]);
 820        }elseif(isset($name['id'])) {
 821            session_id($name['id']);
 822        }
 823        if('common' != APP_MODE){ // 其它模式可能不支持
 824            ini_set('session.auto_start', 0);
 825        }
 826        if(isset($name['name']))            session_name($name['name']);
 827        if(isset($name['path']))            session_save_path($name['path']);
 828        if(isset($name['domain']))          ini_set('session.cookie_domain', $name['domain']);
 829        if(isset($name['expire']))          ini_set('session.gc_maxlifetime', $name['expire']);
 830        if(isset($name['use_trans_sid']))   ini_set('session.use_trans_sid', $name['use_trans_sid']?1:0);
 831        if(isset($name['use_cookies']))     ini_set('session.use_cookies', $name['use_cookies']?1:0);
 832        if(isset($name['cache_limiter']))   session_cache_limiter($name['cache_limiter']);
 833        if(isset($name['cache_expire']))    session_cache_expire($name['cache_expire']);
 834        if(isset($name['type']))            C('SESSION_TYPE',$name['type']);
 835        if(C('SESSION_TYPE')) { // 读取session驱动
 836            $type   =   C('SESSION_TYPE');
 837            $class  =   strpos($type,'\\')? $type : 'Think\\Session\\Driver\\'. ucwords(strtolower($type));
 838            $hander =   new $class();
 839            session_set_save_handler(
 840                array(&$hander,"open"), 
 841                array(&$hander,"close"), 
 842                array(&$hander,"read"), 
 843                array(&$hander,"write"), 
 844                array(&$hander,"destroy"), 
 845                array(&$hander,"gc")); 
 846        }
 847        // 启动session
 848        if(C('SESSION_AUTO_START'))  session_start();
 849    }elseif('' === $value){ 
 850        if(0===strpos($name,'[')) { // session 操作
 851            if('[pause]'==$name){ // 暂停session
 852                session_write_close();
 853            }elseif('[start]'==$name){ // 启动session
 854                session_start();
 855            }elseif('[destroy]'==$name){ // 销毁session
 856                $_SESSION =  array();
 857                session_unset();
 858                session_destroy();
 859            }elseif('[regenerate]'==$name){ // 重新生成id
 860                session_regenerate_id();
 861            }
 862        }elseif(0===strpos($name,'?')){ // 检查session
 863            $name   =  substr($name,1);
 864            if(strpos($name,'.')){ // 支持数组
 865                list($name1,$name2) =   explode('.',$name);
 866                return $prefix?isset($_SESSION[$prefix][$name1][$name2]):isset($_SESSION[$name1][$name2]);
 867            }else{
 868                return $prefix?isset($_SESSION[$prefix][$name]):isset($_SESSION[$name]);
 869            }
 870        }elseif(is_null($name)){ // 清空session
 871            if($prefix) {
 872                unset($_SESSION[$prefix]);
 873            }else{
 874                $_SESSION = array();
 875            }
 876        }elseif($prefix){ // 获取session
 877            if(strpos($name,'.')){
 878                list($name1,$name2) =   explode('.',$name);
 879                return isset($_SESSION[$prefix][$name1][$name2])?$_SESSION[$prefix][$name1][$name2]:null;  
 880            }else{
 881                return isset($_SESSION[$prefix][$name])?$_SESSION[$prefix][$name]:null;                
 882            }            
 883        }else{
 884            if(strpos($name,'.')){
 885                list($name1,$name2) =   explode('.',$name);
 886                return isset($_SESSION[$name1][$name2])?$_SESSION[$name1][$name2]:null;  
 887            }else{
 888                return isset($_SESSION[$name])?$_SESSION[$name]:null;
 889            }            
 890        }
 891    }elseif(is_null($value)){ // 删除session
 892        if($prefix){
 893            unset($_SESSION[$prefix][$name]);
 894        }else{
 895            unset($_SESSION[$name]);
 896        }
 897    }else{ // 设置session
 898        if($prefix){
 899            if (!is_array($_SESSION[$prefix])) {
 900                $_SESSION[$prefix] = array();
 901            }
 902            $_SESSION[$prefix][$name]   =  $value;
 903        }else{
 904            $_SESSION[$name]  =  $value;
 905        }
 906    }
 907}
 908
 909/**
 910 * Cookie 设置、获取、删除
 911 * @param string $name cookie名称
 912 * @param mixed $value cookie值
 913 * @param mixed $options cookie参数
 914 * @return mixed
 915 */
 916function cookie($name, $value='', $option=null) {
 917    // 默认设置
 918    $config = array(
 919        'prefix'    =>  C('COOKIE_PREFIX'), // cookie 名称前缀
 920        'expire'    =>  C('COOKIE_EXPIRE'), // cookie 保存时间
 921        'path'      =>  C('COOKIE_PATH'), // cookie 保存路径
 922        'domain'    =>  C('COOKIE_DOMAIN'), // cookie 有效域名
 923    );
 924    // 参数设置(会覆盖黙认设置)
 925    if (!is_null($option)) {
 926        if (is_numeric($option))
 927            $option = array('expire' => $option);
 928        elseif (is_string($option))
 929            parse_str($option, $option);
 930        $config     = array_merge($config, array_change_key_case($option));
 931    }
 932    // 清除指定前缀的所有cookie
 933    if (is_null($name)) {
 934        if (empty($_COOKIE))
 935            return;
 936        // 要删除的cookie前缀,不指定则删除config设置的指定前缀
 937        $prefix = empty($value) ? $config['prefix'] : $value;
 938        if (!empty($prefix)) {// 如果前缀为空字符串将不作处理直接返回
 939            foreach ($_COOKIE as $key => $val) {
 940                if (0 === stripos($key, $prefix)) {
 941                    setcookie($key, '', time() - 3600, $config['path'], $config['domain']);
 942                    unset($_COOKIE[$key]);
 943                }
 944            }
 945        }
 946        return;
 947    }
 948    $name = $config['prefix'] . $name;
 949    if ('' === $value) {
 950        if(isset($_COOKIE[$name])){
 951            $value =    $_COOKIE[$name];
 952            if(0===strpos($value,'think:')){
 953                $value  =   substr($value,6);
 954                return array_map('urldecode',json_decode(MAGIC_QUOTES_GPC?stripslashes($value):$value,true));
 955            }else{
 956                return $value;
 957            }
 958        }else{
 959            return null;
 960        }
 961    } else {
 962        if (is_null($value)) {
 963            setcookie($name, '', time() - 3600, $config['path'], $config['domain']);
 964            unset($_COOKIE[$name]); // 删除指定cookie
 965        } else {
 966            // 设置cookie
 967            if(is_array($value)){
 968                $value  = 'think:'.json_encode(array_map('urlencode',$value));
 969            }
 970            $expire = !empty($config['expire']) ? time() + intval($config['expire']) : 0;
 971            setcookie($name, $value, $expire, $config['path'], $config['domain']);
 972            $_COOKIE[$name] = $value;
 973        }
 974    }
 975}
 976
 977/**
 978 * 加载动态扩展文件
 979 * @return void
 980 */
 981function load_ext_file($path) {
 982    // 加载自定义外部文件
 983    if(C('LOAD_EXT_FILE')) {
 984        $files      =  explode(',',C('LOAD_EXT_FILE'));
 985        foreach ($files as $file){
 986            $file   = $path.'Common/'.$file.'.php';
 987            if(is_file($file)) include $file;
 988        }
 989    }
 990    // 加载自定义的动态配置文件
 991    if(C('LOAD_EXT_CONFIG')) {
 992        $configs    =  C('LOAD_EXT_CONFIG');
 993        if(is_string($configs)) $configs =  explode(',',$configs);
 994        foreach ($configs as $key=>$config){
 995            $file   = $path.'Conf/'.$config.'.php';
 996            if(is_file($file)) {
 997                is_numeric($key)?C(include $file):C($key,include $file);
 998            }
 999        }
1000    }
1001}
1002
1003/**
1004 * 获取客户端IP地址
1005 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
1006 * @return mixed
1007 */
1008function get_client_ip($type = 0) {
1009    $type       =  $type ? 1 : 0;
1010    static $ip  =   NULL;
1011    if ($ip !== NULL) return $ip[$type];
1012    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1013        $arr    =   explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
1014        $pos    =   array_search('unknown',$arr);
1015        if(false !== $pos) unset($arr[$pos]);
1016        $ip     =   trim($arr[0]);
1017    }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
1018        $ip     =   $_SERVER['HTTP_CLIENT_IP'];
1019    }elseif (isset($_SERVER['REMOTE_ADDR'])) {
1020        $ip     =   $_SERVER['REMOTE_ADDR'];
1021    }
1022    // IP地址合法验证
1023    $long = sprintf("%u",ip2long($ip));
1024    $ip   = $long ? array($ip, $long) : array('0.0.0.0', 0);
1025    return $ip[$type];
1026}
1027
1028/**
1029 * 发送HTTP状态
1030 * @param integer $code 状态码
1031 * @return void
1032 */
1033function send_http_status($code) {
1034    static $_status = array(
1035        // Success 2xx
1036        200 => 'OK',
1037        // Redirection 3xx
1038        301 => 'Moved Permanently',
1039        302 => 'Moved Temporarily ',  // 1.1
1040        // Client Error 4xx
1041        400 => 'Bad Request',
1042        403 => 'Forbidden',
1043        404 => 'Not Found',
1044        // Server Error 5xx
1045        500 => 'Internal Server Error',
1046        503 => 'Service Unavailable',
1047    );
1048    if(isset($_status[$code])) {
1049        header('HTTP/1.1 '.$code.' '.$_status[$code]);
1050        // 确保FastCGI模式下正常
1051        header('Status:'.$code.' '.$_status[$code]);
1052    }
1053}
1054
1055// 过滤表单中的表达式
1056function filter_exp(&$value){
1057    if (in_array(strtolower($value),array('exp','or'))){
1058        $value .= ' ';
1059    }
1060}
1061
1062// 不区分大小写的in_array实现
1063function in_array_case($value,$array){
1064    return in_array(strtolower($value),array_map('strtolower',$array));
1065}