PHP Logo filter_input と filter_var

PHP のフィルタ関数 filter_input() と filter_var() の基本的な使い方についての覚書です(NULL 合体演算子を追加しました)。

更新日:2024年03月25日

作成日:2021年10月28日

関連ページ

filter_input

PHP5.2 以降では filter_input() を使って POST メソッドなどで送信した値(外部からの変数)をフィルタリング(検証やサニタイズ処理)することができます。

指定可能な外部からの変数は以下のスーパーグローバル変数になります。

  • $_GET
  • $_POST
  • $_COOKIE
  • $_SERVER
  • $_ENV

入力値ではない値(任意のデータ)をフィルタリングするには filter_var() を使います。

例えば以下をページに記述すると、$_POST['name'] はまだ存在しない(定義されていない)ため、エラーになってしまいます(例えば、フォームを設置していてページに最初にアクセスした際にまだ POST メソッドでデータが送信されていない場合)。

<?php
$name = $_POST['name'];  //エラー Notice: Undefined index: name
echo $name;
?>

変数が定義されているかどうかを調べる関数 isset() を使って以下のように記述することでエラーにならないようにすることができます。

if(isset($_POST['name'])){
  //変数が定義されていればその値を変数に代入
  $name = $_POST['name'];
} else {
  //変数が定義されていなければ null(NULL)で初期化
  $name = null;  //または $name = NULL;
}
echo $name; 
または三項演算子を使って以下のように記述しても同じです。
$name = isset($_POST['name']) ? $_POST['name'] : null;
echo $name;

filter_input() を使うと上記と同じことを以下のように簡潔に記述することができます。

$name = filter_input(INPUT_POST, 'name');
echo $name; 

filter_input() は指定された名前のスーパーグローバル変数をオプションでフィルタリングします。

上記の場合、フィルタは指定していないので、指定された変数が存在すればその値を返し、存在しない場合は null を返します(前述の2つのコードと同じこと)。

PHP7移行であれば NULL 合体演算子を使って以下のように簡潔に記述することもできます。

但し、$_POST や $_GET などの外部からの変数を扱う場合、filter_input() では実際に送信された値を使い、フィルタリングすることができます。

//NULL 合体演算子(??)を使った初期化
$name = $_POST['name'] ?? null; 

null / NULL

null 型の値は一つだけで、 大文字小文字を区別しない定数です(null でも NULL でも同じ)。

null を echo

PHP では null や false を echo または print すると空文字列("")が出力されます(文字列への変換)。

書式とパラメータ

以下が filter_input() の書式とパラメータです。

filter_input( $type, $var_name, $filter, $options )
パラメータ 説明
$type

フィルタリングする変数に合わせて以下のいずれかの定数を指定します($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV に対応しています )。

例:$_POST['name'] をフィルタリングする場合は、INPUT_POST。

  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_SERVER
  • INPUT_ENV
$var_name

フィルタリングする変数の名前。

例:$_POST['name'] をフィルタリングする場合は 'name'

$filter

適用するフィルタの種類を ID(定数)で指定します。省略した場合は FILTER_DEFAULT(FILTER_UNSAFE_RAW と同等)が適用され、フィルタリングをしません。

指定できるフィルタは以下に記載されています。

$options

フィルタに指定するオプションやフラグを指定します(フィルタごとに指定できるオプションやフラグは異なります)。

オプションを指定する場合オプションとフラグの両方を指定する場合は、連想配列で指定します。

複数のフラグを指定する場合は「|」で区切って指定します。

変数の値が配列の場合は、フラグに FILTER_REQUIRE_ARRAY を指定します。

返り値

成功した場合は(フィルタリングされた)指定した変数の値、フィルタリングに失敗した場合は false、指定した変数 $var_name が設定されていない場合は null を返します。

第4パラメータにフラグ FILTER_NULL_ON_FAILURE を指定すると、フィルタリングに失敗したら null、変数 $var_name が設定されていなければ false を返します。

$_POST['name'] がまだ送信されていない場合
$name = filter_input(INPUT_POST, "name");
var_dump($name);  //NULL

//フラグ FILTER_NULL_ON_FAILURE を指定
$name = filter_input(INPUT_POST, "name", FILTER_DEFAULT , FILTER_NULL_ON_FAILURE);
var_dump($name);  //bool(false)

外部からの変数を扱う

filter_input() は実際に送信された値(外部からの変数)を使う

filter_input() では、スーパーグローバル変数の $_GET や $_POST の値ではなく、実際に送信された値をフィルタリングします。

以下はスーパーグローバル変数 $_POST['name'] をプログラム上で書き換えた例です。次の例の場合、何らかの値を入力して送信しても常に $_POST['name'] に代入された値 xxx が出力されます。

<?php
$_POST['name'] = 'xxx';  //スーパーグローバル変数に xxx を代入
$name = $_POST['name'];
?>
<form method="post">
  <input type="text" name="name" value="">
  <input type="submit" >
</form>
<p>name: <?php echo $name; //代入された値 xxx が出力される ?></p>

filter_input() を使うと、スーパーグローバル変数が書き換えられても実際にサーバーに送信された値(この場合、POST メソッドで送信された値)が使われます。

<?php
$_POST['name'] = 'xxx';  //スーパーグローバル変数に xxx を代入
$name = filter_input(INPUT_POST, "name");
?>
<form method="post">
  <input type="text" name="name" value="">
  <input type="submit" >
</form>
<p>name: <?php echo $name; //送信された値が出力される?></p>

フィルタ

第3パラメータに指定するフィルタ($filter)は以下の3つに分類されています。

フィルタ 説明
検証フィルタ

FILTER_VALIDATE_ で始まるフィルタで、値を検証して問題がなければその値を返します。

値に問題があれば false を、値が存在しない場合は null を返します。

フラグ FILTER_NULL_ON_FAILURE を指定すると、値に問題があれば null を、値が存在しない場合は false を返します。

変数が存在するか(定義されているか)の確認と型のチェックなどができます。

除去フィルタ

FILTER_SANITIZE_ で始まるフィルタで、値を検証して問題がなければその値を返します。

値に不適切な文字が含まれている場合は、不適切な文字を削除または変換(エンコード)した値を返します。

除去(変換)後のデータの妥当性の検証は行いません。

その他のフィルタ FILTER_CALLBACK フィルタに独自のコールバック関数を設定できます。

また、フィルタの種類により、指定できるフラグやオプションが用意されています。

検証フィルタ

filter_input() の第3パラメータに FILTER_VALIDATE_ で始まる検証フィルタを指定する例です。

メールアドレスを検証

以下は POST で入力された値('email')を FILTER_VALIDATE_EMAIL でフィルタリングしてメールアドレスを検証する例です。

正しいメールアドレスを入力して送信すると、$email にはそのままの文字列が代入されます。

正しくないメールアドレスの形式を入力して送信すると、$email は false になります。

また、初回に表示される際は、まだ $_POST['email'] は定義されていないので、$email は NULL になります。

<?php
$email = filter_input( INPUT_POST, 'email', FILTER_VALIDATE_EMAIL );
var_dump( $email );
?>

<form name="myForm" method="post">
  <div>
    <label for="email">Email</label>
    <input type="email" name="email" value="">
  </div>
  <button name="send" type="submit">送信</button>
</form>

上記サンプルや以降のサンプルでは、value 属性に送信された値(この例の場合は $email)をエスケープ処理して出力するようにしています。フォームの送信前では、値は送信されていないので filter_input() により値は NULL になり、空文字が出力されます。

2024/3/25 追記

PHP 8.1.x から、ビルトイン関数の nullable でない引数に null を渡すことが非推奨となっています。

例えば htmlspecialchars() の第1引数に null を渡すと Deprecated エラーになるので、以下のサンプルコードの関数 h() では引数が null の場合は空文字列を返すようにしています(7行目)。

PHP マニュアル:PHP 8.1.x で推奨されなくなる機能

<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return ''; // PHP 8.1.x 対策(null を渡すと Deprecated エラー)
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
$email = filter_input( INPUT_POST, 'email', FILTER_VALIDATE_EMAIL );
?>
<div class="container">
  <form name="myForm" method="post">
    <div>
      <label for="email">Email</label>
      <input type="email" id="email" name="email" placeholder="Email アドレス" value="<?php echo h($email); ?>" size="30">
    </div>
    <button name="send" type="submit">送信</button>
  </form>
  <div class="result">var_dump :
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $email );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>

以下は第4パラメータにフラグ FILTER_FLAG_EMAIL_UNICODE を指定して、email アドレスのローカルパート(@ より前の部分)に Unicode 文字を含めることを許可する例です(PHP 7.1.0 以降)。

$email = filter_input( INPUT_POST, 'email', FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE );
オプションの指定

フィルタの種類によって、指定できるオプション($options)がいくつか用意されています。どのようなオプションが使えるかは検証フィルタのページのオプション欄で確認できます。

オプションは options をキーとする連想配列で第4パラメータに指定します。

以下は値が整数であるかどうかを検証し、成功した場合は整数に変換するフィルタ FILTER_VALIDATE_INT にオプション(min_range と max_range)を指定する例です。

以下の場合、値が整数でオプションで指定した範囲(1〜100)であればその値を返し、そうでなければ false を返します。初回に表示される際は、まだ $_POST['number'] は定義されていないので、$number は NULL になります。

<?php
//オプションで数値範囲(1〜100)を指定
$options = array('options' => array('min_range' => 1, 'max_range' => 100));

//第4パラメータに上記で定義したオプション($options)を指定
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
var_dump( $number );
?>

<form name="myForm" method="post">
  <input type="text" name="number" value="">
  <button name="send" type="submit">送信</button>
</form>
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
$options = array('options' => array('min_range' => 1, 'max_range' => 100));
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <label for="number">Number</label>
    <input type="text" id="number" name="number" placeholder="数値(1〜100)" value="<?php echo h($number); ?>" size="30">
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump :
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $number );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>
デフォルト値を指定

以下は前述の例にオプションのデフォルト値を追加で指定する例です。

以下の場合、指定した範囲外の値は default で指定した 50 が使われます。また初回表示される際の初期値も 50 になります。

//オプションで数値範囲(1〜100)とデフォルト値(50)を指定
$options = array('options' =>
                 array(
                   'min_range' => 0,
                   'max_range' => 100,
                   'default' => 50
                 ));
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
var_dump( $number );
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
//オプションで数値範囲(1〜100)とデフォルト値(50)を指定
$options = array('options' =>
                 array(
                   'min_range' => 0,
                   'max_range' => 100,
                   'default' => 50
                 ));
//第4パラメータに上記で定義したオプション($options)を指定
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <label for="number">Number</label>
    <input type="text" id="number" name="number" placeholder="数値(1〜100)" value="<?php echo h($number); ?>" size="30">
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump :
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $number );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>
オプションとフラグを指定

以下はオプションとフラグを指定する例です。options と flags をキーとした連想配列で指定します。

フラグ FILTER_NULL_ON_FAILURE を指定しているので、値が整数でない場合やオプションで指定した範囲内でない場合は NULL を返します。$_POST['number'] が定義されていない場合は、$number は false になります。

//オプションで数値範囲(1〜100)とフラグ(FILTER_NULL_ON_FAILURE)を指定
$options = array('options' =>
                 array(
                   'min_range' => 0,
                   'max_range' => 100
                 ),
                'flags' => FILTER_NULL_ON_FAILURE);  //フラグ
//第4パラメータに上記で定義した $options (options と flags)を指定
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
var_dump( $number );
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
//オプションで数値範囲(1〜100)とフラグ(FILTER_NULL_ON_FAILURE)を指定
$options = array('options' =>
                 array(
                   'min_range' => 0,
                   'max_range' => 100
                 ),
                'flags' => FILTER_NULL_ON_FAILURE);
//第4パラメータに上記で定義した $options (options と flags)を指定
$number = filter_input( INPUT_POST, 'number', FILTER_VALIDATE_INT, $options);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <label for="number">Number</label>
    <input type="text" id="number" name="number" placeholder="数値(1〜100)" value="<?php echo h($number); ?>" size="30">
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump :
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $number );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>
コールバック関数を指定

第3パラメータの $filter に FILTER_CALLBACK フィルタを指定して独自のコールバック関数を適用することができます。

コールバック関数の定義では値を引数に受け取って値を処理して返したり、値が条件に合う場合はその値を返し、適合しない場合は false を返すなどが考えられます。

以下は POST メソッドで送信された値の初期化とコールバック関数で trim() を使って前後の空白文字を削除する例です。$val は初回アクセス時にはまだ POST メソッドで送信されていので NULL になります。

<?php
//コールバック関数の定義
function trim_value($value){
  return trim($value);
}
//オプションにコールバック関数(trim_value)を指定
$options = array('options' => 'trim_value');

//第4パラメータに上記で定義した $options (コールバック関数)を指定
$val = filter_input( INPUT_POST, 'val', FILTER_CALLBACK, $options);
//または、まとめて以下のように記述しても同じ
//$val = filter_input( INPUT_POST, 'val', FILTER_CALLBACK, ['options' => 'trim_value']);

var_dump( $val );
?>
<form name="myForm" method="post">
  <input type="text" id="val" name="val" value="<?php echo h($val); ?>" size="30">
  <button name="send" type="submit">送信</button>
</form>
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}

//コールバック関数の定義
function trim_value($value){
  return trim($value);
}
//オプションにコールバック関数(trim_value)を指定
$options = array('options' => 'trim_value');

//第4パラメータに上記で定義した $options (コールバック関数)を指定
$val = filter_input( INPUT_POST, 'val', FILTER_CALLBACK, $options);
//または、まとめて以下のように記述しても同じ
//$val = filter_input( INPUT_POST, 'val', FILTER_CALLBACK, ['options' => 'trim_value']);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <label for="val">Name</label>
    <input type="text" id="val" name="val" value="<?php echo h($val); ?>" size="30">
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump( $val );
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $val );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>

以下は電話番号の形式(ハイフンやドット、スペース区切りの半角数字)を検証する独自のコールバック関数を指定する例です。コールバック関数では正規表現の preg_match() を使って電話番号の形式を判定して、正しい形式の場合はその値を返し、そうでない場合は false を返しています。

<?php
//コールバック関数の定義
function valid_tel($tel){
  //電話番号の形式(半角数字とハイフン、ドット、スペース区切り)に合致しない場合は false を返す
  $tel = trim($tel);
  if ( preg_match( '/\A\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}\z/u', $tel ) ){
    //正しい形式の場合はその値を返す
    return $tel;
  }else{
    return false;
  }
}
//オプションにコールバック関数(valid_tel)を指定
$options = array('options' => 'valid_tel');

//第3パラメータに FILTER_CALLBACK、第4パラメータに上記 $options を指定
$tel = filter_input( INPUT_POST, 'tel', FILTER_CALLBACK, $options);
//または、まとめて以下のように記述しても同じ
//$tel = filter_input( INPUT_POST, 'tel', FILTER_CALLBACK, array('options' => 'valid_tel'));

var_dump( $tel );
?>

<form name="myForm" method="post">
   <input type="tel" name="tel" value="">
  <button name="send" type="submit">送信</button>
</form>
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}

//コールバック関数の定義
function valid_tel($tel){
  //電話番号の形式(半角数字とハイフン、ドット、スペース区切り)に合致しない場合は false を返す
  $tel = trim($tel);
  if ( preg_match( '/\A\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}\z/u', $tel ) ){
    //正しい形式の場合はその値を返す
    return $tel;
  }else{
    return false;
  }
}
//オプションにコールバック関数(valid_tel)を指定
$options = array('options' => 'valid_tel');

//第4パラメータに上記で定義した $options (コールバック関数)を指定
$tel = filter_input( INPUT_POST, 'tel', FILTER_CALLBACK, $options);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <label for="tel">Telephone</label>
    <input type="tel" id="tel" name="tel" placeholder="電話番号(半角数字)" value="<?php echo h($tel); ?>" size="30">
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump( $tel );
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $tel );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
</div>

配列を扱う

複数のチェックボックスなど配列として送信されたデータを filter_input で受け取る場合は、第4パラメータのフラグに FILTER_REQUIRE_ARRAY を指定します。

チェックボックス

以下は複数のチェックボックスの name 属性の値に [ ] を付けて配列としてデータを送信する例です。

第4パラメータを指定する場合、第3パラメータは省略できないので、以下の例ではデフォルトの FILTER_DEFAULT を指定しています。

以下の例では、チェックボックスのいずれかが選択されていれば(送信された $hobby に要素があれば)、各要素のキーと値を出力しています。

<?php
//第4パラメータに FILTER_REQUIRE_ARRAY を指定
$hobby = filter_input( INPUT_POST, 'hobby', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
var_dump( $hobby );

//送信された $hobby に要素があれば、各要素のキーと値を出力
if (!empty($hobby)) {
  foreach($hobby as $key => $val){
    echo "key: " .$key ."  val: " .$val. "<br>";
  }
}
?>

<form name="myForm" method="post">
  <div>
    <input type="checkbox" name="hobby[]" id="sports" value="Sports">
    <label for="sports"> スポーツ</label>
    <input type="checkbox" name="hobby[]" id="music" value="Music">
    <label for="music"> 音楽</label>
    <input type="checkbox" name="hobby[]" id="book" value="Book">
    <label for="book"> 読書 </label>
  </div>
  <button name="send" type="submit">送信</button>
</form>
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
//第4パラメータに FILTER_REQUIRE_ARRAY を指定
$hobby = filter_input( INPUT_POST, 'hobby', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
?>
<div class="container">
<form name="myForm" method="post">
  <div>
    <input type="checkbox" name="hobby[]" id="sports" value="Sports">
    <label for="sports"> スポーツ</label>
    <input type="checkbox" name="hobby[]" id="music" value="Music">
    <label for="music"> 音楽</label>
    <input type="checkbox" name="hobby[]" id="book" value="Book">
    <label for="book"> 読書 </label>
  </div>
  <button name="send" type="submit">送信</button>
</form>
  <div class="result">var_dump( $hobby );
    <?php
    echo '<pre>';
    ob_start();
    var_dump( $hobby );
    $output = ob_get_contents();
    ob_end_clean();
    echo h( $output ); //エスケープ処理して出力
    echo '</pre>';
    ?>
  </div>
  <div class="result">"key: " .$key. " val: ".$val;
    <?php
    echo '<pre>';
    //送信された $hobby に要素があれば、各要素のキーと値を出力
    if (!empty($hobby)) {
      foreach($hobby as $key => $val){
        echo "key: ".h($key)."  val: ".h($val)."<br>";
      }
    }
    echo '</pre>';
    ?>
  </div>
</div>

選択状態を保持

以下は、送信後もチェックボックスの選択状態を保持する例です(フォームが送信されてサーバ側の検証を通過しない場合に再度フォームを表示する場合などを想定しています)。

送信された配列 $hobby にそれぞれのチェックボックの value の値が含まれているかを調べて、value の値が含まれて入れば HTML に checked 属性を出力するようにしています。

チェックボックの値が含まれているかは array_search() を使っています。

<?php
//第4パラメータに FILTER_REQUIRE_ARRAY を指定
$hobby = filter_input( INPUT_POST, 'hobby', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

//$hobby にそれぞれのチェックボックの value の値が含まれているかを調べて checked 属性を出力
$sports_checked = ($hobby && array_search('Sports', $hobby) !== false) ? 'checked' : '';
$music_checked = ($hobby && array_search('Music', $hobby) !== false) ? 'checked' : '';
$book_checked = ($hobby && array_search('Book', $hobby) !== false) ? 'checked' : '';

var_dump( $hobby );
if (!empty($hobby)) {
  foreach($hobby as $key => $val){
    echo "key: " .$key ."  val: " .$val. "<br>";
  }
}
?>

<form name="myForm" method="post">
  <div>
    <input type="checkbox" name="hobby[]" id="sports" value="Sports" <?php echo $sports_checked; ?>>
    <label for="sports"> スポーツ</label>
    <input type="checkbox" name="hobby[]" id="music" value="Music" <?php echo $music_checked; ?>>
    <label for="music"> 音楽</label>
    <input type="checkbox" name="hobby[]" id="book" value="Book" <?php echo $book_checked; ?>>
    <label for="book"> 読書 </label>
  </div>
  <button name="send" type="submit">送信</button>
</form>

以下は上記と同じことを PHP でチェックボックスの HTML を生成して出力する例です。

各チェックボックスの id と value 属性の値、及び label 要素のラベルは多次元配列の連想配列を使っています。多次元配列のループ処理では、list() を使ってネストした配列を展開しています(PHP 5.5 以上)。

<?php
//第4パラメータに FILTER_REQUIRE_ARRAY を指定
$hobby = filter_input( INPUT_POST, 'hobby', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
?>
<form name="myForm" method="post">
  <div>
    <?php
    //各チェックボックスの id と value 属性の値、及び label 要素のラベルの連想配列
    $checkbox = array(
      //array('id の値', 'value の値', ' ラベル'),
      array('sports', 'Sports', ' スポーツ'),
      array('music', 'Music', ' 音楽'),
      array('book', 'Book', ' 読書')
    );
    foreach ($checkbox as list($id, $value, $label)) {
      //$checked を空文字で初期化
      $checked = '';
      if($hobby) {
        $checked = array_search($value, $hobby) !== false ? 'checked' : '';
      }
      echo '<input type="checkbox" name="hobby[]" id="' .$id .'" value="' .$value . '" '.  $checked .  ' >' ."\n" ;
      echo '<label for="' . $id . '">' . $label . '</label>' ."\n";
    }
    ?>
  </div>
  <button name="send" type="submit">送信</button>
</form>
<?php
//エスケープ処理を行う関数
function h( $var ) {
  if ( is_array( $var ) ) {
    return array_map( 'h', $var );
  } else {
    if($var === null) return '';
    return htmlspecialchars( $var, ENT_QUOTES, 'UTF-8' );
  }
}
//第4パラメータに FILTER_REQUIRE_ARRAY を指定
$hobby = filter_input( INPUT_POST, 'hobby', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
?>

<form name="myForm" method="post">
  <div>
    <?php
    $checkbox = array(
      array('sports', 'Sports', ' スポーツ'),
      array('music', 'Music', ' 音楽'),
      array('book', 'Book', ' 読書')
    );
    foreach ($checkbox as list($id, $value, $label)) {
      $checked = '';
      if($hobby) {
        $checked = array_search($value, $hobby) !== false ? 'checked' : '';
      }
      echo '<input type="checkbox" name="hobby[]" id="' .$id .'" value="' .$value . '" '.  $checked .  ' >' ."\n" ;
      echo '<label for="' . $id . '">' . $label . '</label>' ."\n";
    }
    ?>
  </div>
  <button name="send" type="submit">送信</button>
</form>
<div class="result">var_dump( $hobby );
  <?php
  echo '<pre>';
  ob_start();
  var_dump( $hobby );
  $output = ob_get_contents();
  ob_end_clean();
  echo h( $output ); //エスケープ処理して出力
  echo '</pre>';
  ?>
</div>
<div class="result">"key: " .$key. " val: ".$val;
  <?php
  echo '<pre>';
  //送信された $hobby に要素があれば、各要素のキーと値を出力
  if (!empty($hobby)) {
    foreach($hobby as $key => $val){
      echo "key: ".h($key)."  val: ".h($val)."<br>";
    }
  }
  echo '</pre>';
  ?>
</div>

除去フィルタ(サニタイズ)

filter_input() の第3パラメータに FILTER_SANITIZE_ で始まる除去フィルタを指定するとサニタイズ処理をしてくれます。

除去フィルタは値を検証して問題がなければその値を返し、値に不適切な文字が含まれている場合は、不適切な文字を削除または変換(エンコードやエスケープ)した値を返します。

エスケープ処理

FILTER_SANITIZE_SPECIAL_CHARS や FILTER_SANITIZE_FULL_SPECIAL_CHARS を使うと値をエスケープ処理することができます。

除去フィルタ 説明
FILTER_SANITIZE_SPECIAL_CHARS ' " < > & および ASCII 値が 32 未満の文字を エスケープします。オプションでその他の特殊文字を取り除いたりエンコードしたりできます。
FILTER_SANITIZE_FULL_SPECIAL_CHARS 特殊文字を HTML エンティティに変換します。htmlspecialchars() に ENT_QUOTES を指定するのと同じ。

以下は FILTER_SANITIZE_FULL_SPECIAL_CHARS を使ってエスケープ処理する例です。

<?php
//第3パラメータに除去フィルタ(FILTER_SANITIZE_FULL_SPECIAL_CHARS)を指定
$subject = filter_input( INPUT_POST, 'subject', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
?>

<form name="myForm" method="post">
  <div>
    <label for="subject">件名</label>
    <input type="text" id="subject" name="subject" value="<?php echo $subject; ?>" size="50">
  </div>
  <button name="send" type="submit">送信</button>
</form>
<div class="result">var_dump( $subject );
  <?php
  echo '<pre>';
  var_dump( $subject );
  echo '</pre>';
  ?>
</div>

上記の場合、例えば <p>"フィルタ"</p> と入力して送信すると、入力された値は &lt;p&gt;&quot;フィルタ&quot;&lt;/p&gt; にエスケープされます。

但し、以下のサンプルの場合、<script> タグなどを記述すると、サーバの Web アプリケーションファイヤウォール(WAF)でブロックされ「閲覧できません (Forbidden access)」と表示されます。

エスケープされた値はページのソースで確認できます(上記サンプルを別ページで開く)。

除去処理

FILTER_SANITIZE_NUMBER_INT や FILTER_SANITIZE_STRING などを使って不正な文字を除去することができます。

除去フィルタ 説明
FILTER_SANITIZE_NUMBER_INT 数字とプラス、マイナス記号以外のすべての文字を取り除きます。
FILTER_SANITIZE_STRING
PHP 8.1.x から非推奨
タグとシングルクォート、ダブルクォートを取り除きます。 オプションで特殊文字を取り除いたりエンコードできます。

以下は FILTER_SANITIZE_NUMBER_INT と FILTER_SANITIZE_STRING を使って不正な文字を除去する例です。PHP 8.1.x から FILTER_SANITIZE_STRING と FILTER_SANITIZE_STRIPPED は非推奨となっています。

<?php
//第3パラメータに除去フィルタを指定
$number = filter_input( INPUT_POST, 'number', FILTER_SANITIZE_NUMBER_INT);
//$subject = filter_input( INPUT_POST, 'subject', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK);
$subject = filter_input( INPUT_POST, 'subject', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
?>

<form name="myForm" method="post">
  <div>
    <label for="number">数値</label>
    <input type="text" id="number" name="number" value="<?php echo $number; ?>" size="20">
  </div>
  <div>
    <label for="subject">件名</label>
    <input type="text" id="subject" name="subject" value="<?php echo $subject; ?>" size="50">
  </div>
  <button name="send" type="submit">送信</button>
</form>
<div class="result">var_dump( $number );
  <?php
  echo '<pre>';
  var_dump( $number );
  echo '</pre>';
  ?>
</div>
<div class="result">var_dump( $subject );
  <?php
  echo '<pre>';
  var_dump( $subject );
  echo '</pre>';
  ?>
</div>

FILTER_SANITIZE_STRING を使ってバッククォート(` )も除去するにはフラグに FILTER_FLAG_STRIP_BACKTICK を指定します。

$subject = filter_input( INPUT_POST, 'subject', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK);

filter_var

filter_input() は $_GET や $_POST などの入力値(外部からの変数)のみを受け取りますが、filter_var() は任意のデータを受け取ってフィルタリングすることができます。

指定するフィルタやフラグは filter_input() と共通で、パラメータの位置が異なるだけで使い方もほとんど同じです。

書式とパラメータ

以下が filter_var() の書式とパラメータです。

filter_var( $value, $filter, $options )
パラメータ 説明
$value

フィルタリングする値(データ)。

$filter

適用するフィルタの種類を ID(定数)で指定します。省略した場合は FILTER_DEFAULT(FILTER_UNSAFE_RAW と同等)が適用され、フィルタリングをしません。

指定できるフィルタは以下に記載されています。

$options

フィルタに指定するオプションやフラグを指定します(フィルタごとに指定できるオプションやフラグは異なります)。

オプションを指定する場合やオプションとフラグの両方を指定する場合は、連想配列で指定します。

複数のフラグを指定する場合は「|」で区切って指定します。

※ 変数の値が配列の場合は、フラグに FILTER_REQUIRE_ARRAY を指定します。

返り値

成功した場合はフィルタリングされた値(データ)、フィルタリングに失敗した場合は false を返します。

メールアドレスの検証

メールアドレスの検証にはフィルタ(第2パラメータ)に FILTER_VALIDATE_EMAIL を指定します。

正しい形式のメールアドレスの場合は、文字列がそのまま返ります。正しくない場合は、false が返ります(第3パラメータに FILTER_NULL_ON_FAILURE を指定した場合は NULL が返ります)。

// 正しいメールアドレスの場合
$email = filter_var( 'foo@example.com', FILTER_VALIDATE_EMAIL);
var_dump( $email ); //string(15) "foo@example.com"
//正しいメールアドレスが返る

// 正しくないメールアドレスの場合(ドメイン部分がない)
$email = filter_var( 'foo@example', FILTER_VALIDATE_EMAIL);
var_dump( $email ); //bool(false)
//真偽値の false が返る

//正しくないメールアドレスの場合(第3パラメータに FILTER_NULL_ON_FAILURE を指定)
$email = filter_var( 'foo@example', FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE);
var_dump( $email ); //NULL
//NULL が返る

真偽値の検証

値が真偽値かどうかを検証するには FILTER_VALIDATE_BOOLEAN を使います(PHP8.0.0 以降では FILTER_VALIDATE_BOOL が使えます)。

このフィルタは値が true、"true"、1、"1"、"on" および "yes" の場合に true、 それ以外の場合は false を返します。

$value = filter_var( 'yes', FILTER_VALIDATE_BOOLEAN);
var_dump( $value );  //bool(true)
//真偽値 true が返る

$value = filter_var( 'foo', FILTER_VALIDATE_BOOLEAN);
var_dump( $value );  //bool(false)
//真偽値 false が返る

フラグ FILTER_NULL_ON_FAILURE を指定すると false が返されるのは 0、 "0"、false、 "false"、"off"、"no" および "" の場合のみとなり、boolean 以外の値については null を返します。

$value = filter_var( 'foo', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
var_dump( $value );  //NULL
//NULL が返る

整数の検証

値が整数かどうかを検証するには FILTER_VALIDATE_INT を使います。成功した場合はその整数(文字列の場合は整数に変換)を返します。

$int = filter_var( -9, FILTER_VALIDATE_INT);
var_dump( $int );  //int(-9)

$int = filter_var( '123', FILTER_VALIDATE_INT);
var_dump( $int );  //int(123)
//整数に変換される

$int = filter_var( '30px', FILTER_VALIDATE_INT);
var_dump( $int );  //bool(false)

オプションで指定した範囲内にあるかどうかを検証することができます。以下は options に min_range と max_range を指定して、値が0〜9の間にある整数かどうかを検証する例です。

$int = filter_var( '7', FILTER_VALIDATE_INT, array('options' => array('min_range' => 0, 'max_range' => 9)));
var_dump( $int );  //int(7)

以下はフラグ FILTER_FLAG_ALLOW_HEX を指定して、0x や 0X で始まる入力を十六進数とみなして許容する例です。値は10進数に変換されて返されます。

$int = filter_var( '0xffffff', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
var_dump( $int );  //int(16777215)

コールバック関数を使って検証

第2パラメータに FILTER_CALLBACK を指定して独自のコールバック関数を適用することができます。以下の場合、コールバック関数の定義では、値が条件に合う場合はその値を返し、適合しない場合は false を返すようにしています。

//独自のコールバック関数を定義
function check_input($text){
  //制御文字でないことと3文字以上10文字以下であるかを検証
  if ( preg_match( '/\A[[:^cntrl:]]{3,10}\z/u', $text ) ){
    //条件を満たす場合はその値を返す
    return $text;
  }else{
    //条件を満たさない場合は false を返す
    return false;
  }
}

$input = 'abc 123';
//第2パラメータに FILTER_CALLBACK を指定し、options にコールバック関数を指定
$input = filter_var($input, FILTER_CALLBACK, array('options' => 'check_input'));
var_dump( $input ); //string(7) "abc 123"

エスケープ処理

第2パラメータに FILTER_SANITIZE_SPECIAL_CHARS や FILTER_SANITIZE_FULL_SPECIAL_CHARS を指定して、値をエスケープ処理することができます

以下は、FILTER_SANITIZE_FULL_SPECIAL_CHARS を指定して値をエスケープ(特殊文字を HTML エンティティに変換)する例です。

$input = '<script>alert("Hello")</script>';

//第2パラメータに FILTER_SANITIZE_FULL_SPECIAL_CHARS を指定
$input = filter_var($input, FILTER_SANITIZE_FULL_SPECIAL_CHARS);

var_dump( $input );
//string(53) "&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;"

除去処理

第2パラメータに FILTER_SANITIZE_NUMBER_INT や FILTER_SANITIZE_STRING などを指定して不正な文字を除去することができます。

以下は FILTER_SANITIZE_STRING を指定してタグやクォートを値から取り除く例です。

$input = '<script>alert("Hello")</script>';

//第2パラメータに FILTER_SANITIZE_STRING を指定
$input = filter_var($input, FILTER_SANITIZE_STRING);

var_dump( $input ); //string(22) "alert(&#34;Hello&#34;)"

NULL 合体演算子

PHP7以降では NULL 合体演算子(??) を使って、三項演算子と isset() を組み合わせるパターンをより簡単に書くことができます。

$foo = $_GET['foo'] ?? null;

//上記のコードは以下と同じ意味
$foo = isset($_GET['foo']) ? $_GET['foo'] : null;

この演算子は第一オペランドが存在し、null 以外の値であればその値を返し、null や未定義の場合は第二オペランドを返します

この演算子は、第一オペランドの値が存在しない(未定義の)場合でも isset() と同様、notice や warning エラーが発生しません。

isset() は変数が宣言されていることと、null(NULL)ではないことを検査する言語構造です。

但し、$_GET や $_POST などの外部からの変数を初期化する場合は filter_input() を使ったほうが安全性が高いかと思います。

以下はセッション変数($_SESSION)がすで存在すればその値を、そうでなければ NULL で初期化する例です。

$name = $_SESSION[ 'name' ] ?? null;

//以下と同じこと
$name = isset( $_SESSION[ 'name' ] ) ? $_SESSION[ 'name' ] : null;

合体演算子を連結

合体演算子を連結することができます。

以下は $_GET['name']、$_POST['name']、'foo' の順に調べて、定義されている最初の値を返します。$_GET['name'] や $_POST['name']が未定義や NULL の場合は $nameは 'foo' になります。

$name = $_GET['name'] ?? $_POST['name'] ??  'foo';

以下の場合、$foo は定義されておらず、$bar は null なので、定義されている最初の値 $baz の「1」が出力されます。

$bar = null;
$baz = 1;
$qux = 2;

echo $foo ?? $bar ?? $baz ?? $qux; // 1