我有一个这样的用户输入:$_POST['multi']['dim']['form'] = 1.213
.
我想验证这个,现在我使用这个函数:
public function checkFloat($number, $min = null, $max = null)
{
$num = filter_var($number, FILTER_VALIDATE_FLOAT, FILTER_FLAG_ALLOW_THOUSAND);
if($num === false || ($min !== null && $num < $min) || ($max !== null && $num > $max))
return false;
return $num;
}
但是在调用这个函数之前,我必须首先检查$_POST['multi']['dim']['form']
是否设置。
所以每个调用看起来都像
if(isset($_POST['multi']['dim']['form']) && ($num = checkFloat($_POST['multi']['dim']['form']) !== false))
{
// do something here
}
如果我不先检查,如果变量已经设置,PHP将抛出一个通知。我注意到PHP函数filter_input
,但似乎这是不与多维$_POST
工作。
我想到了一个像
这样的包装function checkFloat($names_of_the_fields,$min,$max)
{
// check if $_POST[$name][$of][$the][...] is set
// make the validation
}
但是我不确定如何通过$name_of_the_fields
。
这里我想到了一个数组$arr['key1']['key2'][...]
。但由于我不知道这有多深,我必须运行很多is_array
检查。或者我传递一个像$arr = ['key1','key2',...]
这样的数组
是否有一个好的和干净的方法来做到这一点?我应该忽略这个通知吗?
或者我应该继续使用if(isset.. && checkFloat...)
?
不能更改表单并使用eval()
。
Thanks in advance
编辑1:如果设置为$var
,则is_array($var)
不会那么慢。如果我用一个检查结构的函数,那就没问题了。但问题仍然是,如果这是一个好方法,或者是否有更好(也许更快)的方法来做到这一点。
这个怎么样?你会喜欢的:D
function filter_input_simple($type, $name, $filter = FILTER_DEFAULT, $options = FILTER_REQUIRE_SCALAR) {
static $vars;
if (!$vars) {
$vars = array(
INPUT_GET => filter_input(INPUT_SERVER, 'QUERY_STRING'),
INPUT_POST => file_get_contents('php://input'),
INPUT_COOKIE => filter_input(INPUT_SERVER, 'HTTP_COOKIE'),
);
$s = array('&', '&', ';[; ]++');
foreach ($vars as $t => $var) {
$tmp = array();
foreach (preg_split("@{$s[$t]}@", $var, -1, PREG_SPLIT_NO_EMPTY) as $i) {
list($k, $v) = explode('=', $i, 2) + array(1 => '');
$tmp[urldecode($k)] = urldecode($v);
}
unset($tmp['']);
$vars[$t] = $tmp;
}
$vars += array(INPUT_REQUEST =>
$vars[INPUT_COOKIE] + $vars[INPUT_POST] + $vars[INPUT_GET]
);
}
$type = (int)$type;
$name = filter_var($name);
if (!isset($vars[$type][$name])) {
return null;
}
return filter_var($vars[$type][$name], $filter, $options);
}
用法:
// This function does not use extracted values as $_GET,
// so put QueryString on your browser for testing.
//
// ?foo[bar][validated_float]=1,234.213
$options = array(
'options' => array(
'min_range' => 1234,
'max_range' => 1235,
),
'flags' => FILTER_FLAG_ALLOW_THOUSAND,
);
var_dump(filter_input_simple(INPUT_GET, 'foo[bar][validated_float]', FILTER_VALIDATE_FLOAT, $options));
经过一番思考和测试,我想到了这个:
我将每个验证函数拆分为两个函数,并添加了一个检查数组键是否存在的函数。
-
validate[Type]Var($val, $options)
:检查$val
是否包含有效值。(此处无变化) -
validate[Type]Input(&$inputArr,$keys,$options)
:调用getArrayValue(&array,$keys)
,如果key存在,则调用validate[Type]Val()对值进行验证。
public function getArrayValue(&array,$keys)
{
$key = array_shift($keys);
if(!isset($array[$key]))
return null;
if(empty($keys))
return $array[$key];
return $this->getArrayValue($array[$key],$keys);
}
public function validateTypeInput(&$inputArr, $keys, $options = [])
{
$value = $this->getArrayValue($inputArr, $keys);
if(isset($value))
return $this->validateTypeVal($value,$options);
else
return null; // or return anything else to show that the value was invalid
}
函数可通过
调用ValidationClass->validateTypeInput($_POST,['key1','key2','key3'],$options);
验证$_POST['key1']['key2']['key3']
.
注意:我为每种类型写了一个validateTypeInput
,因为它们中的一些是不同的。如。如果您验证复选框输入,您不希望未设置的输入被认为是无效的。
它不会验证传递给它的数据的结构,但它将确保每个项传递checkFloat
函数:
function validate_info($info) {
if (is_array($info)) {
// $info is an array, validate each of its children
foreach ($info as $item) {
// If item is invalid, stop processing
if (validate_info($item) === false) { return false; }
}
// If each $item was valid, then this $info is valid.
return true;
} else {
// $info is a single item
return checkFloat($info, [YOUR_MIN_VAL], [YOUR_MAX_VAL]);
}
}
如果$info
中的每个项对checkFloat
都返回true,则该函数将返回true。如果$info
中的任何值返回false,则validate_info
将返回false。该函数使用递归检查整个数组,而不考虑其结构。
edit:此函数确实使用is_array
函数,但您似乎错误地认为is_array
函数很慢。is_array
函数在常量时间内运行,这可以通过检查PHP源代码来验证。
类定义:
/**
* Object for filter_input_array_recursive().
*/
class FilterObject {
private $filter;
private $options;
/**
* Constructor.
*
* @param int $filter same as ones for filter_input().
* @param mixed $options same as ones for filter_input().
*/
public function __construct($filter = FILTER_DEFAULT, $options = FILTER_REQUIRE_SCALAR) {
$this->filter = $filter;
$this->options = $options;
}
public function getFilter() {
return $this->filter;
}
public function getOptions() {
return $this->options;
}
}
<标题>函数定义:/**
* Apply filter_input() recursively.
*
* @param int $type INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_REQUEST are supported.
* @param array $filters Multi-demensional array, which contains FilterObject for each leaf.
* @return array
*/
function filter_input_array_recursive($type, array $filters) {
static $recursive_static;
static $flag_match;
static $is_not_array;
static $filter_array;
static $types;
if (!$flag_match) {
/* initialize static variables */
$types = array(
INPUT_GET => $_GET,
INPUT_POST => $_POST,
INPUT_COOKIE => $_COOKIE,
INPUT_REQUEST => $_REQUEST,
);
$flag_match = function ($v, $f) {
return (int)(isset($v['flags']) ? $v['flags'] : $v) & $f;
};
$is_not_array = function ($v) {
return !is_array($v);
};
$filter_array = function ($v) {
return !is_array($v) ? $v : false;
};
}
$recursive = $recursive_static;
if (!$recursive) {
/* only for first loop */
$type = (int)$type;
if (!isset($types[$type])) {
throw new InvalidArgumentException('unknown super global var type');
}
$var = $types[$type];
$recursive_static = true;
} else {
/* after first loop */
$var = $type;
}
$ret = array();
foreach ($filters as $key => $value) {
$isset = isset($var[$key]);
if (is_array($value)) {
// apply child filters
$ret[$key] = filter_input_array_recursive($isset ? $var[$key] : array(), $value);
} else {
if (!($value instanceof FilterObject)) {
// create default FilterObject for invalid leaf
$value = new FilterObject;
}
$filter = $value->getFilter();
$options = $value->getOptions();
// check if key exists...
// true -> apply filter_var() with supplied filter and options
// false -> regard as null
try {
$ret[$key] = $isset ? filter_var($var[$key], $filter, $options) : null;
} catch (Exception $e) {
$recursive_static = false;
throw $e;
}
if ($flag_match($options, FILTER_FORCE_ARRAY | FILTER_REQUIRE_ARRAY)) {
// differently from filter_input(),
// this function prevent unexpected non-array value
if (!is_array($ret[$key])) {
$ret[$key] = array();
}
// differently from filter_input(),
// this function prevent unexpected multi-demensional array
if ($flag_match($options, FILTER_FORCE_ARRAY)) {
// eliminate arrays
$ret[$key] = array_filter($ret[$key], $is_not_array);
} else {
// change arrays into false
$ret[$key] = array_map($filter_array, $ret[$key]);
}
}
}
}
if (!$recursive) {
/* only for first loop */
$recursive_static = false;
}
return $ret;
}
<标题>用法:$_POST['foo']['bar']['validated_float'] = '1,234.213';
$_POST['foo']['forced_1d_array'] = array('a', array('b'), 'c');
$_POST['foo']['required_1d_array'] = array('a', array('b'), 'c');
$_POST['foo']['required_scalar'] = array('a', array('b'), 'c');
当输入像这样时,
var_dump(filter_input_array_recursive(INPUT_POST, array(
'foo' => array(
'bar' => array(
'validated_float' => new FilterObject(
FILTER_VALIDATE_FLOAT,
array(
'options' => array(
'min_range' => 1234,
'max_range' => 1235,
),
'flags' => FILTER_FLAG_ALLOW_THOUSAND,
)
),
),
'forced_1d_array' => new FilterObject(FILTER_DEFAULT, FILTER_FORCE_ARRAY),
'required_1d_array' => new FilterObject(FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
'required_scalar' => new FilterObject,
),
)));
这段代码将输出…
array(1) {
["foo"]=>
array(4) {
["bar"]=>
array(1) {
["validated_float"]=>
float(1234.213)
}
["forced_1d_array"]=>
array(2) {
[0]=>
string(1) "a"
[2]=>
string(1) "c"
}
["required_1d_array"]=>
array(3) {
[0]=>
string(1) "a"
[1]=>
bool(false)
[2]=>
string(1) "c"
}
["required_scalar"]=>
bool(false)
}
}
标题>标题>