LoginSignup
49

More than 1 year has passed since last update.

filter_input_array_recursive

Last updated at Posted at 2013-08-06

【2021/10/15 追記】
この記事は更新が停止されています。現在では筆者の思想が変化している面もありますので,過去の記事として参考程度にご覧ください。

これは何?

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49