验证多维$_POST的优雅方式



我有一个这样的用户输入:$_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)
  }
}

相关内容

  • 没有找到相关文章