Edited at

filter_input_array_recursive

More than 3 years have passed since last update.


これは何?

filter_input_array 関数の惜しいところを使いやすく、安全にした強化版です。

フィルタリングされる変数名を多次元配列で定義できます。

cURLにおけるファイルアップロードに関する問題の解決策としてPHP公式が採用した葉要素にオプション専用のクラスを用意するという手段を真似て、任意のフィルタによる再帰的フィルタリングを可能にしました。(詳しくは 「PHPでcURLのクソ仕様 "@" を回避する」 で紹介しています)


filter_input_simple 関数との比較

filter_input_simple
filter_input_array_recursive

パフォーマンス(初回コール時)

パフォーマンス(2回目以降)

ネストした配列への対応

ネストした任意の配列への対応
×


クラスと関数の定義


FilterObject クラス


コンストラクタのシグネチャ

public __construct(int $filter = FILTER_DEFAULT, mixed $options = FILTER_REQUIRE_SCALAR)


コンストラクタの引数


$filter

filter_input 関数の引数と同様です。


$options

filter_input 関数の引数と同様です。


ソースコード

/**

* 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;
}

}


filter_input_array_recursive 関数


シグネチャ

array filter_input_array_recursive(int $type, array $filters)


引数


$type

INPUT_GET INPUT_POST INPUT_COOKIE INPUT_REQUEST のいずれか。


$filters

以下の形式の要素で構成される配列。

"name属性値" => FilterObjectのインスタンス


  • 配列がネストしても構いません。

  • FilterObjectのインスタンス以外を渡した場合、 new FilterObject で生成されるデフォルトオプションが適用されます。


返り値

フィルタリングした配列を返します。


ソースコード

/**

* Apply filter_input() recursively.
*
* @param int $type INPUT_GET, INPUT_POST, INPUT_COOKIE or INPUT_REQUEST.
* @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) {
if (is_array($value)) {
// apply child filters
$ret[$key] = filter_input_array_recursive(
isset($var[$key]) ? $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($var[$key]) ?
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
// or multi-demensional array
if ($flag_match($options, FILTER_FORCE_ARRAY)) {
// eliminate arrays
$ret[$key] = array_filter((array)$ret[$key], $is_not_array);
} else {
// change arrays into false
$ret[$key] = array_map($filter_array, (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' => null,
),
)));


実行結果

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)
}
}