558 lines
23 KiB
PHP
558 lines
23 KiB
PHP
<?php namespace Phpcmf\Library;
|
||
/**
|
||
* https://www.besyun.com
|
||
* BESCMS
|
||
* 本文件是框架系统文件,二次开发时不可以修改本文件,可以通过继承类方法来重写此文件
|
||
**/
|
||
|
||
// 表单验证类
|
||
class Form {
|
||
|
||
public $fields;
|
||
public $mfields;
|
||
|
||
protected $id = 0;
|
||
|
||
// 初始化
|
||
public function id($id) {
|
||
$this->id = (int)$id;
|
||
return $this;
|
||
}
|
||
|
||
// 获取表单临时存储数据
|
||
public function auto_form_data($name, $data) {
|
||
// 默认数据
|
||
$dt = \Phpcmf\Service::L('cache')->init('file')->get(md5($name));
|
||
if (!$dt) {
|
||
return $data;
|
||
}
|
||
$dt['is_form_cache'] = 1;
|
||
return $data ? $dt + $data : $dt;
|
||
}
|
||
|
||
// 删除表单临时存储数据
|
||
public function auto_form_data_delete($name) {
|
||
\Phpcmf\Service::L('cache')->init('file')->delete(md5($name));
|
||
}
|
||
|
||
/**
|
||
* 自动临时存储表单数据ajax
|
||
*/
|
||
public function auto_form_data_ajax($name) {
|
||
|
||
return '
|
||
$(function(){
|
||
setInterval("auto_form_data_ajax()", 5000);
|
||
});
|
||
function auto_form_data_ajax() {
|
||
$.ajax({
|
||
type: "POST",
|
||
url: "'.dr_web_prefix('index.php?s=api&c=api&m=save_form_data&name='.$name).'",
|
||
dataType: "json",
|
||
data: $("#myform").serialize(),
|
||
success: function(data){ }
|
||
});
|
||
}
|
||
function auto_form_data_delete() {
|
||
var index = layer.load(2, {
|
||
shade: [0.3,\'#fff\'],
|
||
time: 10000
|
||
});
|
||
$.ajax({
|
||
type: "GET",
|
||
url: "'.dr_web_prefix('index.php?s=api&c=api&m=delete_form_data&name='.$name).'",
|
||
dataType: "json",
|
||
success: function(json){
|
||
layer.close(index);
|
||
dr_tips(json.code, json.msg);
|
||
setTimeout("window.location.reload(true)", 3000)
|
||
}
|
||
});
|
||
}
|
||
';
|
||
}
|
||
|
||
/**
|
||
*
|
||
* 验证字段格式
|
||
* 字段名 => [name显示名称, rule验证规则(支持正则), length长度]
|
||
* field 自定义字段配置
|
||
* id 是否修改的条件
|
||
**/
|
||
|
||
public function validation($data, $config, $fields = [], $old = []) {
|
||
|
||
if (!$data) {
|
||
$data = [];
|
||
}
|
||
|
||
// 初始化信息
|
||
\Phpcmf\Service::L('Field')->old = $old;
|
||
\Phpcmf\Service::L('Field')->data = [];
|
||
|
||
$attach = []; // 附件信息
|
||
|
||
// 表单规则验证
|
||
if ($config) {
|
||
foreach ($config as $name => $t) {
|
||
// 长度验证
|
||
if ($t['length'] && dr_strlen($data[$name]) > $t['length']) {
|
||
return [[], ['name' => $name, 'error' => dr_lang('长度不规范')]];
|
||
}
|
||
// 规则验证
|
||
if ($t['rule']) {
|
||
foreach ($t['rule'] as $rule => $error) {
|
||
switch ($rule) {
|
||
case 'empty':
|
||
if (!$data[$name] && !dr_strlen($data[$name])) {
|
||
return [[], ['name' => $name, 'error' => $error]];
|
||
}
|
||
break;
|
||
case 'table':
|
||
if (!(preg_match('/^[a-z]+[0-9]+/i', (string)$data[$name]) || preg_match('/[a-z]+/i', (string)$data[$name]))) {
|
||
return [[], ['name' => $name, 'error' => $error]];
|
||
}
|
||
break;
|
||
case 'pinyin':
|
||
if (!preg_match('/[a-z0-9]+/i', (string)$data[$name])) {
|
||
return [[], ['name' => $name, 'error' => $error]];
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// 过滤
|
||
if ($t['filter']) {
|
||
foreach ($t['filter'] as $value) {
|
||
switch ($value) {
|
||
case 'intval':
|
||
$data[$name] = intval($data[$name]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$notfields = [];
|
||
$radio_link_required = [];
|
||
|
||
// 自定义字段验证
|
||
if ($fields) {
|
||
$post = [];
|
||
$this->fields = $fields;
|
||
// 格式化字段值
|
||
foreach ($fields as $fid => $field) {
|
||
// 验证字段对象的有效性
|
||
$obj = \Phpcmf\Service::L('Field')->get($field['fieldtype']);
|
||
if (!$obj) {
|
||
unset($fields[$fid]);
|
||
continue; // 对象不存在
|
||
}
|
||
$obj->init($field);
|
||
$name = $field['fieldname']; // 字段名称
|
||
$obj->id = $this->id;
|
||
// 非后台时
|
||
if (!IS_ADMIN) {
|
||
if (!$field['ismember']) {
|
||
$notfields[] = $field['fieldname']; // 无权限排除的字段
|
||
unset($fields[$fid]);
|
||
continue; // 前端字段筛选
|
||
} elseif ($obj->_not_edit($field, $old[$field['fieldname']])) {
|
||
unset($fields[$fid]);
|
||
$notfields[] = $field['fieldname']; // 无权限排除的字段
|
||
continue; // 前端禁止修改时
|
||
} elseif ($field['setting']['show_member'] && dr_array_intersect(\Phpcmf\Service::C()->member['groupid'], $field['setting']['show_member'])) {
|
||
unset($fields[$fid]);
|
||
$notfields[] = $field['fieldname']; // 无权限排除的字段
|
||
continue; // 非后台时 判断用户权限
|
||
}
|
||
} elseif (IS_ADMIN && $field['setting']['show_admin'] && !dr_in_array(1, \Phpcmf\Service::C()->admin['roleid'])
|
||
&& dr_array_intersect(\Phpcmf\Service::C()->admin['roleid'], $field['setting']['show_admin'])) {
|
||
$notfields[] = $field['fieldname']; // 无权限排除的字段
|
||
continue; // 后台时 判断管理员权限
|
||
}
|
||
|
||
// 验证字段
|
||
$validate = $field['setting']['validate']; // 字段验证规则
|
||
// 默认xss开关
|
||
$xss = 0;
|
||
if ($obj->close_xss) {
|
||
$xss = 0;
|
||
} elseif ($obj->use_xss) {
|
||
$xss = 1;
|
||
} elseif (isset($validate['xss']) && $validate['xss']) {
|
||
// 手动开启xss
|
||
$xss = 1;
|
||
}
|
||
// 从表单获取值
|
||
$post[$name] = $value = ($xss ? \Phpcmf\Service::L('Security')->xss_clean($data[$name]) : $data[$name]);
|
||
// 验证字段值
|
||
$frt = $obj->check_value($field, $value);
|
||
if ($frt) {
|
||
return [[], ['name' => $name, 'error' => $frt]];
|
||
}
|
||
// Radio联动字段
|
||
if ($field['fieldtype'] == 'Radio'
|
||
&& isset($field['setting']['option']['is_field_ld']) && $field['setting']['option']['is_field_ld']
|
||
&& isset($field['setting']['option']['field_ld'][$value])
|
||
&& isset($field['setting']['option']['field_ld'][$value]['hide'])
|
||
) {
|
||
$radio_link_required = dr_array2array($field['setting']['option']['field_ld'][$value]['hide'], $radio_link_required);
|
||
}
|
||
// 验证必填字段
|
||
if ($obj->is_validate && $validate['required'] && !dr_in_array($name, $radio_link_required)) {
|
||
if (IS_ADMIN && dr_in_array(1, \Phpcmf\Service::C()->admin['roleid'])) {
|
||
// 后台超管不验证必填
|
||
} else {
|
||
// 开始验证必填字段
|
||
$frt = $obj->check_required($field, $value);
|
||
if ($frt) {
|
||
return [[], ['name' => $name, 'error' => $frt]];
|
||
}
|
||
}
|
||
// 正则验证
|
||
if (!is_array($value) && $validate['pattern'] && !preg_match($validate['pattern'], $value)) {
|
||
return [[], ['name' => $name, 'error' => $field['name'].':'.($validate['errortips'] ? dr_lang($validate['errortips']) : dr_lang('格式不正确'))]];
|
||
}
|
||
}
|
||
// 编辑器长度判断(依据 php.ini 的 post_max_size,预留附件与其它字段空间)
|
||
if (stripos($field['fieldtype'], 'editor') !== false && dr_strlen($data[$name]) > dr_form_editor_max_length()) {
|
||
return [[], ['name' => $name, 'error' => dr_lang('%s长度超限', $field['name'])]];
|
||
}
|
||
// 函数/方法校验
|
||
if ($validate['check']) {
|
||
if (strpos($validate['check'], '_') === 0) {
|
||
// 方法格式
|
||
$method = substr($validate['check'], 1);
|
||
if (method_exists($this, $method)) {
|
||
if ('check_member' == $method && $value == 'guest') {
|
||
// 游客不验证
|
||
} else {
|
||
$rt = call_user_func_array([$this, $method], [$value, $data, $old]);
|
||
if (!$rt['code']) {
|
||
return [[], ['name' => $name, 'error' => $rt['msg']]];
|
||
}
|
||
}
|
||
} else {
|
||
log_message('error', "校验方法 $method 不可用");
|
||
}
|
||
} else {
|
||
// 函数格式
|
||
$func = $validate['check'];
|
||
if (dr_is_call_function($func)) {
|
||
$rt = call_user_func_array($func, [$value, $data, $old]);
|
||
if (!$rt['code']) {
|
||
return [[], ['name' => $name, 'error' => $rt['msg']]];
|
||
}
|
||
} else {
|
||
log_message('error', "校验函数 $func 不可用");
|
||
}
|
||
}
|
||
}
|
||
// 过滤函数/方法
|
||
if ($validate['filter']) {
|
||
if (strpos($validate['filter'], '_') === 0) {
|
||
// 方法格式
|
||
$method = substr($validate['filter'], 1);
|
||
if (method_exists($this, $method)) {
|
||
// 开始过滤
|
||
$post[$name] = call_user_func_array([$this, $method], [$value, $data, $old]);
|
||
} else {
|
||
log_message('error', "过滤方法 $method 不可用");
|
||
}
|
||
} else {
|
||
// 函数格式
|
||
$func = $validate['filter'];
|
||
if (dr_is_call_function($func)) {
|
||
// 开始过滤
|
||
$post[$name] = call_user_func_array($func, [$value, $data, $old]);
|
||
} else {
|
||
log_message('error', "过滤函数 $func 不可用");
|
||
}
|
||
}
|
||
}
|
||
// 判断表字段值的唯一性
|
||
if ($field['ismain'] && $field['setting']['option']['unique']) {
|
||
if (empty(dr_strlen($post[$name]))) {
|
||
return [[], ['name' => $name, 'error' => dr_lang('%s不能为空', $field['name'])]];
|
||
}
|
||
if (\Phpcmf\Service::C()->init['table']) {
|
||
$table = \Phpcmf\Service::M()->dbprefix(\Phpcmf\Service::C()->init['table']);
|
||
if (\Phpcmf\Service::M()->db->fieldExists($name, $table)) {
|
||
$rt = \Phpcmf\Service::M()->db->table(\Phpcmf\Service::C()->init['table'])->where('id<>'. $this->id)->where($name, $post[$name])->countAllResults();
|
||
if ($rt) {
|
||
return [[], ['name' => $name, 'error' => dr_lang('%s已经存在', $field['name'])]];
|
||
}
|
||
} else {
|
||
log_message('error', "字段唯一性验证失败:表".$table."中字段".$name."不存在!");
|
||
}
|
||
} else {
|
||
log_message('error', "字段唯一性验证失败:数据表不存在!");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 存储附件id
|
||
$attach['add'] = $attach['del'] = [];
|
||
|
||
// 主表附表归类
|
||
foreach ($fields as $field) {
|
||
// 验证字段对象的有效性
|
||
$obj = \Phpcmf\Service::L('Field')->get($field['fieldtype'], $this->id, $post);
|
||
if (!$obj) {
|
||
continue; // 对象不存在
|
||
}
|
||
$obj->insert_value($field); // 格式化入库值
|
||
// 处理附件归档
|
||
if (SYS_ATTACHMENT_DB) {
|
||
// 通过附件处理方法获得增加和删除的附件
|
||
list($add_id, $del_id) = $obj->attach(
|
||
\Phpcmf\Service::L('Field')->data[$field['ismain']][$field['fieldname']],
|
||
$old[$field['fieldname']]
|
||
);
|
||
$attach['add'] = $add_id ? dr_array2array($add_id, $attach['add']) : $attach['add'];
|
||
$attach['del'] = $del_id ? dr_array2array($del_id, $attach['del']) : $attach['del'];
|
||
}
|
||
}
|
||
// 格式化后的数据
|
||
$data = \Phpcmf\Service::L('Field')->data;
|
||
// 获取uid
|
||
}
|
||
|
||
|
||
return [$data, [], $attach, $notfields];
|
||
}
|
||
|
||
// 获取已发短信验证码
|
||
public function get_mobile_code($phone) {
|
||
return \Phpcmf\Service::L('cache')->get_auth_data('phone-code-'.$phone, SITE_ID, defined('SYS_CACHE_SMS') && SYS_CACHE_SMS ? SYS_CACHE_SMS : 300);
|
||
}
|
||
|
||
// 储存已发短信验证码
|
||
public function set_mobile_code($phone, $code) {
|
||
return \Phpcmf\Service::L('cache')->set_auth_data('phone-code-'.$phone, $code, SITE_ID);
|
||
}
|
||
|
||
// 验证码类
|
||
public function check_captcha($id) {
|
||
|
||
// API请求时,是否进行验证图片
|
||
if (IS_API_HTTP && defined('SYS_API_CODE') && !SYS_API_CODE) {
|
||
return true;
|
||
}
|
||
|
||
if (!IS_ADMIN && function_exists('my_check_captcha')) {
|
||
return my_check_captcha($id);
|
||
}
|
||
|
||
$data = trim((string)\Phpcmf\Service::L('input')->post($id));
|
||
if (!$data) {
|
||
IS_DEV && log_message('debug', '图片验证码验证失败:没有输入验证码'.dr_safe_replace(\Phpcmf\Service::L('input')->ip_address().':'.\Phpcmf\Service::L('input')->get_user_agent()));
|
||
return false;
|
||
}
|
||
|
||
$code = \Phpcmf\Service::L('cache')->get_auth_data('web-captcha-'.USER_HTTP_CODE, SITE_ID, 300);
|
||
if (!$code) {
|
||
IS_DEV && log_message('error', '图片验证码未生成('.USER_HTTP_CODE.')'.dr_safe_replace(\Phpcmf\Service::L('input')->ip_address().':'.\Phpcmf\Service::L('input')->get_user_agent()));
|
||
return false;
|
||
} elseif (strtolower($data) == strtolower($code)) {
|
||
\Phpcmf\Service::L('cache')->del_auth_data('web-captcha-'.USER_HTTP_CODE, SITE_ID);
|
||
return true;
|
||
}
|
||
|
||
IS_DEV && log_message('debug', '图片验证码验证失败:你输入的是('.$data.'),正确的是('.$code.')'.dr_safe_replace(\Phpcmf\Service::L('input')->ip_address().':'.\Phpcmf\Service::L('input')->get_user_agent()));
|
||
|
||
return false;
|
||
}
|
||
|
||
// 验证码类:只比较不删除
|
||
public function check_captcha_value($data) {
|
||
|
||
// API请求时,是否进行验证图片
|
||
if (IS_API_HTTP && defined('SYS_API_CODE') && !SYS_API_CODE) {
|
||
return true;
|
||
} elseif (!$data) {
|
||
IS_DEV && log_message('debug', '图片验证码验证失败:没有输入验证码');
|
||
return false;
|
||
}
|
||
|
||
$data = trim((string)$data);
|
||
if (!IS_ADMIN && function_exists('my_check_captcha_value')) {
|
||
return my_check_captcha_value($data);
|
||
}
|
||
|
||
$code = \Phpcmf\Service::L('cache')->get_auth_data('web-captcha-'.USER_HTTP_CODE, SITE_ID, 300);
|
||
if (!$code) {
|
||
IS_DEV && log_message('error', '图片验证码未生成('.USER_HTTP_CODE.')'.dr_safe_replace(\Phpcmf\Service::L('input')->ip_address().':'.\Phpcmf\Service::L('input')->get_user_agent()));
|
||
return false;
|
||
} elseif (strtolower($data) == strtolower($code)) {
|
||
\Phpcmf\Service::L('cache')->del_auth_data('web-captcha-'.USER_HTTP_CODE, SITE_ID);
|
||
return true;
|
||
}
|
||
|
||
IS_DEV && log_message('debug', '图片验证码验证失败:你输入的是('.$data.'),正确的是('.$code.')'.dr_safe_replace(\Phpcmf\Service::L('input')->ip_address().':'.\Phpcmf\Service::L('input')->get_user_agent()));
|
||
|
||
return false;
|
||
}
|
||
|
||
// 验证手机号码长度
|
||
public function check_phone($value) {
|
||
|
||
if (!$value) {
|
||
return false;
|
||
} elseif (dr_strlen($value) > 30) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 验证邮件地址
|
||
public function check_email($value) {
|
||
|
||
if (!$value) {
|
||
return false;
|
||
} elseif (!preg_match('/^[\w\-\.]+@[\w\-\.]+(\.\w+)+$/', $value)) {
|
||
return false;
|
||
} elseif (strpos($value, '"') !== false || strpos($value, '\'') !== false) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 验证账号
|
||
public function check_username($value) {
|
||
|
||
if (!$value) {
|
||
return dr_return_data(0, dr_lang('账号不能为空'), ['field' => 'username']);
|
||
} elseif (\Phpcmf\Service::C()->member_cache['register']['preg']
|
||
&& !preg_match(\Phpcmf\Service::C()->member_cache['register']['preg'], $value)) {
|
||
// 验证账号的组成格式
|
||
return dr_return_data(0, dr_lang('账号格式不正确'), ['field' => 'username']);
|
||
} elseif (strpos($value, '"') !== false
|
||
|| strpos($value, '<') !== false
|
||
|| strpos($value, '>') !== false
|
||
|| strpos($value, '\'') !== false
|
||
) {
|
||
// 引号判断
|
||
return dr_return_data(0, dr_lang('账号名存在非法字符'), ['field' => 'username']);
|
||
} elseif (\Phpcmf\Service::C()->member_cache['config']['userlen']
|
||
&& mb_strlen($value) < \Phpcmf\Service::C()->member_cache['config']['userlen']) {
|
||
// 验证账号长度
|
||
return dr_return_data(0, dr_lang('账号长度不能小于%s位,当前%s位', \Phpcmf\Service::C()->member_cache['config']['userlen'], mb_strlen($value)), ['field' => 'username']);
|
||
} elseif (\Phpcmf\Service::C()->member_cache['config']['userlenmax']
|
||
&& mb_strlen($value) > \Phpcmf\Service::C()->member_cache['config']['userlenmax']) {
|
||
// 验证账号长度
|
||
return dr_return_data(0, dr_lang('账号长度不能大于%s位,当前%s位', \Phpcmf\Service::C()->member_cache['config']['userlenmax'], mb_strlen($value)), ['field' => 'username']);
|
||
}
|
||
$notallow = \Phpcmf\Service::C()->member_cache['register']['notallow'];
|
||
if (!$notallow || !is_array($notallow)) {
|
||
$notallow = [];
|
||
}
|
||
$notallow[] = dr_lang('游客');
|
||
// 后台不允许注册的词语,放在最后一次比较
|
||
foreach ($notallow as $a) {
|
||
if (dr_strlen($a) && strpos($value, $a) !== false) {
|
||
return dr_return_data(0, dr_lang('账号名不允许注册'), ['field' => 'username']);
|
||
}
|
||
}
|
||
|
||
return dr_return_data(1, 'ok');
|
||
}
|
||
|
||
// 验证账号的密码
|
||
public function check_password($value, $username) {
|
||
|
||
if (!$value) {
|
||
return dr_return_data(0, dr_lang('密码不能为空'), ['field' => 'password']);
|
||
} elseif (!\Phpcmf\Service::C()->member_cache['config']['user2pwd'] && $value == $username) {
|
||
return dr_return_data(0, dr_lang('密码不能与账号相同'), ['field' => 'password']);
|
||
} elseif (\Phpcmf\Service::C()->member_cache['config']['pwdpreg']
|
||
&& !preg_match(trim(\Phpcmf\Service::C()->member_cache['config']['pwdpreg']), $value)) {
|
||
return dr_return_data(0, dr_lang('密码格式不正确'), ['field' => 'password']);
|
||
} elseif (\Phpcmf\Service::C()->member_cache['config']['pwdlen']
|
||
&& mb_strlen($value) < \Phpcmf\Service::C()->member_cache['config']['pwdlen']) {
|
||
return dr_return_data(0, dr_lang('密码长度不能小于%s位,当前%s位', \Phpcmf\Service::C()->member_cache['config']['pwdlen'], mb_strlen($value)), ['field' => 'password']);
|
||
} elseif (mb_strlen($value) > 100) {
|
||
return dr_return_data(0, dr_lang('密码长度不能过长'), ['field' => 'password']);
|
||
}
|
||
|
||
return dr_return_data(1, 'ok');
|
||
}
|
||
|
||
// 验证姓名
|
||
public function check_name($value) {
|
||
|
||
if (!$value) {
|
||
return false;
|
||
} elseif (\Phpcmf\Service::C()->member_cache['register']['cutname'] && mb_strlen($value) > \Phpcmf\Service::C()->member_cache['register']['cutname']) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 验证域名
|
||
public function check_domain($value) {
|
||
|
||
if (!$value) {
|
||
return false;
|
||
}
|
||
|
||
foreach (['/', '?', '&', '\\', '*', ' ', '..', '(', ')', '\'', '"', ',', ';'] as $p) {
|
||
if (strpos($value, $p) !== false) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 验证目录式域名
|
||
public function check_domain_dir($value) {
|
||
|
||
if (!$value) {
|
||
return false;
|
||
}
|
||
|
||
foreach (['/', '?', '&', '\\', '*', ' ', '..', '(', ')', '\'', '"', ',', ';'] as $p) {
|
||
if (strpos($value, $p) !== false) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (substr_count($value, '/') > 1) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 生成随机验证码
|
||
public function get_rand_value() {
|
||
return rand(100000, 999999);
|
||
}
|
||
|
||
|
||
|
||
//=====校验方法=========
|
||
|
||
/**
|
||
* 验证会员名称是否存在
|
||
*
|
||
* @param $value 当前字段提交的值
|
||
* @return flase不通过 , true通过
|
||
*/
|
||
public function check_member($value) {
|
||
|
||
if (!$value || dr_lang('游客') == $value) {
|
||
return dr_return_data(1);
|
||
} elseif (!\Phpcmf\Service::M('member')->uid($value)) {
|
||
return dr_return_data(0, dr_lang('账号【%s】不存在', $value));
|
||
}
|
||
|
||
return dr_return_data(1);
|
||
}
|
||
} |