<?php use Think\Build; use Think\Exception; // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- /** * Think 系统函数库 */ /** * 获取和设置配置参数 支持批量定义 * * @param string|array $name 配置变量 * @param mixed $value 配置值 * @param mixed $default 默认值 * * @return mixed */ function C($name = null, $value = null, $default = null) { static $_config = array(); // 无参数时获取所有 if (empty($name)) { return $_config; } // 优先执行设置获取或赋值 if (is_string($name)) { if (!strpos($name, '.')) { $name = strtoupper($name); if (is_null($value)) { return isset($_config[$name]) ? $_config[$name] : $default; } $_config[$name] = $value; return null; } // 二维数组设置和获取支持 $name = explode('.', $name); $name[0] = strtoupper($name[0]); if (is_null($value)) { return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default; } $_config[$name[0]][$name[1]] = $value; return null; } // 批量设置 if (is_array($name)) { $_config = array_merge($_config, array_change_key_case($name, CASE_UPPER)); return null; } return null; // 避免非法参数 } /** * 加载配置文件 支持格式转换 仅支持一级配置 * * @param string $file 配置文件名 * @param string $parse 配置解析方法 有些格式需要用户自己解析 * * @return array */ function load_config($file, $parse = CONF_PARSE) { $ext = pathinfo($file, PATHINFO_EXTENSION); switch ($ext) { case 'php': return include $file; case 'ini': return parse_ini_file($file); case 'yaml': return yaml_parse_file($file); case 'xml': return (array)simplexml_load_file($file); case 'json': return json_decode(file_get_contents($file), true); default: if (function_exists($parse)) { return $parse($file); } else { E(L('_NOT_SUPPORT_') . ':' . $ext); } } } /** * 解析yaml文件返回一个数组 * * @param string $file 配置文件名 * * @return array */ if (!function_exists('yaml_parse_file')) { function yaml_parse_file($file) { vendor('spyc.Spyc'); return Spyc::YAMLLoad($file); } } /** * 抛出异常处理 * * @param string $msg 异常消息 * @param integer $code 异常代码 默认为0 * * @throws Think\Exception * @return void */ function E($msg, $code = 0) { throw new Exception($msg, $code); } /** * 记录和统计时间(微秒)和内存使用情况 * 使用方法: * <pre> * G('begin'); // 记录开始标记位 * // ... 区间运行代码 * G('end'); // 记录结束标签位 * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位 * echo G('begin','end','m'); // 统计区间内存使用情况 * 如果end标记位没有定义,则会自动以当前作为标记位 * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效 * </pre> * * @param string $start 开始标签 * @param string $end 结束标签 * @param integer|string $dec 小数位或者m * * @return mixed */ function G($start, $end = '', $dec = 4) { static $_info = array(); static $_mem = array(); if (is_float($end)) { // 记录时间 $_info[$start] = $end; } elseif (!empty($end)) { // 统计时间和内存使用 if (!isset($_info[$end])) { $_info[$end] = microtime(true); } if (MEMORY_LIMIT_ON && $dec == 'm') { if (!isset($_mem[$end])) { $_mem[$end] = memory_get_usage(); } return number_format(($_mem[$end] - $_mem[$start]) / 1024); } else { return number_format(($_info[$end] - $_info[$start]), $dec); } } else { // 记录时间和内存使用 $_info[$start] = microtime(true); if (MEMORY_LIMIT_ON) { $_mem[$start] = memory_get_usage(); } } return null; } /** * 获取和设置语言定义(不区分大小写) * * @param string|array $name 语言变量 * @param mixed $value 语言值或者变量 * * @return mixed */ function L($name = null, $value = null) { static $_lang = array(); // 空参数返回所有定义 if (empty($name)) { return $_lang; } // 判断语言获取(或设置) // 若不存在,直接返回全大写$name if (is_string($name)) { $name = strtoupper($name); if (is_null($value)) { return isset($_lang[$name]) ? $_lang[$name] : $name; } elseif (is_array($value)) { // 支持变量 $replace = array_keys($value); foreach ($replace as &$v) { $v = '{$' . $v . '}'; } return str_replace($replace, $value, isset($_lang[$name]) ? $_lang[$name] : $name); } $_lang[$name] = $value; // 语言定义 return null; } // 批量定义 if (is_array($name)) { $_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER)); } return null; } /** * 添加和获取页面Trace记录 * * @param string $value 变量 * @param string $label 标签 * @param string $level 日志级别 * @param boolean $record 是否记录日志 * * @return void|array */ function trace($value = '[think]', $label = '', $level = 'DEBUG', $record = false) { return Think\Think::trace($value, $label, $level, $record); } /** * 编译文件 * * @param string $filename 文件名 * * @return string */ function compile($filename) { $content = php_strip_whitespace($filename); $content = trim(substr($content, 5)); // 替换预编译指令 $content = preg_replace('/\/\/\[RUNTIME\](.*?)\/\/\[\/RUNTIME\]/s', '', $content); if (0 === strpos($content, 'namespace')) { $content = preg_replace('/namespace\s(.*?);/', 'namespace \\1{', $content, 1); } else { $content = 'namespace {' . $content; } if ('?>' == substr($content, -2)) { $content = substr($content, 0, -2); } return $content . '}'; } /** * 获取模版文件 格式 资源://模块@主题/控制器/操作 * * @param string $template 模版资源地址 * @param string $layer 视图层(目录)名称 * * @return string */ function T($template = '', $layer = '') { // 解析模版资源地址 if (false === strpos($template, '://')) { $template = 'http://' . str_replace(':', '/', $template); } $info = parse_url($template); $file = $info['host'] . (isset($info['path']) ? $info['path'] : ''); $module = isset($info['user']) ? $info['user'] . '/' : MODULE_NAME . '/'; $extend = $info['scheme']; $layer = $layer ? $layer : C('DEFAULT_V_LAYER'); // 获取当前主题的模版路径 $auto = C('AUTOLOAD_NAMESPACE'); if ($auto && isset($auto[$extend])) { // 扩展资源 $baseUrl = $auto[$extend] . $module . $layer . '/'; } elseif (C('VIEW_PATH')) { // 改变模块视图目录 $baseUrl = C('VIEW_PATH'); } elseif (defined('TMPL_PATH')) { // 指定全局视图目录 $baseUrl = TMPL_PATH . $module; } else { $baseUrl = APP_PATH . $module . $layer . '/'; } // 获取主题 $theme = substr_count($file, '/') < 2 ? C('DEFAULT_THEME') : ''; // 分析模板文件规则 $depr = C('TMPL_FILE_DEPR'); if ('' == $file) { // 如果模板文件名为空 按照默认规则定位 $file = CONTROLLER_NAME . $depr . ACTION_NAME; } elseif (false === strpos($file, '/')) { $file = CONTROLLER_NAME . $depr . $file; } elseif ('/' != $depr) { $file = substr_count($file, '/') > 1 ? substr_replace($file, $depr, strrpos($file, '/'), 1) : str_replace('/', $depr, $file); } return $baseUrl . ($theme ? $theme . '/' : '') . $file . C('TMPL_TEMPLATE_SUFFIX'); } /** * 获取输入参数 支持过滤和默认值 * 使用方法: * <pre> * I('id',0); 获取id参数 自动判断get或者post * I('post.name','','htmlspecialchars'); 获取$_POST['name'] * I('get.'); 获取$_GET * </pre> * * @param string $name 变量的名称 支持指定类型 * @param mixed $default 不存在的时候默认值 * @param mixed $filter 参数过滤方法 * @param mixed $datas 要获取的额外数据源 * * @return mixed */ function I($name, $default = '', $filter = null, $datas = null) { static $_PUT = null; if (strpos($name, '/')) { // 指定修饰符 list ($name, $type) = explode('/', $name, 2); } elseif (C('VAR_AUTO_STRING')) { // 默认强制转换为字符串 $type = 's'; } if (strpos($name, '.')) { // 指定参数来源 list ($method, $name) = explode('.', $name, 2); } else { // 默认为自动判断 $method = 'param'; } switch (strtolower($method)) { case 'get': $input = &$_GET; break; case 'post': $input = &$_POST; if (empty($input) && ($_input = get_input_json()) !== false) { $input = $_input; unset($_input); } break; case 'put': if (is_null($_PUT)) { parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; case 'param': switch ($_SERVER['REQUEST_METHOD']) { case 'POST': $input = $_POST; if (empty($input) && ($_input = get_input_json()) !== false) { $input = $_input; } unset($_input); break; case 'PUT': if (is_null($_PUT)) { parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; default: $input = $_GET; } break; case 'path': $input = array(); if (!empty($_SERVER['PATH_INFO'])) { $depr = C('URL_PATHINFO_DEPR'); $input = explode($depr, trim($_SERVER['PATH_INFO'], $depr)); } break; case 'request': $input = &$_REQUEST; break; case 'session': $input = &$_SESSION; break; case 'cookie': $input = &$_COOKIE; break; case 'server': $input = &$_SERVER; break; case 'globals': $input = &$GLOBALS; break; case 'data': $input = &$datas; break; default: return null; } if ('' == $name) { // 获取全部变量 $data = $input; $filters = isset($filter) ? $filter : C('DEFAULT_FILTER'); if ($filters) { if (is_string($filters)) { $filters = explode(',', $filters); } foreach ($filters as $filter) { $data = array_map_recursive($filter, $data); // 参数过滤 } } } elseif (isset($input[$name])) { // 取值操作 $data = $input[$name]; $filters = isset($filter) ? $filter : C('DEFAULT_FILTER'); if ($filters) { if (is_string($filters)) { if (0 === strpos($filters, '/')) { if (1 !== preg_match($filters, (string)$data)) { // 支持正则验证 return isset($default) ? $default : null; } } else { $filters = explode(',', $filters); } } elseif (is_int($filters)) { $filters = array( $filters ); } if (is_array($filters)) { foreach ($filters as $filter) { if (function_exists($filter)) { $data = is_array($data) ? array_map_recursive($filter, $data) : $filter($data); // 参数过滤 } else { $data = filter_var($data, is_int($filter) ? $filter : filter_id($filter)); if (false === $data) { return isset($default) ? $default : null; } } } } } if (!empty($type)) { switch (strtolower($type)) { case 'a': // 数组 $data = (array)$data; break; case 'd': // 数字 $data = (int)$data; break; case 'f': // 浮点 $data = (float)$data; break; case 'b': // 布尔 $data = (boolean)$data; break; case 's': // 字符串 default: $data = (string)$data; } } } else { // 变量默认值 $data = isset($default) ? $default : null; } is_array($data) && array_walk_recursive($data, 'think_filter'); return $data; } function array_map_recursive($filter, $data) { $result = array(); foreach ($data as $key => $val) { $result[$key] = is_array($val) ? array_map_recursive($filter, $val) : call_user_func($filter, $val); } return $result; } /** * 设置和获取统计数据 * 使用方法: * <pre> * N('db',1); // 记录数据库操作次数 * N('read',1); // 记录读取次数 * echo N('db'); // 获取当前页面数据库的所有操作次数 * echo N('read'); // 获取当前页面读取次数 * </pre> * * @param string $key 标识位置 * @param integer $step 步进值 * @param boolean $save 是否保存结果 * * @return mixed */ function N($key, $step = 0, $save = false) { static $_num = array(); if (!isset($_num[$key])) { $_num[$key] = (false !== $save) ? S('N_' . $key) : 0; } if (empty($step)) { return $_num[$key]; } else { $_num[$key] = $_num[$key] + (int)$step; } if (false !== $save) { // 保存结果 S('N_' . $key, $_num[$key], $save); } return null; } /** * 字符串命名风格转换 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 * * @param string $name 字符串 * @param integer $type 转换类型 * * @return string */ function parse_name($name, $type = 0) { if ($type) { return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]); }, $name)); } else { return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } } /** * 优化的require_once * * @param string $filename 文件地址 * * @return boolean */ function require_cache($filename) { static $_importFiles = array(); if (!isset($_importFiles[$filename])) { if (file_exists_case($filename)) { require $filename; $_importFiles[$filename] = true; } else { $_importFiles[$filename] = false; } } return $_importFiles[$filename]; } /** * 区分大小写的文件存在判断 * * @param string $filename 文件地址 * * @return boolean */ function file_exists_case($filename) { if (is_file($filename)) { if (IS_WIN && APP_DEBUG) { if (basename(realpath($filename)) != basename($filename)) { return false; } } return true; } return false; } /** * 导入所需的类库 同java的Import 本函数有缓存功能 * * @param string $class 类库命名空间字符串 * @param string $baseUrl 起始路径 * @param string $ext 导入的文件扩展名 * * @return boolean */ function import($class, $baseUrl = '', $ext = EXT) { static $_file = array(); $searchs = array( '.', '#' ); $replaces = array( '/', '.' ); $class = str_replace($searchs, $replaces, $class); if (isset($_file[$class . $baseUrl])) { return true; } else { $_file[$class . $baseUrl] = true; } $class_strut = explode('/', $class); if (empty($baseUrl)) { $systemDirs = array( 'Think', 'Org', 'Behavior', 'Com', 'Vendor' ); if ('@' == $class_strut[0] || MODULE_NAME == $class_strut[0]) { // 加载当前模块的类库 $baseUrl = MODULE_PATH; $class = substr_replace($class, '', 0, strlen($class_strut[0]) + 1); } elseif ('Common' == $class_strut[0]) { // 加载公共模块的类库 $baseUrl = COMMON_PATH; $class = substr($class, 7); } elseif (in_array($class_strut[0], $systemDirs) || is_dir(LIB_PATH . $class_strut[0])) { // 系统类库包和第三方类库包 $baseUrl = LIB_PATH; } else { // 加载其他模块的类库 $baseUrl = APP_PATH; } } if (substr($baseUrl, -1) != '/') { $baseUrl .= '/'; } $classfile = $baseUrl . $class . $ext; if (!class_exists(basename($class), false)) { // 如果类不存在 则导入类库文件 return require_cache($classfile); } return null; } /** * 基于命名空间方式导入函数库 * load('@.Util.Array') * * @param string $name 函数库命名空间字符串 * @param string $baseUrl 起始路径 * @param string $ext 导入的文件扩展名 * * @return void */ function load($name, $baseUrl = '', $ext = '.php') { $name = str_replace(array( '.', '#' ), array( '/', '.' ), $name); if (empty($baseUrl)) { if (0 === strpos($name, '@/')) { // 加载当前模块函数库 $baseUrl = MODULE_PATH . 'Common/'; $name = substr($name, 2); } else { // 加载其他模块函数库 $array = explode('/', $name); $baseUrl = APP_PATH . array_shift($array) . '/Common/'; $name = implode('/', $array); } } if (substr($baseUrl, -1) != '/') { $baseUrl .= '/'; } require_cache($baseUrl . $name . $ext); } /** * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面 * * @param string $class 类库 * @param string $baseUrl 基础目录 * @param string $ext 类库后缀 * * @return boolean */ function vendor($class, $baseUrl = '', $ext = '.php') { if (empty($baseUrl)) { $baseUrl = VENDOR_PATH; } return import($class, $baseUrl, $ext); } /** * 实例化模型类 格式 [资源://][模块/]模型 * * @param string $name 资源地址 * @param string $layer 模型层名称 * * @return Think\Model */ function D($name = '', $layer = '') { if (empty($name)) { return new Think\Model(); } static $_model = array(); $layer = $layer ?: C('DEFAULT_M_LAYER'); if (isset($_model[$name . $layer])) { return $_model[$name . $layer]; } $class = parse_res_name($name, $layer); if (class_exists($class)) { $model = new $class(basename($name)); } elseif (false === strpos($name, '/')) { // 启用自动生成 MODULE 层 by zhuxun37 if (APP_DEBUG && cfg('CREATE_MODEL_ON') && I('request._create')) { $paths = explode('/', str_replace('\\', '/', $class)); $model = array_pop($paths); Build::buildModel($paths[0], $model); $model = new $class($model); } else { // 自动加载公共模块下面的模型 if (!C('APP_USE_NAMESPACE')) { import('Common/' . $layer . '/' . $class); } else { $class = '\\Common\\' . $layer . '\\' . $name . $layer; } $model = class_exists($class) ? new $class($name) : new Think\Model($name); } } else { // 启用自动生成 MODULE 层 by zhuxun37 if (APP_DEBUG && cfg('CREATE_MODULE_ON') && I('request._create')) { $paths = explode('/', str_replace('\\', '/', $class)); $model = array_pop($paths); Build::buildModel($paths[0], $model); $model = new $class($model); } else { Think\Log::record('D方法实例化没找到模型类' . $class, Think\Log::NOTICE); $model = new Think\Model(basename($name)); } } $_model[$name . $layer] = $model; return $model; } /** * 实例化一个没有模型文件的Model * * @param string $name Model名称 支持指定基础模型 例如 MongoModel:User * @param string $tablePrefix 表前缀 * @param mixed $connection 数据库连接信息 * * @return Think\Model */ function M($name = '', $tablePrefix = '', $connection = '') { static $_model = array(); if (strpos($name, ':')) { list ($class, $name) = explode(':', $name); } else { $class = 'Think\\Model'; } $guid = (is_array($connection) ? implode('', $connection) : $connection) . $tablePrefix . $name . '_' . $class; if (!isset($_model[$guid])) { $_model[$guid] = new $class($name, $tablePrefix, $connection); } return $_model[$guid]; } /** * 解析资源地址并导入类库文件 * 例如 module/controller addon://module/behavior * * @param string $name 资源地址 格式:[扩展://][模块/]资源名 * @param string $layer 分层名称 * @param integer $level 控制器层次 * * @return string */ function parse_res_name($name, $layer, $level = 1) { if (strpos($name, '://')) { // 指定扩展资源 list ($extend, $name) = explode('://', $name); } else { $extend = ''; } if (strpos($name, '/') && substr_count($name, '/') >= $level) { // 指定模块 list ($module, $name) = explode('/', $name, 2); } else { $module = defined('MODULE_NAME') ? MODULE_NAME : ''; } $array = explode('/', $name); if (!C('APP_USE_NAMESPACE')) { $class = parse_name($name, 1); import($module . '/' . $layer . '/' . $class . $layer); } else { $class = $module . '\\' . $layer; foreach ($array as $name) { $class .= '\\' . parse_name($name, 1); } // 导入资源类库 if ($extend) { // 扩展资源 $class = $extend . '\\' . $class; } } return $class . $layer; } /** * 用于实例化访问控制器 * * @param string $name 控制器名 * @param string $path 控制器命名空间(路径) * * @return false */ function controller($name, $path = '') { $layer = C('DEFAULT_C_LAYER'); if (!C('APP_USE_NAMESPACE')) { $class = parse_name($name, 1) . $layer; import(MODULE_NAME . '/' . $layer . '/' . $class); } else { $class = ($path ? basename(ADDON_PATH) . '\\' . $path : MODULE_NAME) . '\\' . $layer; $array = explode('/', $name); foreach ($array as $name) { $class .= '\\' . parse_name($name, 1); } $class .= $layer; } if (class_exists($class)) { return new $class(); } else { // 启用自动生成控制器 by zhuxun37 if (APP_DEBUG && cfg('CREATE_CONTROLLER_ON') && I('request._create')) { $paths = explode('\\', preg_replace("/{$layer}$/i", '', $class)); $module = array_shift($paths); unset($paths[0]); Build::buildController($module, implode('/', $paths)); return new $class(); } // end return false; } } /** * 实例化多层控制器 格式:[资源://][模块/]控制器 * * @param string $name * 资源地址 * @param string $layer * 控制层名称 * @param integer $level * 控制器层次 * * @return false */ function A($name, $layer = '', $level = 0) { static $_action = array(); $layer = $layer ?: C('DEFAULT_C_LAYER'); $level = $level ?: ($layer == C('DEFAULT_C_LAYER') ? C('CONTROLLER_LEVEL') : 1); if (isset($_action[$name . $layer])) { return $_action[$name . $layer]; } $class = parse_res_name($name, $layer, $level); if (class_exists($class)) { $action = new $class(); $_action[$name . $layer] = $action; return $action; } else { return false; } } /** * 远程调用控制器的操作方法 URL 参数格式 [资源://][模块/]控制器/操作 * * @param string $url * 调用地址 * @param string|array $vars * 调用参数 支持字符串和数组 * @param string $layer * 要调用的控制层名称 * * @return mixed */ function R($url, $vars = array(), $layer = '') { $info = pathinfo($url); $action = $info['basename']; $module = $info['dirname']; $class = A($module, $layer); if ($class) { if (is_string($vars)) { parse_str($vars, $vars); } return call_user_func_array(array( &$class, $action . C('ACTION_SUFFIX') ), $vars); } else { return false; } } /** * 处理标签扩展 * * @param string $tag * 标签名称 * @param mixed $params * 传入参数 * * @return void */ function tag($tag, &$params = null) { \Think\Hook::listen($tag, $params); } /** * 执行某个行为 * * @param string $name 行为名称 * @param string $tag 标签名称(行为类无需传入) * @param Mixed $params 传入的参数 * * @return void */ function B($name, $tag = '', &$params = null) { if ('' == $tag) { $name .= 'Behavior'; } \Think\Hook::exec($name, $tag, $params); return; } /** * 去除代码中的空白和注释 * * @param string $content * 代码内容 * * @return string */ function strip_whitespace($content) { $stripStr = ''; // 分析php源码 $tokens = token_get_all($content); $last_space = false; for ($i = 0, $j = count($tokens); $i < $j; $i++) { if (is_string($tokens[$i])) { $last_space = false; $stripStr .= $tokens[$i]; } else { switch ($tokens[$i][0]) { // 过滤各种PHP注释 case T_COMMENT: case T_DOC_COMMENT: break; // 过滤空格 case T_WHITESPACE: if (!$last_space) { $stripStr .= ' '; $last_space = true; } break; case T_START_HEREDOC: $stripStr .= "<<<THINK\n"; break; case T_END_HEREDOC: $stripStr .= "THINK;\n"; for ($k = $i + 1; $k < $j; $k++) { if (is_string($tokens[$k]) && $tokens[$k] == ';') { $i = $k; break; } else if ($tokens[$k][0] == T_CLOSE_TAG) { break; } } break; default: $last_space = false; $stripStr .= $tokens[$i][1]; } } } return $stripStr; } /** * 自定义异常处理 * * @param string $msg * 异常消息 * @param string $type * 异常类型 默认为Think\Exception * @param integer $code * 异常代码 默认为0 * * @return void */ function throw_exception($msg, $type = 'Think\\Exception', $code = 0) { Think\Log::record('建议使用E方法替代throw_exception', Think\Log::NOTICE); if (class_exists($type, false)) { throw new $type($msg, $code); } else { Think\Think::halt($msg); // 异常类型不存在则输出错误信息字串 } } /** * 浏览器友好的变量输出 * * @param mixed $var * 变量 * @param boolean $echo * 是否输出 默认为True 如果为false 则返回输出字符串 * @param string $label * 标签 默认为空 * @param boolean $strict * 是否严谨 默认为true * * @return void|string */ function dump($var, $echo = true, $label = null, $strict = true) { $label = ($label === null) ? '' : rtrim($label) . ' '; if (!$strict) { if (ini_get('html_errors')) { $output = print_r($var, true); $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>'; } else { $output = $label . print_r($var, true); } } else { ob_start(); var_dump($var); $output = ob_get_clean(); if (!extension_loaded('xdebug')) { $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output); $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>'; } } if ($echo) { echo($output); return null; } else { return $output; } } /** * 设置当前页面的布局 * * @param string|false $layout * 布局名称 为false的时候表示关闭布局 * * @return void */ function layout($layout) { if (false !== $layout) { // 开启布局 C('LAYOUT_ON', true); if (is_string($layout)) { // 设置新的布局模板 C('LAYOUT_NAME', $layout); } } else { // 临时关闭布局 C('LAYOUT_ON', false); } } /** * URL组装 支持不同URL模式 * * @param string $url URL表达式,格式:'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...' * @param string|array $vars 传入的参数,支持数组和字符串 * @param string|boolean $suffix 伪静态后缀,默认为true表示获取配置值 * @param boolean $domain 是否显示域名 * * @return string */ function U($url = '', $vars = '', $suffix = true, $domain = false) { // 解析URL $info = parse_url($url); $url = !empty($info['path']) ? $info['path'] : ACTION_NAME; if (isset($info['fragment'])) { // 解析锚点 $anchor = $info['fragment']; if (false !== strpos($anchor, '?')) { // 解析参数 list ($anchor, $info['query']) = explode('?', $anchor, 2); } if (false !== strpos($anchor, '@')) { // 解析域名 list ($anchor, $host) = explode('@', $anchor, 2); } } elseif (false !== strpos($url, '@')) { // 解析域名 list ($url, $host) = explode('@', $info['path'], 2); } // 解析子域名 if (isset($host)) { $domain = $host . (strpos($host, '.') ? '' : strstr($_SERVER['HTTP_HOST'], '.')); } elseif ($domain === true) { $domain = $_SERVER['HTTP_HOST']; if (C('APP_SUB_DOMAIN_DEPLOY')) { // 开启子域名部署 $domain = $domain == 'localhost' ? 'localhost' : 'www' . strstr($_SERVER['HTTP_HOST'], '.'); // '子域名'=>array('模块[/控制器]'); foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) { $rule = is_array($rule) ? $rule[0] : $rule; if (false === strpos($key, '*') && 0 === strpos($url, $rule)) { $domain = $key . strstr($domain, '.'); // 生成对应子域名 $url = substr_replace($url, '', 0, strlen($rule)); break; } } } } // 解析参数 if (is_string($vars)) { // aaa=1&bbb=2 转换成数组 parse_str($vars, $vars); } elseif (!is_array($vars)) { $vars = array(); } if (isset($info['query'])) { // 解析地址里面参数 合并到vars parse_str($info['query'], $params); $vars = array_merge($params, $vars); } // URL组装 $depr = C('URL_PATHINFO_DEPR'); $urlCase = C('URL_CASE_INSENSITIVE'); if ($url) { if (0 === strpos($url, '/')) { // 定义路由 $route = true; $url = substr($url, 1); if ('/' != $depr) { $url = str_replace('/', $depr, $url); } } else { if ('/' != $depr) { // 安全替换 $url = str_replace('/', $depr, $url); } // 解析模块、控制器和操作 $url = trim($url, $depr); $path = explode($depr, $url); $var = array(); $varModule = C('VAR_MODULE'); $varController = C('VAR_CONTROLLER'); $varAction = C('VAR_ACTION'); $var[$varAction] = !empty($path) ? array_pop($path) : ACTION_NAME; $var[$varController] = !empty($path) ? array_pop($path) : CONTROLLER_NAME; $maps = C('URL_ACTION_MAP'); if ($maps) { if (isset($maps[strtolower($var[$varController])])) { $maps = $maps[strtolower($var[$varController])]; $action = array_search(strtolower($var[$varAction]), $maps); if ($action) { $var[$varAction] = $action; } } } $maps = C('URL_CONTROLLER_MAP'); if ($maps) { $controller = array_search(strtolower($var[$varController]), $maps); if ($controller) { $var[$varController] = $controller; } } if ($urlCase) { $var[$varController] = parse_name($var[$varController]); } $module = ''; if (!empty($path)) { $var[$varModule] = implode($depr, $path); } else { if (C('MULTI_MODULE')) { if (MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')) { $var[$varModule] = MODULE_NAME; } } } $maps = C('URL_MODULE_MAP'); if ($maps) { $_module = array_search(strtolower($var[$varModule]), $maps); if ($_module) { $var[$varModule] = $_module; } } if (isset($var[$varModule])) { $module = $var[$varModule]; unset($var[$varModule]); } } } if (C('URL_MODEL') == 0) { // 普通模式URL转换 $url = __APP__ . '?' . C('VAR_MODULE') . "={$module}&" . http_build_query(array_reverse($var)); if ($urlCase) { $url = strtolower($url); } if (!empty($vars)) { $vars = http_build_query($vars); $url .= '&' . $vars; } } else { // PATHINFO模式或者兼容URL模式 if (isset($route)) { $url = __APP__ . '/' . rtrim($url, $depr); } else { $module = (defined('BIND_MODULE') && BIND_MODULE == $module) ? '' : $module; $url = __APP__ . '/' . ($module ? $module . MODULE_PATHINFO_DEPR : '') . implode($depr, array_reverse($var)); } if ($urlCase) { $url = strtolower($url); } if (!empty($vars)) { // 添加参数 foreach ($vars as $var => $val) { if ('' !== trim($val)) { $url .= $depr . $var . $depr . urlencode($val); } } } if ($suffix) { $suffix = $suffix === true ? C('URL_HTML_SUFFIX') : $suffix; $pos = strpos($suffix, '|'); if ($pos) { $suffix = substr($suffix, 0, $pos); } if ($suffix && '/' != substr($url, -1)) { $url .= '.' . ltrim($suffix, '.'); } } } if (isset($anchor)) { $url .= '#' . $anchor; } if ($domain) { $url = (is_ssl() ? 'https://' : 'http://') . $domain . $url; } return $url; } /** * 渲染输出Widget * * @param string $name Widget名称 * @param array $data 传入的参数 * * @return void */ function W($name, $data = array()) { return R($name, $data, 'Widget'); } /** * 判断是否SSL协议 * * @return boolean */ function is_ssl() { if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) { return true; } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) { return true; } elseif (stripos(cfg('PROTOCAL'), 'https') !== false) { return true; } return false; } /** * URL重定向 * * @param string $url 重定向的URL地址 * @param integer $time 重定向的等待时间(秒) * @param string $msg 重定向前的提示信息 * * @return void */ function redirect($url, $time = 0, $msg = '') { // 多行URL地址支持 $url = str_replace(array( "\n", "\r" ), '', $url); if (empty($msg)) { $msg = "系统将在{$time}秒之后自动跳转到{$url}!"; } if (!headers_sent()) { // redirect if (0 === $time) { header('Location: ' . $url); } else { header("refresh:{$time};url={$url}"); echo($msg); } exit(); } else { $str = "<meta http-equiv='Refresh' content='{$time};URL={$url}'>"; if ($time != 0) { $str .= $msg; } exit($str); } } /** * 缓存管理 * * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 * @param mixed $value 缓存值 * @param mixed $options 缓存参数 * * @return mixed */ function S($name, $value = '', $options = null) { static $cache = ''; if (is_array($options)) { // 缓存操作的同时初始化 $type = isset($options['type']) ? $options['type'] : ''; $cache = Think\Cache::getInstance($type, $options); } elseif (is_array($name)) { // 缓存初始化 $type = isset($name['type']) ? $name['type'] : ''; $cache = Think\Cache::getInstance($type, $name); return $cache; } elseif (empty($cache)) { // 自动初始化 $cache = Think\Cache::getInstance(); } if ('' === $value) { // 获取缓存 return $cache->get($name); } elseif (is_null($value)) { // 删除缓存 return $cache->rm($name); } else { // 缓存数据 if (is_array($options)) { $expire = isset($options['expire']) ? $options['expire'] : null; } else { $expire = is_numeric($options) ? $options : null; } // 写入缓存名称数据表时去掉应用标识, Public 没有表, 所以过滤 if (APP_DIR != 'Public') { call_user_func('syscache_record', $name); } return $cache->set($name, $value, $expire); } } /** * 快速文件数据读取和保存 针对简单类型数据 字符串、数组 * * @param string $name 缓存名称 * @param mixed $value 缓存值 * @param string $path 缓存路径 * * @return mixed */ function F($name, $value = '', $path = DATA_PATH) { static $_cache = array(); $filename = $path . $name . '.php'; if ('' !== $value) { if (is_null($value)) { // 删除缓存 if (false !== strpos($name, '*')) { return false; } else { unset($_cache[$name]); return Think\Storage::unlink($filename, 'F'); } } else { Think\Storage::put($filename, serialize($value), 'F'); // 缓存数据 $_cache[$name] = $value; return null; } } // 获取缓存数据 if (isset($_cache[$name])) { return $_cache[$name]; } if (Think\Storage::has($filename, 'F')) { $value = unserialize(Think\Storage::read($filename, 'F')); $_cache[$name] = $value; } else { $value = false; } return $value; } /** * 记录缓存 * * @param string $cachename 缓存名称 * * @return boolean */ function syscache_record($cachename) { static $s_caches = null; $serv_sys = D('Common/Syscache', 'Service'); if (null == $s_caches) { $s_caches = $serv_sys->list_all(); if (!empty($s_caches)) { $s_caches = array_combine_by_key($s_caches, 'name'); } } // 如果不存在, 则 if (empty($s_caches[$cachename])) { // 记录缓存 $cache = array('name' => $cachename, 'type' => 1, 'data' => ''); $s_caches[$cachename] = $cache; // 数据入库 $serv_sys->insert($cache); } return true; } /** * 根据PHP各种类型变量生成唯一标识号 * * @param mixed $mix * 变量 * * @return string */ function to_guid_string($mix) { if (is_object($mix)) { return spl_object_hash($mix); } elseif (is_resource($mix)) { $mix = get_resource_type($mix) . strval($mix); } else { $mix = serialize($mix); } return md5($mix); } /** * XML编码 * * @param mixed $data * 数据 * @param string $root * 根节点名 * @param string $item * 数字索引的子节点名 * @param string $attr * 根节点属性 * @param string $id * 数字索引子节点key转换的属性名 * @param string $encoding * 数据编码 * * @return string */ function xml_encode($data, $root = 'think', $item = 'item', $attr = '', $id = 'id', $encoding = 'utf-8') { if (is_array($attr)) { $_attr = array(); foreach ($attr as $key => $value) { $_attr[] = "{$key}=\"{$value}\""; } $attr = implode(' ', $_attr); } $attr = trim($attr); $attr = empty($attr) ? '' : " {$attr}"; $xml = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>"; $xml .= "<{$root}{$attr}>"; $xml .= data_to_xml($data, $item, $id); $xml .= "</{$root}>"; return $xml; } /** * 数据XML编码 * * @param mixed $data * 数据 * @param string $item * 数字索引时的节点名称 * @param string $id * 数字索引key转换为的属性名 * * @return string */ function data_to_xml($data, $item = 'item', $id = 'id') { $xml = $attr = ''; foreach ($data as $key => $val) { if (is_numeric($key)) { $id && $attr = " {$id}=\"{$key}\""; $key = $item; } $xml .= "<{$key}{$attr}>"; $xml .= (is_array($val) || is_object($val)) ? data_to_xml($val, $item, $id) : $val; $xml .= "</{$key}>"; } return $xml; } /** * session管理函数 * * @param string|array $name * session名称 如果为数组则表示进行session设置 * @param mixed $value * session值 * * @return mixed */ function session($name = '', $value = '') { $prefix = C('SESSION_PREFIX'); if (is_array($name)) { // session初始化 在session_start 之前调用 if (isset($name['prefix'])) { C('SESSION_PREFIX', $name['prefix']); } if (C('VAR_SESSION_ID') && isset($_REQUEST[C('VAR_SESSION_ID')])) { session_id($_REQUEST[C('VAR_SESSION_ID')]); } elseif (isset($name['id'])) { session_id($name['id']); } if ('common' == APP_MODE) { // 其它模式可能不支持 ini_set('session.auto_start', 0); } if (isset($name['name'])) { session_name($name['name']); } if (isset($name['path'])) { session_save_path($name['path']); } if (isset($name['domain'])) { ini_set('session.cookie_domain', $name['domain']); } if (isset($name['expire'])) { ini_set('session.gc_maxlifetime', $name['expire']); ini_set('session.cookie_lifetime', $name['expire']); } if (isset($name['use_trans_sid'])) { ini_set('session.use_trans_sid', $name['use_trans_sid'] ? 1 : 0); } if (isset($name['use_cookies'])) { ini_set('session.use_cookies', $name['use_cookies'] ? 1 : 0); } if (isset($name['cache_limiter'])) { session_cache_limiter($name['cache_limiter']); } if (isset($name['cache_expire'])) { session_cache_expire($name['cache_expire']); } if (isset($name['type'])) { C('SESSION_TYPE', $name['type']); } if (C('SESSION_TYPE')) { // 读取session驱动 $type = C('SESSION_TYPE'); $class = strpos($type, '\\') ? $type : 'Think\\Session\\Driver\\' . ucwords(strtolower($type)); $hander = new $class(); session_set_save_handler(array( &$hander, "open" ), array( &$hander, "close" ), array( &$hander, "read" ), array( &$hander, "write" ), array( &$hander, "destroy" ), array( &$hander, "gc" )); } // 启动session if (C('SESSION_AUTO_START')) { session_start(); } } elseif ('' === $value) { if ('' === $name) { // 获取全部的session return $prefix ? $_SESSION[$prefix] : $_SESSION; } elseif (0 === strpos($name, '[')) { // session 操作 if ('[pause]' == $name) { // 暂停session session_write_close(); } elseif ('[start]' == $name) { // 启动session session_start(); } elseif ('[destroy]' == $name) { // 销毁session $_SESSION = array(); session_unset(); session_destroy(); } elseif ('[regenerate]' == $name) { // 重新生成id session_regenerate_id(); } } elseif (0 === strpos($name, '?')) { // 检查session $name = substr($name, 1); if (strpos($name, '.')) { // 支持数组 list ($name1, $name2) = explode('.', $name); return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]); } else { return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]); } } elseif (is_null($name)) { // 清空session if ($prefix) { unset($_SESSION[$prefix]); } else { $_SESSION = array(); } } elseif ($prefix) { // 获取session if (strpos($name, '.')) { list ($name1, $name2) = explode('.', $name); return isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; } else { return isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null; } } else { if (strpos($name, '.')) { list ($name1, $name2) = explode('.', $name); return isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; } else { return isset($_SESSION[$name]) ? $_SESSION[$name] : null; } } } elseif (is_null($value)) { // 删除session if (strpos($name, '.')) { list ($name1, $name2) = explode('.', $name); if ($prefix) { unset($_SESSION[$prefix][$name1][$name2]); } else { unset($_SESSION[$name1][$name2]); } } else { if ($prefix) { unset($_SESSION[$prefix][$name]); } else { unset($_SESSION[$name]); } } } else { // 设置session if (strpos($name, '.')) { list ($name1, $name2) = explode('.', $name); if ($prefix) { $_SESSION[$prefix][$name1][$name2] = $value; } else { $_SESSION[$name1][$name2] = $value; } } else { if ($prefix) { $_SESSION[$prefix][$name] = $value; } else { $_SESSION[$name] = $value; } } } return null; } /** * Cookie 设置、获取、删除 * * @param string $name * cookie名称 * @param mixed $value * cookie值 * @param mixed $option * cookie参数 * * @return mixed */ function cookie($name = '', $value = '', $option = null) { // 默认设置 $config = array( 'prefix' => C('COOKIE_PREFIX'), // cookie 名称前缀 'expire' => C('COOKIE_EXPIRE'), // cookie 保存时间 'path' => C('COOKIE_PATH'), // cookie 保存路径 'domain' => C('COOKIE_DOMAIN'), // cookie 有效域名 'secure' => C('COOKIE_SECURE'), // cookie 启用安全传输 'httponly' => C('COOKIE_HTTPONLY') ); // httponly设置 // 参数设置(会覆盖黙认设置) if (!is_null($option)) { if (is_numeric($option)) { $option = array( 'expire' => $option ); } elseif (is_string($option)) { parse_str($option, $option); } $config = array_merge($config, array_change_key_case($option)); } if (!empty($config['httponly'])) { ini_set("session.cookie_httponly", 1); } // 清除指定前缀的所有cookie if (is_null($name)) { if (empty($_COOKIE)) { return null; } // 要删除的cookie前缀,不指定则删除config设置的指定前缀 $prefix = empty($value) ? $config['prefix'] : $value; if (!empty($prefix)) { // 如果前缀为空字符串将不作处理直接返回 foreach ($_COOKIE as $key => $val) { if (0 === stripos($key, $prefix)) { setcookie($key, '', time() - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); unset($_COOKIE[$key]); } } } return null; } elseif ('' === $name) { // 获取全部的cookie return $_COOKIE; } $name = $config['prefix'] . str_replace('.', '_', $name); if ('' === $value) { if (isset($_COOKIE[$name])) { $value = $_COOKIE[$name]; if (0 === strpos($value, 'think:')) { $value = substr($value, 6); return array_map('urldecode', json_decode(MAGIC_QUOTES_GPC ? stripslashes($value) : $value, true)); } else { return $value; } } else { return null; } } else { if (is_null($value)) { setcookie($name, '', time() - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); unset($_COOKIE[$name]); // 删除指定cookie } else { // 设置cookie if (is_array($value)) { $value = 'think:' . json_encode(array_map('urlencode', $value)); } $expire = !empty($config['expire']) ? time() + intval($config['expire']) : 0; setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']); $_COOKIE[$name] = $value; } } return null; } /** * 加载动态扩展文件 * * @var string $path 文件路径 * @return void */ function load_ext_file($path) { // 加载自定义外部文件 $files = C('LOAD_EXT_FILE'); if ($files) { $files = explode(',', $files); foreach ($files as $file) { $file = $path . 'Common/' . $file . '.php'; if (is_file($file)) { include $file; } elseif ('___' == substr($file, -3)) { // add by zhuxun37, 系统配置文件的特殊标记字符"___" $commonFile = ROOT_PATH . 'Common/Common/Common/' . substr($file, 0, -3) . '.php'; if (is_file($commonFile)) { include $commonFile; } } } } // 加载自定义的动态配置文件 $configs = C('LOAD_EXT_CONFIG'); if ($configs) { if (is_string($configs)) { $configs = explode(',', $configs); } foreach ($configs as $key => $config) { $file = is_file($config) ? $config : $path . 'Conf/' . $config . CONF_EXT; if (is_file($file)) { is_numeric($key) ? C(load_config($file)) : C($key, load_config($file)); } elseif ('___' == substr($file, -3)) { // add by zhuxun37, 系统配置文件的特殊标记字符"___" $commonFile = ROOT_PATH . 'Common/Common/Conf/' . substr($config, 0, -3) . CONF_EXT; if (is_file($commonFile)) { is_numeric($key) ? C(load_config($commonFile)) : C($key, load_config($commonFile)); } } } } } /** * 获取客户端IP地址 * * @param integer $type * 返回类型 0 返回IP地址 1 返回IPV4地址数字 * @param boolean $adv * 是否进行高级模式获取(有可能被伪装) * * @return mixed */ function get_client_ip($type = 0, $adv = false) { $type = $type ? 1 : 0; static $ip = null; if ($ip !== null) { return $ip[$type]; } if ($adv) { if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $pos = array_search('unknown', $arr); if (false !== $pos) { unset($arr[$pos]); } $ip = trim($arr[0]); } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } } elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } // IP地址合法验证 $long = sprintf("%u", ip2long($ip)); $ip = $long ? array( $ip, $long ) : array( '0.0.0.0', 0 ); return $ip[$type]; } /** * 发送HTTP状态 * * @param integer $code * 状态码 * * @return void */ function send_http_status($code) { static $_status = array( // Informational 1xx 100 => 'Continue', 101 => 'Switching Protocols', // Success 2xx 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', // Redirection 3xx 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Moved Temporarily ', // 1.1 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', // 306 is deprecated but reserved 307 => 'Temporary Redirect', // Client Error 4xx 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 Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', // Server Error 5xx 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 509 => 'Bandwidth Limit Exceeded' ); if (isset($_status[$code])) { header('HTTP/1.1 ' . $code . ' ' . $_status[$code]); // 确保FastCGI模式下正常 header('Status:' . $code . ' ' . $_status[$code]); } } function think_filter(&$value) { // 过滤查询特殊字符 if (preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { $value .= ' '; } } // 不区分大小写的in_array实现 function in_array_case($value, $array) { return in_array(strtolower($value), array_map('strtolower', $array)); } // by zhuxun, begin. /** * 获取和设置配置参数 支持批量定义 * * @param string|array $name * 配置变量 * @param mixed $value * 配置值 * @param mixed $default * 默认值 * * @return mixed */ function cfg($name = null, $value = null, $default = null) { return C($name, $value, $default); } /** * 把字串中字母转成小写(重写 rstrtolower); * * @param string $str 字串; * * @return string */ function rstrtolower($str) { return strtr($str, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"); } /** * 把字串中字母转成大写(重写 rstrtoupper); * * @param string $str 字串; * * @return string; */ function rstrtoupper($str) { return strtr($str, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); } /** * 重写 base64_encode 方法 * * @param string $string * 字串 * * @return mixed */ function rbase64_encode($string) { $data = base64_encode($string); $data = str_replace(array( '+', '/', '=' ), array( '-', '_', '' ), $data); return $data; } /** * 重写 base64_decode 方法 * * @param string $string * 字串 * * @return string */ function rbase64_decode($string) { $data = str_replace(array( '-', '_' ), array( '+', '/' ), $string); $mod4 = strlen($data) % 4; if ($mod4) { $data .= substr('====', $mod4); } return base64_decode($data); } /** * 把数据转成数字 * * @param * $int 传入的数据 * @param bool $allowarray 是否允许数组 * * @return int */ function rintval($int, $allowarray = false) { // 如果是标量 $ret = is_scalar($int) ? intval($int) : 0; if ($int == $ret || (!$allowarray && is_array($int))) { return $ret; } // 如果允许数组 if ($allowarray && is_array($int)) { // 遍历数组 foreach ($int as &$v) { $v = rintval($v, true); } return $int; } elseif ($int <= 0xffffffff) { // 如果数值小于32位 $l = strlen($int); $m = substr($int, 0, 1) == '-' ? 1 : 0; // 是正常的数值 if (($l - $m) === strspn($int, '0987654321', $m)) { return $int; } } return $ret; } /** * 创建目录 * * @param string $dir 目录 * @param int $mode 权限字串 * @param boolean $makeindex 是否创建默认索引文件 * * @return bool */ function rmkdir($dir, $mode = 0777, $makeindex = true) { if (!is_dir($dir)) { // 如果非目录 rmkdir(dirname($dir), $mode, $makeindex); @mkdir($dir, $mode); // 如果需要创建索引文件 if (!empty($makeindex)) { @touch($dir . '/index.html'); @chmod($dir . '/index.html', 0777); } } return true; } /** * 删除反斜杠 * * @param mixed $string * 待处理数据 * * @return array|string */ function rstripslashes($string) { // 如果为空或是数字 if (empty($string) || is_numeric($string)) { return $string; } if (is_array($string)) { // 如果是数组 // 遍历数据, 逐个过滤 foreach ($string as $key => $val) { $string[$key] = rstripslashes($val); } } else { // 如果是字串 $string = stripslashes($string); } return $string; } /** * 数据过滤 * * @param mixed $string * 待处理数据 * * @return mixed */ function raddslashes($string) { if (is_array($string)) { // 如果是数组 // 先取出所有键值 $keys = array_keys($string); // 遍历整个数组 foreach ($keys as $key) { $val = $string[$key]; unset($string[$key]); $string[addslashes($key)] = raddslashes($val); } } else { // 如果是字串 $string = addslashes($string); } return $string; } /** * 把一些预定义字符转成HTML实体 * * @param mixed $string * 待处理数据 * @param string $flags * 如何编码单/双引号 * * @return mixed */ function rhtmlspecialchars($string, $flags = null) { if (is_array($string)) { // 如果是数组 // 遍历数组, 嵌套处理 foreach ($string as $key => $val) { $string[$key] = rhtmlspecialchars($val, $flags); } } else { // 如果是数值 if (is_numeric($string)) { return $string; } // 如果未如何编码单/双引号, 则 if ($flags === null) { // 正则替换 $searchs = array( '&', '"', '<', '>' ); $replaces = array( '&', '"', '<', '>' ); $string = str_replace($searchs, $replaces, $string); if (strpos($string, '&#') !== false) { $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string); } } else { // 判断 PHP 版本 if (PHP_VERSION < '5.4.0') { $string = htmlspecialchars($string, $flags); } else { if (strtolower(CHARSET) == 'utf-8') { $charset = 'UTF-8'; } else { $charset = 'ISO-8859-1'; } $string = htmlspecialchars($string, $flags, $charset); } } } return $string; } /** * 时间转换为毫秒 * * @param int $ts 时间戳 * * @return int */ function to_milli_time($ts) { if (0 >= ($ts >> 32)) { $ts = $ts * 1000; } return $ts; } /** * 时间转换为秒 * * @param int $ts 时间戳 * * @return int */ function to_second_time($ts) { if (0 < ($ts >> 32)) { $ts = $ts / 1000; } return $ts; } /** * 把时间戳转换成指定格式 * * @param int $timestamp 时间戳 * @param string $format 格式 * @param string $timeoffset 时区 * @param string $uformat * * @return string; */ function rgmdate($timestamp, $format = 'zx', $timeoffset = '9999', $uformat = '') { static $dformat, $tformat, $dtformat, $offset, $lang; // 如果未指定格式 if ($dformat === null) { $dformat = cfg('DATE_FORMAT'); $tformat = cfg('TIME_FORMAT'); $dtformat = $dformat . ' ' . $tformat; $lang = L('DATE'); $offset = cfg('TIME_OFFSET'); // 默认时区为 +8 if (!$offset) { $offset = 8; } } // 时间戳转换为秒为单位 $timestamp = to_second_time($timestamp); // 剔除首尾空格 $format = trim($format); // 时区偏移 $timeoffset = $timeoffset == 9999 ? $offset : $timeoffset; $timestamp += $timeoffset * 3600; // 判断用户所需格式 $format = (empty($format) || $format == 'zx') ? $dtformat : ($format == 'z' ? $dformat : ($format == 'x' ? $tformat : $format)); if ($format == 'u') { // 今天的起始时间戳(即: 00:00:00) $today_ts = NOW_TIME - (NOW_TIME + $timeoffset * 3600) % 86400 + $timeoffset * 3600; $s = gmdate(!$uformat ? $dtformat : $uformat, $timestamp); // 今天已逝去的时间 $time = NOW_TIME + $timeoffset * 3600 - $timestamp; // 如果当前时间戳就是当天之内 if ($timestamp >= $today_ts) { if ($time > 3600) { // 如果在1个小时以上 return intval($time / 3600) . ' ' . $lang['HOUR'] . $lang['BEFORE']; } elseif ($time > 1800) { // 如果在半个小时以上 return $lang['HALF'] . $lang['HOUR'] . $lang['BEFORE']; } elseif ($time > 60) { // 如果在1分钟以上 return intval($time / 60) . ' ' . $lang['MIN'] . $lang['BEFORE']; } elseif ($time > 0) { // 如果在1分钟以内 return $time . ' ' . $lang['SEC'] . $lang['BEFORE']; } elseif ($time == 0) { // 当前 return $lang['NOW']; } else { return $s; } } elseif (($days = intval(($today_ts - $timestamp) / 86400)) >= 0 && $days < 7) { // 一周之内 if ($days == 0) { // 当天 return $lang['YDAY'] . ' ' . gmdate($tformat, $timestamp); } elseif ($days == 1) { // 昨天 return $lang['BYDAY'] . ' ' . gmdate($tformat, $timestamp); } else { return ($days + 1) . ' ' . $lang['DAY'] . $lang['BEFORE']; } } else { return $s; } } else { // 默认时间格式 $format = trim($format); if (!$format) { $format = 'Y-m-d H:i'; } return gmdate($format, $timestamp); } } /** * 把数组合并成一个字串(会过滤单/双引号) * * @param array $array 数组 * * @return string|number */ function rimplode($array) { if (!empty($array)) { $array = array_map('addslashes', $array); return "'" . implode("','", is_array($array) ? $array : array( $array )) . "'"; } else { return 0; } } /** * 生成随机字串 * * @param int $length 随机字串长度 * @param int $numeric 是否为数字字串 * * @return bool */ function random($length, $numeric = 0) { // 生成随机数种子 $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . rstrtoupper($seed)); // 如果只是需要数字 if ($numeric) { $hash = ''; } else { // 生成字串 // 随机生成一个首字母 $hash = chr(rand(1, 26) + rand(0, 1) * 32 + 64); $length--; } $max = strlen($seed) - 1; // 逐个生成字符 for ($i = 0; $i < $length; $i++) { $hash .= $seed{mt_rand(0, $max)}; } return $hash; } /** * 判断文件或是路径是否可写; * * @param string $filepath * * @return bool 可写则返回 true; */ function ris_writable($filepath) { $unlink = false; // 判断是否以 / 结尾 '/' == substr($filepath, -1) && $filepath = substr($filepath, 0, -1); // 如果是目录 if (is_dir($filepath)) { $unlink = true; mt_srand((double)microtime() * 1000000); $filepath = $filepath . '/vcy_' . uniqid(mt_rand()) . '.tmp'; } // 打开文件 $fp = @fopen($filepath, 'ab'); // 失败, 则返回 false if (false === $fp) { return false; } // 关闭文件 fclose($fp); // 删除临时测试文件 if ($unlink) { @unlink($filepath); } return true; } /** * 读取指定文件; * * @param string $filename * @param string $method * * @return string 返回读取的数据; */ function rfread($filename, $method = 'rb') { check_filepath($filename); $filedata = ''; // 如果打开成功 $handle = @fopen($filename, $method); if ($handle) { flock($handle, LOCK_SH); $filedata = @fread($handle, filesize($filename)); fclose($handle); } return $filedata; } /** * 把数据写入文件; * * @param string $filename 文件名 * @param string $data 数据 * @param string $method fopen参数 * @param boolean $iflock 是否需要锁 * @param boolean $check 是否检查文件路径 * @param boolean $chmod 是否修改权限 * * @return bool */ function rfwrite($filename, $data, $method = 'rb+', $iflock = true, $check = true, $chmod = true) { $check && check_filepath($filename); // 判断文件是否可写 if (false == ris_writable($filename)) { \Think\Log::record('Can not write to cache files, please check directory ' . $filename . ' .'); } // 生成文件 touch($filename); // 打开文件 $handle = fopen($filename, $method); // 如果需要文件锁, 则 $iflock && flock($handle, LOCK_EX); // 写入数据 fwrite($handle, $data); $method == 'rb+' && ftruncate($handle, strlen($data)); // 关闭文件 fclose($handle); // 修改权限 $chmod && @chmod($filename, 0777); return true; } /** * 检查文件的路径是否合法; * * @param string $filename * @param boolean $ifcheck * -> 可能会造成跨目录的BUG); * * @return string 返回文件路径; */ function check_filepath($filename, $ifcheck = true) { $tmpname = strtolower($filename); $arr = array( 'http://' ); // 剔除掉 .. $ifcheck && array_push($arr, '..'); if (str_replace($arr, '', $tmpname) != $tmpname) { E('_ERROR_FORBIDDEN_'); return false; } return $filename; } /** * 格式化文件的大小; * * @param int $filesize * * @return string 返回格式化后的字串; */ function size_count($filesize) { if (1073741824 <= $filesize) { $filesize = (round($filesize / 1073741824 * 100) / 100) . ' G'; } elseif (1048576 <= $filesize) { $filesize = (round($filesize / 1048576 * 100) / 100) . ' M'; } elseif (1024 <= $filesize) { $filesize = (round($filesize / 1024 * 100) / 100) . ' K'; } else { $filesize = $filesize . ' bytes'; } return $filesize; } /** * 文件尺寸描述转为bytes整数值 * * @param string $val * * @return number */ function count_size($val) { $unit = ''; $value = 0; // 如果不是数字 if (preg_match('/^(\d+)(.*)/s', trim($val), $match)) { $unit = strtolower(trim($match[2])); $value = intval(trim($match[1])); } // 如果单位是 bytes if ($unit == 'bytes' || empty($unit)) { return $value; } // 这里需要注意字节转换的代码写法!!! switch ($unit) { case 't': $value *= 1024; case 'g': $value *= 1024; case 'm': $value *= 1024; case 'k': $value *= 1024; } return $value; } /** * 计算目录的大小; * * @param string $dir 目录 * * @return int */ function dir_size($dir) { // 打开目录 $dh = opendir($dir); $size = 0; // 遍历所有目录 $file = readdir($dh); while ($file) { // 如果是当前/上级目录 if ($file != '.' && $file != '..') { $path = $dir . "/" . $file; // 如果是目录 if (@is_dir($path)) { $size += dir_size($path); } else { // 非目录, 则直接计算大小 $size += filesize($path); } } } @closedir($dh); return $size; } /** * 转换编码 * * @param mixed $var * @param string $from * @param string $to * * @return mixed */ function riconv($var, $from = 'UTF-8', $to = 'GBK') { // 如果没有指定默认处理方法, 则 if (strpos($to, '//') === false) { $to = $to . '//IGNORE'; } switch (gettype($var)) { case 'integer': case 'boolean': case 'float': case 'double': case 'NULL': return $var; case 'string': return @iconv($from, $to, $var); case 'object': // 如果为对象, 则取出所有值 $vars = array_keys(get_object_vars($var)); // 遍历成员 foreach ($vars as $key) { $var->$key = riconv($var->$key, $from, $to); } return $var; case 'array': // 遍历数组 foreach ($var as $k => $v) { $k2 = riconv($k, $from, $to); // 如果键值和转换后的不相等, 则剔除 if ($k != $k2) { unset($var[$k]); } $var[$k2] = riconv($v, $from, $to); } return $var; default: return ''; } } /** * json_encode2 将变量转为 json 编码字符串 * * @param mixed $value * @param int $options * 同内置函数的第二个参数参量,默认为:0,或JSON_*_* * * @return string */ function rjson_encode($value, $options = 0) { // 如果数据为对象 if (is_object($value)) { $value = get_object_vars($value); } $value = _urlencode($value); $json = json_encode($value, $options); return urldecode($json); } /** * _urlencode urlencode 字符串或数组 * 注意: 本函数其实只是用于json_encode2,如果php版本>=5.3的话, 建议用闭包实现,这样就不用将此函数暴露在全局中 * * @param string|array $value * * @return string|array */ function _urlencode($value) { // 如果数据为数组 if (is_array($value)) { foreach ($value as $k => $v) { $_k = _urlencode($k); if ($_k != $k) { unset($value[$k]); } $value[$_k] = _urlencode($v); } } else if (is_string($value)) { $searchs = array( "\\", "\r\n", "\r", "\n", "\"", "\/", "\t" ); $replaces = array( '\\\\', '\\n', '\\n', '\\n', '\\"', '\\/', '\\t' ); $value = urlencode(str_replace($searchs, $replaces, $value)); } return $value; } /** * 字符截取 * * @param string $string 数据字串 * @param number $length 需要的长度 * @param string $dot 末尾剪切时的后缀 * @param string $charset * * @return string */ function rsubstr($string, $length, $dot = ' ...', $charset = null) { // 如果长度小于指定长度 if (strlen($string) <= $length) { return $string; } // 字符集为空时, 默认为 UTF8 if ($charset === null) { $charset = 'utf-8'; } // 标记空格/逗号/左右尖括号 $pre = chr(1); $end = chr(1); $searchs = array( '&', '"', '<', '>' ); $replaces = array( $pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end ); $string = str_replace($searchs, $replaces, $string); $strcut = ''; // 如果字符集为 UTF8 if (strtolower($charset) == 'utf-8') { $n = $tn = $noc = 0; // 遍历 while ($n < strlen($string)) { // 获取当前位置的ASCII码值 $t = ord($string[$n]); // 如果为标准的ASCII码 if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) { $tn = 1; $n++; $noc++; } elseif (194 <= $t && $t <= 223) { // 双字节字符 $tn = 2; $n += 2; $noc += 2; } elseif (224 <= $t && $t <= 239) { // 三字节字符 $tn = 3; $n += 3; $noc += 2; } elseif (240 <= $t && $t <= 247) { // 四字节字符 $tn = 4; $n += 4; $noc += 2; } elseif (248 <= $t && $t <= 251) { // 五字节字符 $tn = 5; $n += 5; $noc += 2; } elseif ($t == 252 || $t == 253) { // 六字节字符 $tn = 6; $n += 6; $noc += 2; } else { // 非正常字符 $n++; } // 如果当前长度已经达到需要的长度 if ($noc >= $length) { break; } } if ($noc > $length) { $n -= $tn; } // 剪切字串 $strcut = substr($string, 0, $n); } else { // 根据长度剪切 for ($i = 0; $i < $length; $i++) { $strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i]; } } // 换回标记 $searchs = array( $pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end ); $replaces = array( '&', '"', '<', '>' ); $strcut = str_replace($searchs, $replaces, $strcut); $pos = strrpos($strcut, chr(1)); // 如果字串中还包含了标记字符, 则 if ($pos !== false) { $strcut = substr($strcut, 0, $pos); } return $strcut . $dot; } /** * 转换文本格式的时间为本地时间戳 * * @param string $datetime 时间字串 * @param int $precision 要求精度, 0: 秒; 1: 毫秒 * * @return number */ function rstrtotime($datetime, $precision = 1) { $timestamp = strtotime($datetime); // 如果时间戳不正确 if (-1 === $timestamp || false === $timestamp) { return 0; } // 判断是否含有时区标识 if (!preg_match("/(GMT|UTC)/i", $datetime) && !preg_match("/T(.*?)Z$/i", $datetime)) { // 获取时区配置 $offset = (int)cfg('TIME_OFFSET'); $ymdhis = date('Y m d H i s', $timestamp); list ($y, $m, $d, $h, $i, $s) = explode(' ', $ymdhis); $timestamp = gmmktime($h, $i, $s, $m, $d, $y) - 3600 * $offset; } return 0 == $precision ? $timestamp : ($timestamp * 1000); } /** * 认证字符串的加密和解密函数 * * @param string $string 待处理的字符串 * @param string $key 加密密钥 * @param string $operation 操作方式:DECODE=解密,ENCODE=加密 * @param int $expiry 过期时间。单位:秒。0=不过期 * * @return boolean|string */ function authcode($string, $key, $operation = 'DECODE', $expiry = 0) { // 如果秘钥为空, 则 if ($key == '') { return false; } $ckey_length = 4; $key = md5($key); $keya = md5(substr($key, 0, 16)); $keyb = md5(substr($key, 16, 16)); $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey = $keya . md5($keya . $keyc); $key_length = strlen($cryptkey); $string = $operation == 'DECODE' ? rbase64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string; $string_length = strlen($string); $result = ''; $box = range(0, 255); $rndkey = array(); for ($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for ($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } for ($a = $j = $i = 0; $i < $string_length; $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])); } if ($operation == 'DECODE') { if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) { return substr($result, 26); } else { return ''; } } else { return $keyc . str_replace('=', '', rbase64_encode($result)); } } /** * 替换preg_replace 函数,兼容php 5.5 * //3 array(array('callback' => 'function', 'params' => array(1,2)), array('callback' => 'function', 'params' => array(1,2)) ); * //2 array('callback' => 'function', 'params' => array(1,2)) array('callback' => array($obj, 'function'), 'params' => array(1,2)); * //1 array('num \\1', 'num \\2') * * @param mixed $pattern 正则表达式 * @param mixed $replacement 替换字串 * @param string $subject 待替换数据 * * @return mixed */ function rpreg_replace($pattern, $replacement, $subject) { // 如果正则表达式为数组 if (is_array($pattern)) { // 遍历所有表达式 foreach ($pattern as $loop => $pat) { $rep = $replacement; if (is_string($replacement)) { // 用于替换的值为字串时 $rep = $replacement; } elseif (!empty($replacement['callback'])) { // 用户替换的值为回调函数时 $rep = $replacement; } elseif (!empty($replacement[$loop]['callback']) || is_string($replacement[$loop])) { $rep = $replacement[$loop]; } $subject = rpreg_replace($pat, $rep, $subject); } return $subject; } else { // 回调替换函数 return preg_replace_callback($pattern, function ($matches) use ($replacement) { $ret = ''; if (is_array($replacement)) { // 如果用于替换的值为数组时 // 遍历所有替换的值 foreach ($replacement['params'] as $pk => $pv) { if (is_numeric($pv)) { $replacement['params'][$pk] = $matches[$pv - 1]; } } $ret = call_user_func_array($replacement['callback'], $replacement['params']); } elseif (is_string($replacement)) { $ret = $replacement; foreach ($matches as $k => $v) { $num = $k + 1; $ret = str_replace(array( "\\{$num}", "\${$num}", "\$\{{$num}\}" ), $v, $ret); } } return $ret; }, $subject); } } // 获取页面的唯一标识 function url_uniqueid() { static $uniqueid = ''; // 如果已经生成 if (!empty($uniqueid)) { return $uniqueid; } $str = boardurl() . random(16) . MILLI_TIME; $uniqueid = md5($str); return $uniqueid; } // 获取当前页面的完整 URL function boardurl() { static $boardurl = ''; // 如果已经生成过 if (!empty($boardurl)) { return $boardurl; } // 拼接URL $protocal = is_ssl() ? 'https://' : 'http://'; $php_self = I('server.PHP_SELF', I('server.SCRIPT_NAME', '', 'trim'), 'trim'); $relate_url = I('server.REQUEST_URI', $php_self . I('server.QUERY_STRING', I('server.PATH_INFO', '', 'trim'), 'trim'), 'trim'); $boardurl = $protocal . I('server.HTTP_HOST', '') . $relate_url; return $boardurl; } /** * 公共的密码加密储存方法 * * @param string $password 密码值,可以为原文也可以md5后 * @param string $salt 散列值,如为空则自动生成 * @param boolean $is_original 给定的$password是否为密码原文,true是,false为md5后的值 * @param int $salt_length 散列值的长度 * * @return array(password, salt) * <p>array(0=>password 加密后的密码字符串, 1 =>salt 密码散列值)</p> */ function generate_password($password, $salt = null, $is_original = false, $salt_length = 6) { $salt = (string)$salt; // 如果干扰码为空, 则生成 if (null === $salt || '' === $salt) { $salt = random($salt_length); } // 如果是源密码 if ($is_original) { $password = md5($password); } return array( md5($password . $salt), $salt ); } /** * 根据数组指定的键对应的值, 作为新数组的键名 * * @param array $arr 二维数组 * @param string $key 键名 * * @return array */ function array_combine_by_key($arr, $key) { $keys = array_column($arr, $key); return array_combine($keys, $arr); } /** * 根据组数指定的值保留键值对 * * @param $arr array 二维数组 * @param $key array 值 * @param $handleneDimensional bool 处理一维数组 * @return mixed */ function array_intersect_key_reserved($arr, $key, $handleneDimensional = false) { // 要处理的键 settype($key, 'array'); $key = array_fill_keys($key, ''); // 处理一维数组 if ($handleneDimensional) { return array_intersect_key($arr, $key); } // 处理二维数组 foreach ($arr as &$value) { $value = array_intersect_key($value, $key); } return $arr; } /** * 根据组数指定的值排除键值对 * * @param $arr array 二维数组 * @param $key array 值 * @param $handleneDimensional bool 处理一维数组 * @return mixed */ function array_diff_key_reserved($arr, $key, $handleneDimensional = false) { // 要处理的键 settype($key, 'array'); $key = array_fill_keys($key, ''); // 处理一维数组 if ($handleneDimensional) { return array_diff_key($arr, $key); } // 处理二维数组 foreach ($arr as &$value) { $value = array_diff_key($value, $key); } return $arr; } /** * 重写 fopen 方法, 调用 Snoopy 来模拟请求 * * @param array $data 返回数据 * @param string $url 目标URL * @param mixed $post 请求的数据 * @param array $headers 请求的头部 * @param string $method 请求协议, 默认 GET * @param boolean $ret_snoopy 是否需要返回 Snoopy 实例 * @param boolean $json 目标地址是否返回的 JSON 数据 * @param mixed $files 文件数据 * * @return bool */ function rfopen(&$data, $url, $post = null, $headers = array(), $method = 'GET', $ret_snoopy = false, $json = true, $files = null) { // 载入 Snoopy 类 $snoopy = new \Org\Net\Snoopy(); // 使用自定义的头字段,格式为 array(字段名 => 值, ... ...) if (!is_array($headers)) { $headers = array(); } \Think\Log::record("URL: {$url} Method: {$method} Post: " . var_export($post, true) . " Header: " . var_export($headers, true)); $snoopy->rawheaders = $headers; $method = rstrtoupper($method); // 非 GET 协议, 需要设置 $methods = array( 'POST', 'PUT', 'DELETE' ); if (!in_array($method, $methods)) { $method = 'GET'; } // 设置协议 if (!empty($files)) { // 如果需要传文件 $method = 'POST'; $snoopy->set_submit_multipart(); } else { $snoopy->set_submit_normal(); } // 判断协议 $snoopy->set_submit_method($method); switch (rstrtoupper($method)) { case 'POST': case 'PUT': case 'DELETE': $result = $snoopy->submit($url, $post, $files); break; default: // 如果有请求数据 if ($post) { // 拼凑 GET 字串 if (is_array($post)) { $get_data = http_build_query($post); } else { $get_data = $post; } // 判断 URL 是否有参数 if (false === strpos($url, '?')) { $url .= '?'; } else { $url .= '&'; } $url .= $get_data; } $result = $snoopy->fetch($url); } // 如果读取错误 if (!$result || 200 != $snoopy->status) { \Think\Log::record('$snoopy[' . $method . '] error: ' . $url . '|' . $post . '|' . var_export($result, true) . '|' . $snoopy->status); $data = $snoopy; // 出错时, 返回 $snoopy 对象 E('_ERR_SNOOPY_STATUS_ERROR'); return false; } // 获取返回数据 $data = $snoopy->results; // 如果返回的是 JSON, 则解析 JSON if ($json) { $data = json_decode($data, true); } \Think\Log::record("result: " . var_export($snoopy->results, true) . " data: " . var_export($data, true)); // 如果返回的数据为空, 则 if (empty($data)) { \Think\Log::record('$snoopy[' . $method . '] error: ' . $url . '|' . var_export($result, true) . '|' . $snoopy->status); $data = $snoopy; // 出错时, 返回 $snoopy 对象 E('_ERR_SNOOPY_RESULT_EMPTY'); return false; } // 如果需要返回 Snoopy 对象 if ($ret_snoopy) { $snoopy->set_results($data); $data = $snoopy; } return true; } /** * 从主机地址中获取二级域名信息(second-level) * * @param string $host 主机地址 * * @return string */ function get_sl_domain($host = '') { static $domain = ''; // 如果未指定主机, 则取当前请求的 if (empty($host)) { // 如果域名已经存在 if (!empty($domain)) { return $domain; } $host = I('server.HTTP_HOST'); } // 取二级域名 $hosts = explode('.', $host); $domain = rawurlencode($hosts[0]); return $domain; } /** * 把驼峰转成以下划线分隔, 如:MsgType => msg_type * * @param string $key 转换 * @param bool $to_camel 是否转成驼峰 * * @return string */ function convert_camel_underscore($key, $to_camel = false) { // 如果是转成驼峰式 if ($to_camel) { $key{0} = rstrtoupper($key{0}); $key = preg_replace_callback('/\_[a-z]/s', function ($m) { return rstrtoupper($m); }, $key); } else { // 非驼峰 $key{0} = rstrtolower($key{0}); $key = preg_replace("/([A-Z]+)/s", "_\\1", $key); $key = rstrtolower($key); } return $key; } /** * 根据当前页数、每页的显示数来确定 sql 查询的开始及读取条数; * * @param int $page 当前页码 * @param int $perpage 每页记录数 * @param int $max_perpage 每页显示数的最大值 * @param int $max_page 最大页码 * * @return array 返回开始数、每页显示数、当前页数; */ function page_limit($page = 0, $perpage = 0, $max_perpage = 200, $max_page = 0) { // 类型转换 $perpage = rintval($perpage); // 如果每页个数值超出范围, 则取最大值 if (0 >= $perpage || $perpage > $max_perpage) { $perpage = $max_perpage; } $max_page = rintval($max_page); // 如果最大页码大于规定的值 $page = max(rintval($page), 1); if ($max_page && $page > $max_page) { $page = $max_page; } $start = ($page - 1) * $perpage; return array( $start, $perpage, $page ); } /** * 返回给定秒数的时间描述 * * @param mixed $result 结果集 * @param number $second 秒 * @param boolean $diy 是否DIY输出 * * @return array(day, hour, minute) */ function sec2dhis(&$result, $second, $diy = false) { $d = floor($second / 86400); $h = floor(($second % 86400) / 3600); $i = floor(($second % 3600) / 60); $s = $second % 60; if ($diy) { // 如果是 diy 输出 $result = array(); // 读取语言信息 $lang = L('date'); // 如果大于 1 天 if (0 < $d) { $result[] = $d . $lang['DAY']; } // 如果大于 1 小时 if (0 < $h) { $result[] = $h . $lang['HOUR']; } // 如果大于 1 分钟 if (0 < $i) { $result[] = $i . $lang['MIN']; } // 如果大于 1 秒 if (0 < $s) { $result[] = $s . $lang['SEC']; } // 如果时间为 0 if (empty($result)) { $result[] = "0" . $lang['SEC']; } $result = implode($result); } else { $result = array( $d, $h, $i, $s ); } return true; } /** * 根据给定的键值提取参数 * * @param array $out 提取之后的数据 * @param array $fields 需要获取的参数列表 * array( * 'passwd', * array('username', 'string'), * array('gender', 'string', false), * 'weight' => 'mem_weight' * ) * @param array $in 源数据 * * @return boolean */ function extract_field(&$out, $fields, $in = array()) { // 如果未给定数据, 则取用户提交的数据 if (empty($in)) { $in = I('request.'); } // 遍历所有需要提取的参数 foreach ($fields as $_in_k => $_rule) { /** * 取出键值和数据类型 * $out_k => 解析后的目的键值 * $type => 类型 * $ignore => 是否忽略键值不存在的参数 */ $_rule = (array)$_rule; switch (count($_rule)) { case 1: // 如果只有 1 个参数 $_rule[] = 'string'; case 2: // 如果只有 2 个参数 $_rule[] = false; default: break; } list ($out_k, $type, $ignore) = $_rule; // 取 $out_k 对应的值 $out_k = (string)$out_k; // 如果来源键值为数字, 则说明未指定来源键值 if ($_in_k === (int)$_in_k) { $_in_k = $out_k; } // 如果允许该字段为空 if ($ignore && !isset($in[$_in_k])) { continue; } $val = isset($in[$_in_k]) ? $in[$_in_k] : ''; // 类型强制转换 switch ($type) { case 'array': $val = empty($val) ? array() : (array)$val; break; case 'int': $val = (int)$val; break; case 'string': $val = (string)$val; break; default: $val = (string)$val; break; } // 赋值 $out[$out_k] = $val; } return true; } /** * 拼凑 api 数据返回值结构 * * @param array $data 返回数据 * * @return array */ function generate_api_result($data) { $errsdkcode = ''; $requestId = ''; $errcode = \Com\Error::instance()->get_errcode(); // 如果是异常数据 if ($data instanceof \Exception) { // 如果是SDK错误 if (method_exists($data, 'getSdkCode')) { $errsdkcode = $data->getSdkCode(); $errcode = 700; } if (method_exists($data, 'getRequestID')) { $requestId = $data->getRequestID(); } $data = ''; } elseif (method_exists(\VcySDK\Service::instance(), 'allRequestId')) { // 不是异常的时候 返回所有请求 RequestId $requestId = \VcySDK\Service::instance()->allRequestId(); } $result = array( 'errcode' => $errcode, 'errmsg' => \Com\Error::instance()->get_errmsg(), 'errsdkcode' => $errsdkcode, 'requestId' => $requestId, 'timestamp' => MILLI_TIME, 'result' => $data ); return $result; } /** * 解析错误信息 * * @param string $message 错误信息 * * @return array */ function parse_lang_error($message) { $code = 0; // 如果是语言 if (preg_match('/^[\w+\.\_]+$/i', $message)) { $message = L($message); } // 判断是否有错误编号 if (preg_match('/^(\s*\d+\s*):/', $message)) { $pos = stripos($message, ':'); $ncode = substr($message, 0, $pos); $message = substr($message, $pos + 1); // 如果错误号为空, 则取详情中得编号 if (empty($code)) { $code = $ncode; } } return array($code, $message); } // PHP < 5.5 兼容函数 array_column if (!function_exists('array_column')) { /** * 兼容 php < 5.5 以下版本的 array_column 函数 - 返回数组中指定的一列 * * 返回 input 数组中键值为 column_key 的列, 如果指定了可选参数 index_key,那么 input 数组中的这一列的值将作为返回数组中对应值的键 * * @param array $input 需要取出数组列的多维数组(或结果集) * @param mixed $column_key 需要返回值的列,它可以是索引数组的列索引,或者是关联数组的列的键。 也可以是 NULL ,此时将返回整个数组(配合index_key参数来重置数组键的时候,非常管用) * @param mixed $index_key 作为返回数组的索引/键的列,它可以是该列的整数索引,或者字符串键值 * * @return array 从多维数组中返回单列数组 */ function array_column($input, $column_key, $index_key = null) { if ($index_key !== null) { // Collect the keys $keys = array(); $i = 0; // Counter for numerical keys when key does not exist foreach ($input as $row) { if (array_key_exists($index_key, $row)) { // Update counter for numerical keys if (is_numeric($row[$index_key]) || is_bool($row[$index_key])) { $i = max($i, (int)$row[$index_key] + 1); } // Get the key from a single column of the array $keys[] = $row[$index_key]; } else { // The key does not exist, use numerical indexing $keys[] = $i++; } } } if ($column_key !== null) { // Collect the values $values = array(); $i = 0; // Counter for removing keys foreach ($input as $row) { if (array_key_exists($column_key, $row)) { // Get the values from a single column of the input array $values[] = $row[$column_key]; $i++; } elseif (isset($keys)) { // Values does not exist, also drop the key for it array_splice($keys, $i, 1); } } } else { // Get the full arrays $values = array_values($input); } if ($index_key !== null) { return array_combine($keys, $values); } return $values; } } /** * 根据域名生成站点缓存目录 * * @param string $domain 二级域名 * * @return string */ function get_sitedir($domain = '') { static $sitedir = ''; // 如果已经路径生成 if (!empty($sitedir)) { return $sitedir; } // 如果 $domain 为空, 则使用当前站点域名 if (empty($domain)) { $domain = QY_DOMAIN; } // md5, 取首尾字符 + 域名作为目录 $md5 = md5($domain); // cfg('DATA_CACHE_PATH') . substr($md5, 0, 1) . '/' . substr($md5, - 1) . '/' . $domain . '/'; $sitedir = CODE_ROOT . D_S . 'Common' . D_S . 'Runtime' . D_S . 'Temp' . D_S . substr($md5, 0, 1) . D_S . substr($md5, -1) . D_S . $domain . D_S; rmkdir($sitedir); return $sitedir; } if (!function_exists('array_column')) { function array_column($input, $column_key, $index_key = null) { if ($index_key !== null) { // Collect the keys $keys = array(); $i = 0; // Counter for numerical keys when key does not exist foreach ($input as $row) { if (array_key_exists($index_key, $row)) { // Update counter for numerical keys if (is_numeric($row[$index_key]) || is_bool($row[$index_key])) { $i = max($i, (int)$row[$index_key] + 1); } // Get the key from a single column of the array $keys[] = $row[$index_key]; } else { // The key does not exist, use numerical indexing $keys[] = $i++; } } } if ($column_key !== null) { // Collect the values $values = array(); $i = 0; // Counter for removing keys foreach ($input as $row) { if (array_key_exists($column_key, $row)) { // Get the values from a single column of the input array $values[] = $row[$column_key]; $i++; } elseif (isset($keys)) { // Values does not exist, also drop the key for it array_splice($keys, $i, 1); } } } else { // Get the full arrays $values = array_values($input); } if ($index_key !== null) { return array_combine($keys, $values); } return $values; } } /** * 记录SQL日志 * * @param string $sql SQL 语句 * @param array $params 参数数组 * * @return bool */ function sql_record($sql, $params = array()) { return true; // 导入类库文件 $class = parse_res_name('Common/CommonSqlrecord', 'Service'); // 如果该类文件不存在, 则直接返回 if (!class_exists($class)) { return true; } // 初始化 Service 层 $serv = D('Common/CommonSqlrecord', 'Service'); // 如果不是 SQL 日志表操作, 则记录 if (!preg_match('/\s+\`?' . $serv->tname() . '\`?(\(|\s+)/is', $sql)) { $serv->insert($sql . ';' . var_export((array)$params, true)); return true; } return true; } /** * OA URL 拼写输出函数 * * 根据 OA 特点扩展了的 TP 内置的 U() 函数,前两个参数的使用方法同 U() 函数使用完全一致 * * @param string $url URL表达式,格式:'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...' * @param array $vars 传入的参数,支持数组和字符串 * @param string $qyDomain 指定企业的域名,不指定则使用当前访问的企业域名 QY_DOMAIN * @param string $app_dir 指定应用目录名称,不指定则使用当前目录名称 APP_DIR * @param bool $showDomain 是否显示域名,true=等于输出完整的URL,同 U() 函数的 $domain 参数一致。 * * @return string */ function oaUrl($url = '', $vars = array(), $qyDomain = '', $app_dir = '', $showDomain = true) { if (!$qyDomain) { // 如果未指定企业域名,则使用当前定义的 $qyDomain = QY_DOMAIN; } if (!$app_dir) { // 如果未指定应用,则使用当前定义的 $app_dir = APP_DIR; } // 如果是完整的 url 或者已经包含企业域名 if (preg_match('/^https?\:\/\//i', $url) || preg_match("/^\/?{$qyDomain}\//i", $url)) { return U($url, $vars, false, $showDomain); } // 拼凑当前的 URL 前缀(企业域名/应用唯一标识符) if (!preg_match("/^\/?{$qyDomain}\/{$app_dir}\//i", $url)) { $url = '/' . $qyDomain . '/' . $app_dir . '/' . ltrim($url, '/'); } return U($url, $vars, false, $showDomain); } /** * 后台地址格式化 * * @param string $url URL地址 * @param string $vars 传入的参数,支持数组和字符串 * @param boolean $suffix 伪静态后缀,默认为true表示获取配置值 * @param boolean $domain 是否显示域名 * * @return string */ function cpUrl($url, $vars = '', $suffix = false, $domain = false) { return U($url, $vars, $suffix, $domain); } /** * 后台地址格式化 * * @param string $url URL表达式,格式:'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...' * @param array $vars 传入的参数 * @return string */ function admincpUrl($url, $vars = null) { $admincpUrl = cfg('PROTOCAL') . $_SERVER['HTTP_HOST'] . $url; return empty($vars) ? $admincpUrl : $admincpUrl . '?' . http_build_query($vars); } /** * 根据图片id获取图片展示的Url * * @param string $atid 附件ID * @param string $qyDomain 企业域名 * @param string $_identifier 应用唯一标识 * * @return string */ function imgUrl($atid = '', $qyDomain = '', $_identifier = '') { if (empty($_identifier)) { $_identifier = APP_IDENTIFIER; } // 拼凑URL $url = oaUrl('/Frontend/Attachment/View/Index', array("atid" => $atid), $qyDomain, "Public"); return $url . '?_identifier=' . $_identifier; } /** * 根据图片id获取图片真实地址 * * @param string $atid * * @return string */ function imgUrlReal($atid = '') { if (empty($atid)) { return ''; } return \Com\Attachment::instance()->getAttachUrl($atid); } /** * 字节转换单位显示 * * @param number $size 字节数 * @param string $delimiter 数字和单位分隔符 * * @return string 格式化后的带单位的大小 */ function formatBytes($size, $delimiter = '') { $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB'); for ($i = 0; $size >= 1024 && $i < 6; $i++) { $size /= 1024; } return round($size, 2) . $delimiter . $units[$i]; } /** * 清理缓存 (所有缓存) * * @return boolean */ function clear_cache() { $serv_sys = D('Common/Syscache', 'Service'); $list = $serv_sys->list_all(); foreach ($list as $_cache) { $cachekey = $_cache['name']; // 去除应用标识 $cachekey = str_replace(APP_DIR, '', $cachekey); \Common\Common\Cache::instance()->set($cachekey, null); } return true; } /** * 清理缓存 * * @param string|array $cacheName * @return bool */ function clear_sys_cache($cacheName = null) { if (empty($cacheName)) { clear_cache(); return true; } settype($cacheName, 'array'); // 清理指定缓存 foreach ($cacheName as $name) { \Common\Common\Cache::instance()->set($name, null); } return true; } /** * 获取前端完整Url * * @author zhonglei * * @param string $pageUrl 前端地址(#号之后的页面地址,不包含参数) * @param array $params 参数 * @param string $appdir 应用目录,默认为当前目录 * * @return string */ function frontUrl($pageUrl, $params = [], $appdir = '') { if (!isset($params['ts'])) { $params['ts'] = MILLI_TIME; } if (cfg('STATIC_URL_DEBUG') === false) { unset($params['ts']); } if (empty($appdir)) { $appdir = APP_DIR; } $frontPath = cfg('FRONTEND_PATH'); $url = (is_ssl() ? 'https://' : 'http://') . (cfg('FRONT_DOMAIN') ?: $_SERVER['HTTP_HOST']); $url .= sprintf('/%s/%s/%s/index.html#%s?%s', QY_DOMAIN, $appdir, $frontPath, $pageUrl, http_build_query($params)); return $url; } /** * 跳转至前端Url * * @author zhonglei * * @param string $pageUrl 前端地址(#号之后的页面地址,不包含参数) * @param array $params 参数 * @param string $appdir 应用目录,默认为当前目录 * * @return void */ function redirectFront($pageUrl, $params = [], $appdir = '') { $url = frontUrl($pageUrl, $params, $appdir); redirect($url); } /** * 获取当前访问的操作系统 * * @return string 返回值可能是:ios|android 或者 空字符串 */ function getOS() { $userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? (string)$_SERVER['HTTP_USER_AGENT'] : ''; if ($userAgent) { if (stripos($userAgent, 'iPhone') !== false || stripos($userAgent, 'iPad') !== false) { return 'ios'; } if (stripos($userAgent, 'Android') !== false) { return 'android'; } } return ''; } /** * 字符串可逆加密函数 * @param string $string 待 加密/解密 的字符串 * @param string $operation 执行动作(注意大小写)。DECODE=解密,ENCODE=加密,默认:DECODE=解密 * @param string $key 加密的密钥 * @param int $expiry 过期时间,单位:秒,默认=0 不过期。 * @return string */ function strEncrypt($string, $operation = 'DECODE', $key = '', $expiry = 0) { $ckey_length = 4; $key = md5($key); $keya = md5(substr($key, 0, 16)); $keyb = md5(substr($key, 16, 16)); $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey = $keya . md5($keya . $keyc); $key_length = strlen($cryptkey); $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string; $string_length = strlen($string); $result = ''; $box = range(0, 255); $rndkey = array(); for ($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for ($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } for ($a = $j = $i = 0; $i < $string_length; $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])); } if ($operation == 'DECODE') { if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) { return substr($result, 26); } else { return ''; } } else { return $keyc . str_replace('=', '', base64_encode($result)); } } /** * 获取 IP 地址的地理位置 * @param string $ip IP 地址 * @return string */ function ipLocation($ip) { if (empty($ip)) { return ''; } $result = Com\IP\IP::find($ip); if (empty($result) || !is_array($result)) { return ''; } $result = array_unique($result); $location = ''; foreach ($result as $v) { if (empty($v)) { break; } elseif ($v == '中国') { continue; } $location .= $v; } return $location; } /** * 调用 python * @throws Exception * @return mixed */ function callPython() { // 参数数量 $args_len = func_num_args(); // 参数数组 $arg_array = func_get_args(); // 参数数量不能小于 1 if ($args_len < 1) { E('_ERR_PYTHON_PARAM_LENGTH_ERROR'); } // 第一个参数是Python模块函数名称,必须是string类型 if (!is_string($arg_array[0])) { E('_ERR_PYTHON_PARAM_MODULE_ERROR'); } if (($socket = socket_create(AF_INET, SOCK_STREAM, 0)) === false) { E('_ERR_PYTHON_SOCKET_CREATE_ERROR'); } if (socket_connect($socket, cfg('PYTHON_SOCKET_IP'), cfg('PYTHON_SOCKET_PORT')) === false) { E('_ERR_PYTHON_CONNECT_ERROR'); } // 消息体序列化 $request = json_encode($arg_array); $req_len = strlen($request); $request = $req_len . "," . $request; // echo "{$request}<br>"; $send_len = 0; do { // 发送 if (($sends = socket_write($socket, $request, strlen($request))) === false) { E('_ERR_PYTHON_SOCKET_WRITE_ERROR'); } $send_len += $sends; $request = substr($request, $sends); } while ($send_len < $req_len); // 接收 $response = ""; while (true) { if (($buf = socket_read($socket, 2048)) === false) { E('_ERR_PYTHON_SOCKET_READ_ERROR'); } if ($buf == "") { break; } $response .= $buf; } // 关闭 socket_close($socket); $resp_stat = substr($response, 0, 1); // 返回类型 "S":成功 "F":异常 $resp_msg = substr($response, 1); // 返回信息 // echo "返回类型:{$resp_stat},返回信息:{$resp_msg}<br>"; if ($resp_stat == "F") { // 异常信息不用反序列化 E('_ERR_PYTHON_CALL_ERROR', array('errmsg' => $resp_msg)); } // 反序列化 return json_decode($resp_msg); } /** * 对二维数组进行排序 * @param array $arr 二维数组 * @param string $key 排序的依据 * @param int $type 排序类型 * SORT_REGULAR 对对象进行 通常 比较 * SORT_NUMERIC 对对象进行 数值 比较 * SORT_STRING 对对象进行 字符串 比较 * SORT_LOCALE_STRING 基于当前区域来对对象进行字符串比较 * 详见: http://php.net/manual/zh/array.constants.php * @param int $short 排序顺序 * SORT_ASC 升序 * SORT_DESC 降序 * @return mixed * @author zhoutao * @date 2017-06-01 18:49:42 */ function multi_array_sort($arr, $key, $type = SORT_NUMERIC, $short = SORT_DESC) { array_multisort(array_column($arr, $key), $type, $short, $arr); return $arr; } /** * 将链接地址协议转换为当前访问使用的协议 * @param string $url * @return $url */ function urlProtocolConversion($url) { if (empty($url)) { return $url; } if (is_ssl() && stripos($url, 'https://') !== 0) { $url = preg_replace('/^http:\/\//i', 'https://', $url); } return $url; } /** * 获取资源鉴权配置 * @param string $key 配置名称 * @return array */ function getResAuthConfig($key) { if (empty($key)) { return []; } $auth_config = cfg('RES_AUTH_CONFIG'); if (!is_array($auth_config) || !isset($auth_config[$key])) { return []; } $config = $auth_config[$key]; // 处理资源权限认证地址 if (isset($config['atAuthUrl']) && is_array($config['atAuthUrl'])) { list($url, $app_dir) = $config['atAuthUrl']; $config['atAuthUrl'] = oaUrl($url, [], '', $app_dir); } return $config; } /** * 判断输入是否为 JSON 请求 * @return array | false */ function get_input_json() { $contentType = ''; if (isset($_SERVER['CONTENT_TYPE'])) { $contentType = $_SERVER['CONTENT_TYPE']; } elseif (isset($_SERVER['HTTP_CONTENT_TYPE'])) { $contentType = $_SERVER['HTTP_CONTENT_TYPE']; } return stripos($contentType, 'application/json') !== false ? (array)json_decode(file_get_contents('php://input'), true) : false; } /** * 清理历史旧的日志文件 * @param string $dir 指定待清理的目录 * @param string $ext 待清理的文件扩展名,默认:log * @param integer $keepCount 需要保留最新的文件数,默认:100 * @param integer $cycle 清理周期(上次清理时间距离当前时间差超过该值则执行),默认:86400(单位:秒) * @return boolean */ function clearLogs($dir, $ext = 'log', $keepCount = null, $cycle = null) { // 读取日志清理相关配置 $cfg = cfg('LOG_CLEAR'); if ($keepCount === null) { // 自全局配置 读取保留最新的文件数 配置 $keepCount = is_array($cfg) && isset($cfg['keep_count']) && is_numeric($cfg['keep_count']) ? $cfg['keep_count'] : 100; } if ($cycle === null) { // 自全局配置 读取清理周期 配置 $cycle = is_array($cfg) && isset($cfg['cycle']) && is_numeric($cfg['cycle']) ? $cfg['cycle'] : 86400; } // 用于标记最后清理时间的文件 $lastClearTagFilename = $dir . DIRECTORY_SEPARATOR . 'clear.last'; if (is_file($lastClearTagFilename) && time() - filemtime($lastClearTagFilename) < $cycle) { return false; } if (!is_dir($dir)) { return false; } $dh = opendir($dir); if (!$dh) { return false; } $ext = rstrtolower($ext); $files = []; while (($file = readdir($dh)) !== false) { if ($file == '.' || $file == '..') { continue; } if (rstrtolower(substr(strrchr($file, '.'), 1)) != $ext) { continue; } // 以文件名为键名,文件最后修改时间为键值 $files[$file] = (int)filemtime($dir . DIRECTORY_SEPARATOR . $file); } closedir($dh); if (empty($files)) { // 如果文件列表为空 return false; } // 对文件列表按时间正序 arsort($files, SORT_NUMERIC); // 从列表取出超出最大需要保留数量的文件 $files = array_slice($files, $keepCount); if (empty($files)) { return false; } // 对旧文件进行删除处理 foreach ($files as $_file => $_time) { unlink($dir . DIRECTORY_SEPARATOR . $_file); } // 写入最后清理时间 file_put_contents($lastClearTagFilename, time()); return true; } /** * Rpc URL 拼写输出函数 * @param string $path 请求路径,如/Contact/Rpc/Competence/List,必须以/开头 * @return string */ function rpcUrl($path) { $path_data = explode('/', $path); if (!is_array($path_data) || count($path_data) !== 5) { return ''; } $scheme = cfg('PROTOCAL'); $host = $_SERVER['HTTP_HOST']; return sprintf('%s%s/%s%s', $scheme, $host, QY_DOMAIN, $path); } /** * 格式化文件附件Url,加入资源鉴权和水印参数(切勿将格式化后的Url存储在数据库中) * @param string $url 附件Url * @param array $params 参数 * + string _id 资源鉴权业务ID * + array user 用户信息,用于生成水印参数 * @return string */ function fileUrl($url, $params) { if (empty($url) || !is_array($params) || empty($params)) { return $url; } // 水印参数 if (isset($params['user']) && is_array($params['user'])) { $user = $params['user']; $str = json_encode([ 'userName' => $user['memUsername'], 'mobile' => $user['memMobile'], ]); $aes = new \Think\Crypt\Driver\Aes(); $str_encrypt = $aes->encrypt($str); if (!empty($str_encrypt)) { $params['sign'] = urlencode($str_encrypt); } unset($params['user']); } $url .= strpos($url, '?') === false ? '?' : '&'; return $url . http_build_query($params) . '&_time=' . MILLI_TIME; } /** * 根据字段对比原始数据变更的数据 * @param array $newData 新数组 * @param array $originalData 原始数组 * @param array $field 字段 * @return array */ function array_diff_assoc_key($newData, $originalData, $field) { $field = array_fill_keys($field, ''); // 获取两个数组里需要的字段 $mainData = array_intersect_key($newData, $field); $mainOldData = array_intersect_key($originalData, $field); // 返回有变更的字段 return array_diff_assoc($mainData, $mainOldData); } /** * 判断字符串是否是序列化,是序列号返回true * @param string $data 字符串 * @return array */ function is_serialized($data) { $data = trim($data); if ('N;' == $data) { return true; } if (!preg_match('/^([adObis]):/', $data, $badions)) { return false; } switch ($badions[1]) { case 'a': case 'O': case 's': if (preg_match("/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data)) { return true; } break; case 'b': case 'i': case 'd': if (preg_match("/^{$badions[1]}:[0-9.E-]+;\$/", $data)) { return true; } break; } return false; }