/** * 自动表单验证 * @access protected * @param array $data 创建数据 * @param string $type 创建类型 * @return boolean */ public function valid($data, $rule = []) { $validate = $rule ? $rule : $this->validate; // 属性验证 if ($validate) { // 如果设置了数据自动验证则进行数据验证 if ($this->patchValidate) { // 重置验证错误信息 $this->error = []; } foreach ($validate as $key => $val) { // 验证因子定义格式 // array(field,rule,message,condition,type,params) // 判断是否需要执行验证 if (0 == strpos($val[2], '{%') && strpos($val[2], '}')) { $val[2] = Lang::get(substr($val[2], 2, -1)); } $val[3] = isset($val[3]) ? $val[3] : 0; $val[4] = isset($val[4]) ? $val[4] : 'regex'; // 判断验证条件 if (1 == $val[3] || 2 == $val[3] && '' != trim($data[$val[0]]) || 0 == $val[3] && isset($data[$val[0]])) { if (false === $this->_validationField($data, $val)) { return false; } } } // 批量验证的时候最后返回错误 if (!empty($this->error)) { return false; } } return true; }
/** * 构造方法,可用于打开一张图像 * * @param string $imgname 图像路径 */ public function __construct($imgname = null) { if (!extension_loaded('Imagick')) { throw new \Exception(Lang::get('_NOT_SUPPERT_') . ':Imagick'); } $imgname && $this->open($imgname); }
public function testRange() { $this->assertEquals('zh-cn', Lang::range()); Lang::set('hello', '欢迎', 'test'); Lang::range('test'); $this->assertEquals('test', Lang::range()); $this->assertEquals('欢迎', Lang::get('hello')); }
/** * 操作失败返回 JSON * * @param string|Exception $error * @return void */ public function errorJson($error) { $this->assign('success', 0); if (is_object($error)) { $this->assign('error', $error->getMessage()); $this->assign('error_msg', Lang::get($error->getMessage())); } else { if (is_string($error)) { $this->assign('error', $error); $this->assign('error_msg', Lang::get($error)); } } $this->json(); }
/** * 利用__call方法实现一些特殊的Model方法 * @access public * * @param string $method 方法名称 * @param array $args 调用参数 * * @return mixed * @throws \think\Exception */ public function __call($method, $args) { if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 $field = Loader::parseName(substr($method, 5)); $where[$field] = $args[0]; return $this->where($where)->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 $name = Loader::parseName(substr($method, 10)); $where[$name] = $args[0]; return $this->where($where)->getField($args[1]); } else { throw new \think\Exception(__CLASS__ . ':' . $method . Lang::get('_METHOD_NOT_EXIST_')); } }
/** * REST 调用 * @access public * * @param string $method 方法名 * @param array $args 参数 * * @return mixed * @throws \think\Exception */ public function _empty($method, $args) { if (method_exists($this, $method . '_' . $this->_method . '_' . $this->_type)) { // RESTFul方法支持 $fun = $method . '_' . $this->_method . '_' . $this->_type; } elseif ($this->_method == $this->restDefaultMethod && method_exists($this, $method . '_' . $this->_type)) { $fun = $method . '_' . $this->_type; } elseif ($this->_type == $this->restDefaultType && method_exists($this, $method . '_' . $this->_method)) { $fun = $method . '_' . $this->_method; } if (isset($fun)) { $this->{$fun}(); } else { // 抛出异常 throw new \think\Exception(\think\Lang::get('_ERROR_ACTION_:') . ACTION_NAME); } }
/** * 利用__call方法实现一些特殊的Model方法 * @access public * @param string $method 方法名称 * @param array $args 调用参数 * @return mixed */ public function __call($method, $args) { if (in_array(strtolower($method), ['count', 'sum', 'min', 'max', 'avg'], true)) { // 统计查询的实现 $field = isset($args[0]) ? $args[0] : '*'; return $this->getField(strtoupper($method) . '(' . $field . ') AS tp_' . $method); } elseif (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 $field = Loader::parseName(substr($method, 5)); $where[$field] = $args[0]; return $this->where($where)->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 $name = Loader::parseName(substr($method, 10)); $where[$name] = $args[0]; return $this->where($where)->getField($args[1]); } elseif (isset($this->scope[$method])) { // 命名范围的单独调用支持 return $this->scope($method, $args[0]); } else { throw new \think\Exception(__CLASS__ . ':' . $method . Lang::get('_METHOD_NOT_EXIST_')); return; } }
/** * 获取和设置语言定义(不区分大小写) * * @param string|array $name 语言变量 * @param string $value 语言值 * * @return mixed */ function L($name = null, $value = null) { // 空参数返回所有定义 if (empty($name)) { return Lang::getAll(); } // 获取 if (is_string($name) && is_null($value)) { return Lang::get($name); } // 设置 if (is_string($name) && !is_null($value)) { return Lang::set($name, $value); } // 批量定义 if (is_array($name)) { return Lang::setAll($name); } }
/** * where分析 * @access protected * @param mixed $where * @return array */ public function parseWhere($where) { $query = []; foreach ((array) $where as $key => $val) { if ('_id' != $key && 0 === strpos($key, '_')) { // 解析特殊条件表达式 $query = $this->parseThinkWhere($key, $val); } else { // 查询字段的安全过滤 if (!preg_match('/^[A-Z_\\|\\&\\-.a-z0-9]+$/', trim($key))) { throw new Exception(Lang::get('_ERROR_QUERY_') . ':' . $key); } $key = trim($key); if (strpos($key, '|')) { $array = explode('|', $key); $str = []; foreach ($array as $k) { $str[] = $this->parseWhereItem($k, $val); } $query['$or'] = $str; } elseif (strpos($key, '&')) { $array = explode('&', $key); $str = []; foreach ($array as $k) { $str[] = $this->parseWhereItem($k, $val); } $query = array_merge($query, $str); } else { $str = $this->parseWhereItem($key, $val); $query = array_merge($query, $str); } } } return $query; }
function L($name, $vars = [], $lang = '') { return \think\Lang::get($name, $vars, $lang); }
/** * 获取语言变量值 * @param string $name 语言变量名 * @param array $vars 动态变量值 * @param string $lang 语言 * @return mixed */ function lang($name, $vars = [], $lang = '') { return Lang::get($name, $vars, $lang); }
/** * 验证表单字段 支持批量验证 * 如果批量验证返回错误的数组信息 * @access protected * @param array $data 创建数据 * @param array $val 验证因子 * @param string $type 创建类型 * @return boolean */ protected function _validationField(&$data, &$val, $type) { // 如果是批量验证,并且当前字段已经有规则验证没有通过则跳过 if ($this->patchValidate && isset($this->error[$val[0]])) { return true; } // 验证因子定义格式 // [field,rule,message,condition,type,when,params] if (empty($val[5])) { $flags = 1 << self::MODEL_BOTH - 1; } elseif (is_array($val[5])) { $flags = 0; foreach ($val[5] as $v) { $flags |= 1 << $v - 1; } } else { $flags = 3 == $val[5] ? 3 : 1 << $val[5] - 1; } // 判断是否需要执行验证 if ($flags & $type) { if (0 == strpos($val[2], '{%') && strpos($val[2], '}')) { // 支持提示信息的多语言 使用 {%语言定义} 方式 $val[2] = Lang::get(substr($val[2], 2, -1)); } $val[3] = isset($val[3]) ? $val[3] : self::EXISTS_VALIDATE; $val[4] = isset($val[4]) ? $val[4] : 'regex'; $status = true; // 判断验证条件 switch ($val[3]) { case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段 $status = $this->_validationFieldItem($data, $val); break; case self::VALUE_VALIDATE: // 值不为空的时候才验证 if ('' != trim($data[$val[0]])) { $status = $this->_validationFieldItem($data, $val); } break; default: // 默认表单存在该字段就验证 if (isset($data[$val[0]])) { $status = $this->_validationFieldItem($data, $val); } } if (false === $status) { if ($this->patchValidate) { $this->error[$val[0]] = $val[2]; } else { $this->error = $val[2]; return false; } } } return true; }
/** * 自动表单验证 * @access protected * @param array $data 创建数据 * @param string $type 创建类型 * @return boolean */ protected function autoValidation($data, $type) { if (!empty($this->options['validate'])) { $_validate = $this->options['validate']; unset($this->options['validate']); } elseif (!empty($this->validate)) { $_validate = $this->validate; } // 属性验证 if (isset($_validate)) { // 如果设置了数据自动验证则进行数据验证 if ($this->patchValidate) { // 重置验证错误信息 $this->error = []; } foreach ($_validate as $key => $val) { // 验证因子定义格式 // [field,rule,message,condition,type,when,params] // 判断是否需要执行验证 if (empty($val[5]) || self::MODEL_BOTH == $val[5] || $val[5] == $type) { if (0 == strpos($val[2], '{%') && strpos($val[2], '}')) { // 支持提示信息的多语言 使用 {%语言定义} 方式 $val[2] = Lang::get(substr($val[2], 2, -1)); } $val[3] = isset($val[3]) ? $val[3] : self::EXISTS_VALIDATE; $val[4] = isset($val[4]) ? $val[4] : 'regex'; // 判断验证条件 switch ($val[3]) { case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段 if (false === $this->_validationField($data, $val)) { return false; } break; case self::VALUE_VALIDATE: // 值不为空的时候才验证 if ('' != trim($data[$val[0]])) { if (false === $this->_validationField($data, $val)) { return false; } } break; default: // 默认表单存在该字段就验证 if (isset($data[$val[0]]) && false === $this->_validationField($data, $val)) { return false; } } } } // 批量验证的时候最后返回错误 if (!empty($this->error)) { return false; } } return true; }
/** * 返回403 * 可以根据实际情况在项目内覆盖 * * @return void */ public function redirectTo403() { if (IS_AJAX) { exit(Response::send_http_status(403)); } else { exit(Lang::get('_ERROR_403')); } }
/** * 获取错误信息 * ErrorException则使用错误级别作为错误编码 * @param \Exception $exception * @return string 错误信息 */ protected function getMessage(Exception $exception) { $message = $exception->getMessage(); if (IS_CLI) { return $message; } if (strpos($message, ':')) { $name = strstr($message, ':', true); $message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message; } elseif (strpos($message, ',')) { $name = strstr($message, ',', true); $message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message; } elseif (Lang::has($message)) { $message = Lang::get($message); } return $message; }
/** * 批量更新操作 * @access public * @param mixed $data 创建数据,如果传入true则更新所有缓存 * @param string $type 状态 * @return mixed */ public function bulkSave($data = '', $options = []) { // 如果没有数据传入 if (empty($data)) { $this->error = Lang::get('_DATA_TYPE_INVALID_'); } $options = $this->_parseOptions(); $pk = $this->getPk(); if ($data !== true) { //批量跟新必须是带有主键的数组 if (!isset($data[$pk])) { throw new Exception('no have pk field'); } //由于批量更新存在在原值累加的情况,所以会有 array('id'=>1,'val+'=>10);所以不能先做字段检查 foreach ($data as $field => $val) { //主键不需要处理 if ($field == $pk) { continue; } //对字段缓存初始化.每个字段的更新都会以独立的SQL进行. if (!isset($this->bulkSaveCache[$field])) { $this->bulkSaveCache[$field] = []; $this->bulkSaveSize[$field] = 0; } //如果同一个记录的同一字段被多次更新 if (!isset($this->bulkSaveCache[$field][$data[$pk]])) { //计算新增修改增加的SQL长度 $this->bulkSaveCache[$field][$data[$pk]] = $val; $this->bulkSaveSize[$field] += strlen($val) + strlen($data[$pk]) * 2 + 14; continue; } //如果是自增自减操作 $operator = '='; if (in_array(substr($field, -1, 1), ['+', '-'])) { $operator = substr($field, -1, 1); } if ($operator == '=') { //如果是直接赋值,不能重复写入,因为不确定那个值是正确的 throw new Exception('cannot repeat write in'); } else { //在原有修改基础上再进行自增或自减操作 $this->bulkSaveCache[$field][$data[$pk]] += $operator == '+' ? $val : -$val; } } } $bulkSize = isset($options['bulkSize']) ? $options['bulkSize'] : 10000; //扫描字段长度.看是否有需要先执行的 foreach ($this->bulkSaveSize as $field => $size) { if ($size < $bulkSize && ($size == 0 || $data !== true)) { continue; } //复制出插入数组 $dataSet = $this->bulkSaveCache[$field]; unset($this->bulkSaveCache[$field]); unset($this->bulkSaveSize[$field]); //默认运算符 $operator = '='; //自增或者自减 if (in_array(substr($field, -1, 1), ['+', '-'])) { $operator = substr($field, -1, 1); $field = substr($field, 0, -1); } //批量字段更新 $this->db->updateFieldAll($field, $pk, $dataSet, $operator, $options); } }
/** * 获取错误信息 * ErrorException则使用错误级别作为错误编码 * @param \Exception $exception * @return string 错误信息 */ protected function getMessage(Exception $exception) { $message = $exception->getMessage(); if (IS_CLI) { return $message; } // 导入语言包 if (!Config::get('lang_switch_on')) { Lang::load(THINK_PATH . 'lang' . DS . Lang::detect() . EXT); } if (strpos($message, ':')) { $name = strstr($message, ':', true); return Lang::has($name) ? Lang::get($name) . ' ' . strstr($message, ':') : $message; } else { return Lang::has($message) ? Lang::get($message) : $message; } }
/** * 获取验证规则的错误提示信息 * @access protected * @param string $attribute 字段英文名 * @param string $title 字段描述名 * @param string $type 验证规则名称 * @param mixed $rule 验证规则数据 * @return string */ protected function getRuleMsg($attribute, $title, $type, $rule) { if (isset($this->message[$attribute . '.' . $type])) { $msg = $this->message[$attribute . '.' . $type]; } elseif (isset($this->message[$attribute])) { $msg = $this->message[$attribute]; } elseif (isset(self::$typeMsg[$type])) { $msg = self::$typeMsg[$type]; } else { $msg = $title . '规则错误'; } if (is_string($msg) && strpos($msg, '{%')) { $msg = Lang::get(substr($msg, 2, -1)); } if (is_string($msg) && false !== strpos($msg, ':')) { // 变量替换 if (strpos($rule, ',')) { $array = array_pad(explode(',', $rule), 3, ''); } else { $array = array_pad([], 3, ''); } $msg = str_replace([':attribute', ':rule', ':1', ':2', ':3'], [$title, (string) $rule, $array[0], $array[1], $array[2]], $msg); } return $msg; }
/** * 默认跳转操作 支持错误导向和正确跳转 * 调用模板显示 默认为public目录下面的success页面 * 提示页面为可配置 支持模板标签 * @access private * @param string $message 提示信息 * @param Boolean $status 状态 * @param string $jumpUrl 页面跳转地址 * @param mixed $ajax 是否为Ajax方式 当数字时指定跳转时间 * @return void */ private function dispatchJump($message, $status = 1, $jumpUrl = '', $ajax = false) { if (true === $ajax || IS_AJAX) { // AJAX提交 $data = is_array($ajax) ? $ajax : []; $data['info'] = $message; $data['status'] = $status; $data['url'] = $jumpUrl; $this->ajaxReturn($data); } // 模板变量 $data = []; if (is_int($ajax)) { $data['waitSecond'] = $ajax; } if (!empty($jumpUrl)) { $data['jumpUrl'] = $jumpUrl; } // 提示标题 $data['msgTitle'] = Lang::get($status ? '_OPERATION_SUCCESS_' : '_OPERATION_FAIL_'); $data['status'] = $status; // 状态 //保证输出不受静态缓存影响 Config::set('html_cache_on', false); if ($status) { //发送成功信息 $data['message'] = $message; // 提示信息 // 成功操作后默认停留1秒 $data['waitSecond'] = '1'; // 默认操作成功自动返回操作前页面 if (!$jumpUrl) { $data["jumpUrl"] = $_SERVER["HTTP_REFERER"]; } return $this->display(Config::get('success_tmpl'), $data); } else { $data['error'] = $message; // 提示信息 //发生错误时候默认停留3秒 $data['waitSecond'] = '3'; // 默认发生错误的话自动返回上页 if (!$jumpUrl) { $data['jumpUrl'] = 'javascript:history.back(-1);'; } return $this->display(Config::get('error_tmpl'), $data); } }
protected function parseWhereItem($key, $val) { $whereStr = ''; if (is_array($val)) { if (is_string($val[0])) { $exp = strtolower($val[0]); if (preg_match('/^(eq|neq|gt|egt|lt|elt)$/', $exp)) { // 比较运算 $whereStr .= $key . ' ' . $this->exp[$exp] . ' ' . $this->parseValue($val[1]); } elseif (preg_match('/^(notlike|like)$/', $exp)) { // 模糊查找 if (is_array($val[1])) { $likeLogic = isset($val[2]) ? strtoupper($val[2]) : 'OR'; if (in_array($likeLogic, ['AND', 'OR', 'XOR'])) { $like = []; foreach ($val[1] as $item) { $like[] = $key . ' ' . $this->exp[$exp] . ' ' . $this->parseValue($item); } $whereStr .= '(' . implode(' ' . $likeLogic . ' ', $like) . ')'; } } else { $whereStr .= $key . ' ' . $this->exp[$exp] . ' ' . $this->parseValue($val[1]); } } elseif ('bind' == $exp) { // 使用表达式 $whereStr .= $key . ' = :' . $val[1]; } elseif ('exp' == $exp) { // 使用表达式 $whereStr .= $key . ' ' . $val[1]; } elseif (preg_match('/^(notin|not in|in)$/', $exp)) { // IN 运算 if (isset($val[2]) && 'exp' == $val[2]) { $whereStr .= $key . ' ' . $this->exp[$exp] . ' ' . $val[1]; } else { if (is_string($val[1])) { $val[1] = explode(',', $val[1]); } $zone = implode(',', $this->parseValue($val[1])); $whereStr .= $key . ' ' . $this->exp[$exp] . ' (' . $zone . ')'; } } elseif (preg_match('/^(notbetween|not between|between)$/', $exp)) { // BETWEEN运算 $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1]; $whereStr .= $key . ' ' . $this->exp[$exp] . ' ' . $this->parseValue($data[0]) . ' AND ' . $this->parseValue($data[1]); } else { throw new Exception(Lang::get('_EXPRESS_ERROR_') . ':' . $val[0]); } } else { $count = count($val); $rule = isset($val[$count - 1]) ? is_array($val[$count - 1]) ? strtoupper($val[$count - 1][0]) : strtoupper($val[$count - 1]) : ''; if (in_array($rule, ['AND', 'OR', 'XOR'])) { $count = $count - 1; } else { $rule = 'AND'; } for ($i = 0; $i < $count; $i++) { $data = is_array($val[$i]) ? $val[$i][1] : $val[$i]; if ('exp' == strtolower($val[$i][0])) { $whereStr .= $key . ' ' . $data . ' ' . $rule . ' '; } else { $whereStr .= $this->parseWhereItem($key, $val[$i]) . ' ' . $rule . ' '; } } $whereStr = '( ' . substr($whereStr, 0, -4) . ' )'; } } else { //对字符串类型字段采用模糊匹配 $likeFields = $this->config['db_like_fields']; if ($likeFields && preg_match('/^(' . $likeFields . ')$/i', $key)) { $whereStr .= $key . ' LIKE ' . $this->parseValue('%' . $val . '%'); } else { $whereStr .= $key . ' = ' . $this->parseValue($val); } } return $whereStr; }