PHPの数値チェックができる関数について

採用はこちら

PHPで数値チェックを行う場合、よく使われる関数には次のようなものがあります。

  • is_numeric()
  • ctype_digit()
  • filter_var()
  • is_int()
  • is_float()
  • preg_match()

ただし、これらの関数はすべて同じ目的で使うものではありません。

「数値として扱えるか」を確認したいのか、「半角数字だけで構成されているか」を確認したいのか、「整数だけを許可したいのか」「小数も許可したいのか」によって、適した関数は変わります。

PHPで数値チェックを行うときは、まず 何を数値として許可したいのか を明確にすることが大切です。

目次

is_numeric()で数値かどうかをチェックする

is_numeric()とは

is_numeric() は、指定した値が 数値または数値として扱える文字列 かどうかを判定する関数です。

is_numeric(mixed $value): bool

数値そのものだけでなく、文字列の "123""12.34" のように、PHPが数値として解釈できる文字列も true になります。

var_dump(is_numeric(123));       // true
var_dump(is_numeric("123"));     // true
var_dump(is_numeric("12.34"));   // true
var_dump(is_numeric("-123"));    // true
var_dump(is_numeric("+123"));    // true
var_dump(is_numeric("abc"));     // false
var_dump(is_numeric("123abc"));  // false
var_dump(is_numeric(""));        // false

is_numeric()は指数表記もtrueになる

is_numeric() を使うときに注意したいのは、判定範囲がやや広いことです。

例えば、次のような指数表記も true になります。

var_dump(is_numeric("1e3")); // true

"1e3" は数値としては 1000 を意味します。

そのため、単純に「数値として扱えるか」を確認したい場合には便利ですが、ユーザーIDや商品IDのように 数字だけを許可したい項目 には不向きです。

PHP 8以降では空白付きの数値文字列にも注意

PHP 8以降では、前後に空白がある数値文字列も true になる場合があります。

var_dump(is_numeric(" 123")); // true
var_dump(is_numeric("123 ")); // true

そのため、is_numeric() は「数字だけかどうか」を調べる関数ではなく、PHPが数値として解釈できるかどうかを調べる関数 と考えるのが正確です。

is_numeric()が向いているケース

is_numeric() は、次のようなケースに向いています。

$value = "12.5";

if (is_numeric($value)) {
    echo "数値として扱えます";
} else {
    echo "数値ではありません";
}

例えば、計算処理の前に「この値は数値として扱えるか」をざっくり確認したい場合に使えます。

一方で、ID、郵便番号、電話番号、会員番号などのチェックには向いていません。

ctype_digit()で数字だけの文字列かをチェックする

ctype_digit()とは

ctype_digit() は、文字列に含まれるすべての文字が 10進数字 かどうかを判定する関数です。

ctype_digit(string $text): bool

つまり、0 から 9 までの数字だけで構成された文字列なら true になります。

var_dump(ctype_digit("123"));    // true
var_dump(ctype_digit("00123"));  // true
var_dump(ctype_digit("12.3"));   // false
var_dump(ctype_digit("-123"));   // false
var_dump(ctype_digit("+123"));   // false
var_dump(ctype_digit("1e3"));    // false
var_dump(ctype_digit("abc"));    // false
var_dump(ctype_digit(""));       // false

ctype_digit()はIDや番号のチェックに向いている

ctype_digit() は、「数値として計算できるか」ではなく、「数字だけで構成されているか」を確認したい場合に向いています。

例えば、次のような項目です。

項目理由
ユーザーID小数や指数表記を許可しないため
商品ID数字だけで構成されることが多いため
郵便番号先頭ゼロを保持できるため
会員番号数値計算ではなく文字列として扱うため
認証コード桁数と数字のみを確認したいため

例えば、GETパラメータで渡されたIDを数字だけに限定したい場合は、次のように書けます。

$id = $_GET['id'] ?? '';

if (ctype_digit($id)) {
    echo "数字だけです";
} else {
    echo "不正なIDです";
}

ctype_digit()は文字列として渡すのが安全

ctype_digit() は文字列を対象にした関数です。

PHP 8.1以降では、文字列以外の値を渡すことは非推奨になっています。

そのため、汎用的なチェック関数を作る場合は、文字列かどうかも確認しておくと安全です。

function isDigitsOnly($value): bool
{
    return is_string($value) && ctype_digit($value);
}

数値型も受け取りたい場合は、文字列に変換してから判定する方法もあります。

$value = 123;

var_dump(ctype_digit((string)$value)); // true

ただし、安易に文字列化すると、意図しない値まで通してしまう可能性があります。

例えば、true を文字列化すると "1" になるため、チェックの目的によっては注意が必要です。

filter_var()で整数や小数をチェックする

filter_var()とは

filter_var() は、値に対して検証フィルタやサニタイズフィルタを適用する関数です。

数値チェックでは、主に次のフィルタを使います。

フィルタ用途
FILTER_VALIDATE_INT整数として妥当かをチェックする
FILTER_VALIDATE_FLOATfloatとして妥当かをチェックする

フォーム入力、GETパラメータ、POSTパラメータなどの検証では、filter_var() がよく使われます。

FILTER_VALIDATE_INTで整数チェックをする

基本的な使い方

整数かどうかをチェックしたい場合は、FILTER_VALIDATE_INT を使います。

$value = "123";

$result = filter_var($value, FILTER_VALIDATE_INT);

if ($result !== false) {
    echo "整数です";
} else {
    echo "整数ではありません";
}

チェックに成功すると、戻り値は int 型になります。

var_dump(filter_var("123", FILTER_VALIDATE_INT));    // int(123)
var_dump(filter_var("-123", FILTER_VALIDATE_INT));   // int(-123)
var_dump(filter_var("12.3", FILTER_VALIDATE_INT));   // false
var_dump(filter_var("abc", FILTER_VALIDATE_INT));    // false

0の判定には注意する

filter_var() を使うときは、戻り値の判定方法に注意が必要です。

次のように書くと、"0" が正しい整数であっても false と同じように扱われてしまいます。

$value = "0";

$result = filter_var($value, FILTER_VALIDATE_INT);

if ($result) {
    echo "整数です";
} else {
    echo "整数ではありません";
}

PHPでは 0 が falsy な値として扱われるためです。

正しくは、!== false で判定します。

$value = "0";

$result = filter_var($value, FILTER_VALIDATE_INT);

if ($result !== false) {
    echo "整数です";
} else {
    echo "整数ではありません";
}

範囲を指定して整数チェックをする

FILTER_VALIDATE_INT では、最小値や最大値を指定することもできます。

$age = "25";

$result = filter_var($age, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 0,
        'max_range' => 120,
    ],
]);

if ($result !== false) {
    echo "有効な年齢です";
} else {
    echo "年齢が不正です";
}

年齢、数量、ページ番号、IDなどは、整数かどうかだけでなく、範囲まで確認するのが実務的です。

例えば、ページ番号なら1以上の整数だけを許可することが多いです。

$page = $_GET['page'] ?? '1';

$page = filter_var($page, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
    ],
]);

if ($page === false) {
    $page = 1;
}

FILTER_VALIDATE_INTは前後の空白をtrimする

FILTER_VALIDATE_INT は、文字列を検証する前に trim() します。

そのため、次のような値も整数として通ります。

var_dump(filter_var(" 123 ", FILTER_VALIDATE_INT));
// int(123)

これは便利な場合もありますが、前後の空白も不正として扱いたい場合には注意が必要です。

空白付きの入力を拒否したい場合は、先に元の値と trim() 後の値を比較します。

$rawId = $_GET['id'] ?? '';

if ($rawId !== trim($rawId)) {
    exit('前後に空白があります');
}

$id = filter_var($rawId, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
    ],
]);

if ($id === false) {
    exit('不正なIDです');
}

先頭ゼロを残したい値には不向き

FILTER_VALIDATE_INT は、あくまで「整数として妥当か」を確認するためのものです。

そのため、郵便番号や会員番号のように、先頭ゼロに意味がある値には向いていません。

例えば、次のような値は数値として扱うべきではありません。

$code = "00123";

このような値は、整数ではなく「数字で構成された文字列」として扱うべきです。

$code = "00123";

if (ctype_digit($code)) {
    echo "数字だけです";
}

桁数まで確認するなら、次のように書きます。

$zip = "0012345";

if (ctype_digit($zip) && strlen($zip) === 7) {
    echo "7桁の数字です";
}

FILTER_VALIDATE_FLOATで小数を含む数値をチェックする

基本的な使い方

小数を含む数値を許可したい場合は、FILTER_VALIDATE_FLOAT を使います。

$price = "123.45";

$result = filter_var($price, FILTER_VALIDATE_FLOAT);

if ($result !== false) {
    echo "数値です";
} else {
    echo "数値ではありません";
}
var_dump(filter_var("123", FILTER_VALIDATE_FLOAT));      // float(123)
var_dump(filter_var("123.45", FILTER_VALIDATE_FLOAT));   // float(123.45)
var_dump(filter_var("-123.45", FILTER_VALIDATE_FLOAT));  // float(-123.45)
var_dump(filter_var("abc", FILTER_VALIDATE_FLOAT));      // false

FILTER_VALIDATE_FLOATは小数だけを判定する関数ではない

FILTER_VALIDATE_FLOAT は、小数だけをチェックする関数ではありません。

整数表記の "123" も、floatとして妥当な値なので通ります。

var_dump(filter_var("123", FILTER_VALIDATE_FLOAT));
// float(123)

そのため、正確には「小数チェック」ではなく、floatとして妥当な数値かどうかのチェック と考えるのがよいです。

もし「小数点を必ず含む値だけ」を許可したい場合は、正規表現を使う方が明確です。

$value = "123.45";

if (preg_match('/^-?[0-9]+\.[0-9]+$/', $value)) {
    echo "小数点を含む数値です";
}

FILTER_VALIDATE_FLOATも前後の空白をtrimする

FILTER_VALIDATE_FLOAT も、文字列を検証する前に trim() します。

var_dump(filter_var(" 12.34 ", FILTER_VALIDATE_FLOAT));
// float(12.34)

空白を含む入力を厳密に拒否したい場合は、FILTER_VALIDATE_INT と同様に、先に元の値と trim() 後の値を比較するとよいです。

is_int()でint型かをチェックする

is_int()とは

is_int() は、値の型が int型かどうか を判定する関数です。

var_dump(is_int(123));     // true
var_dump(is_int("123"));   // false
var_dump(is_int(12.3));    // false

is_numeric() とは目的が違います。

$value = "123";

var_dump(is_numeric($value)); // true
var_dump(is_int($value));     // false

"123" は数値として扱える文字列なので is_numeric() では true になります。

しかし、型は文字列なので is_int() では false になります。

is_int()が向いているケース

is_int() は、フォーム入力のチェックよりも、プログラム内部で型を確認したい場合に向いています。

例えば、関数に渡された値が本当に整数型かどうかを確認したい場合です。

function calculate(int $num): int
{
    return $num * 2;
}

PHPの型宣言を使える場面では、is_int() で確認するより、型宣言を使う方がシンプルな場合もあります。

is_float()でfloat型かをチェックする

is_float()とは

is_float() は、値の型が float型かどうか を判定する関数です。

var_dump(is_float(12.3));    // true
var_dump(is_float("12.3"));  // false
var_dump(is_float(123));     // false

こちらも、文字列の数値をチェックする関数ではありません。

$value = "12.3";

var_dump(is_numeric($value)); // true
var_dump(is_float($value));   // false

"12.3" は数値として扱える文字列ですが、型は文字列です。

そのため、is_float() では false になります。

is_float()が向いているケース

is_float() は、プログラム内部で値の型を確認したい場合に使います。

フォーム入力やGET/POST値は基本的に文字列として渡されるため、入力値チェックでは FILTER_VALIDATE_FLOAT などを使う方が実務的です。

preg_match()で独自ルールの数値チェックをする

正規表現を使うメリット

preg_match() を使うと、独自のルールで数値チェックができます。

例えば、次のような細かい条件を設定できます。

  • 半角数字だけ許可する
  • 正の整数だけ許可する
  • 0以上の整数だけ許可する
  • 小数点を必須にする
  • 桁数を指定する
  • ハイフンありの郵便番号を許可する
  • プラス記号やマイナス記号の有無を制御する

標準関数だけでは条件に合わない場合は、正規表現が便利です。

半角数字のみをチェックする

半角数字だけを許可したい場合は、次のように書けます。

$value = "12345";

if (preg_match('/^[0-9]+$/', $value)) {
    echo "半角数字のみです";
}

\d を使う方法もあります。

if (preg_match('/^\d+$/', $value)) {
    echo "数字のみです";
}

ただし、半角数字だけに限定したい意図を明確にするなら、[0-9] を使う方がわかりやすいです。

正の整数のみをチェックする

1以上の整数だけを許可したい場合は、次のように書きます。

$value = "123";

if (preg_match('/^[1-9][0-9]*$/', $value)) {
    echo "正の整数です";
}

この正規表現では、0 は許可されません。

0以上の整数をチェックする

0以上の整数を許可したい場合は、次のように書けます。

$value = "0";

if (preg_match('/^(0|[1-9][0-9]*)$/', $value)) {
    echo "0以上の整数です";
}

この書き方であれば、0 は許可しつつ、00123 のような先頭ゼロ付きの値は拒否できます。

小数も許可する

整数と小数の両方を許可したい場合は、次のように書けます。

$value = "-123.45";

if (preg_match('/^-?[0-9]+(\.[0-9]+)?$/', $value)) {
    echo "整数または小数です";
}

この正規表現で許可される値の例は次の通りです。

123
-123
123.45
-123.45

一方、次のような値は許可されません。

.5
123.
+123
1e3

小数点を必須にする

「整数は不可で、小数点を含む値だけ許可したい」場合は、次のように書きます。

$value = "123.45";

if (preg_match('/^-?[0-9]+\.[0-9]+$/', $value)) {
    echo "小数点を含む数値です";
}

この場合、"123" は false になります。

桁数を指定する

郵便番号のように、桁数が決まっている場合は正規表現が便利です。

$zip = "1234567";

if (preg_match('/^[0-9]{7}$/', $zip)) {
    echo "7桁の郵便番号です";
}

ハイフンあり・なしの両方を許可したい場合は、次のように書けます。

$zip = "123-4567";

if (preg_match('/^[0-9]{3}-?[0-9]{4}$/', $zip)) {
    echo "郵便番号形式です";
}

数値チェック関数の違い

関数ごとの比較表

PHPの数値チェック関数は、それぞれ判定基準が異なります。

関数"123""12.3""-123""1e3""00123""123 "主な用途
is_numeric()truetruetruetruetruetrue数値として扱えるか
ctype_digit()truefalsefalsefalsetruefalse数字だけの文字列か
FILTER_VALIDATE_INTtruefalsetruefalse注意true整数として妥当か
FILTER_VALIDATE_FLOATtruetruetruetrueになる場合ありtruetruefloatとして妥当か
is_int()falsefalsefalsefalsefalsefalseint型か
is_float()falsefalsefalsefalsefalsefalsefloat型か
preg_match()書き方次第書き方次第書き方次第書き方次第書き方次第書き方次第独自ルール

is_numeric()ctype_digit()の違い

is_numeric()ctype_digit() は混同されやすいですが、目的が異なります。

is_numeric() は、PHPが数値として扱えるかどうかを判定します。

var_dump(is_numeric("1e3")); // true

一方、ctype_digit() は、文字列が数字だけで構成されているかを判定します。

var_dump(ctype_digit("1e3")); // false

つまり、IDや会員番号などに使うなら、ctype_digit() の方が適しています。

filter_var()ctype_digit()の違い

filter_var()FILTER_VALIDATE_INT は、「整数として妥当か」を判定します。

一方、ctype_digit() は「数字だけで構成された文字列か」を判定します。

例えば、先頭ゼロを含む値を扱う場合は、ctype_digit() の方が向いています。

$code = "00123";

if (ctype_digit($code)) {
    echo "数字だけです";
}

郵便番号や会員番号は、数値計算をするものではないため、整数ではなく文字列として扱うのが安全です。

実務での使い分け

IDやページ番号はFILTER_VALIDATE_INT

IDやページ番号のように、1以上の整数だけを許可したい場合は、FILTER_VALIDATE_INT が使いやすいです。

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

$id = filter_var($id, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
    ],
]);

if ($id === false) {
    exit('不正なIDです');
}

// ここから先は、$idを1以上の整数として扱える

この方法であれば、次のような値を弾けます。

abc
12.3
-1
0
1e3

郵便番号や会員番号はctype_digit()

郵便番号、会員番号、商品コード、認証コードなどは、数値ではなく文字列として扱うべきです。

$zip = $_POST['zip'] ?? '';

if (ctype_digit($zip) && strlen($zip) === 7) {
    echo "有効な郵便番号です";
} else {
    echo "郵便番号が不正です";
}

この方法なら、0012345 のように先頭ゼロを含む値も正しく扱えます。

金額や重量はFILTER_VALIDATE_FLOAT

金額、重量、割合、スコアなど、小数を含む可能性がある値には FILTER_VALIDATE_FLOAT が使えます。

$weight = $_POST['weight'] ?? null;

$weight = filter_var($weight, FILTER_VALIDATE_FLOAT);

if ($weight !== false && $weight > 0) {
    echo "有効な重量です";
} else {
    echo "重量が不正です";
}

ただし、金額を厳密に扱う場合は、floatの丸め誤差に注意が必要です。

会計処理や決済処理では、円単位の整数として扱う、またはDecimal相当の設計を検討する方が安全です。

型チェックにはis_int()is_float()

プログラム内部で変数の型を確認したい場合は、is_int()is_float() を使います。

$value = 123;

if (is_int($value)) {
    echo "int型です";
}

ただし、フォーム入力やGET/POST値は基本的に文字列として渡されます。

そのため、入力値チェックには is_int()is_float() よりも、filter_var()ctype_digit() を使う方が適しています。

よくあるNG例

IDチェックにis_numeric()だけを使う

IDチェックで次のように書くのは避けた方がよいです。

$id = $_GET['id'] ?? '';

if (is_numeric($id)) {
    echo "OK";
}

この書き方だと、"1e3" のような指数表記も通ってしまいます。

IDとして1以上の整数だけを許可したい場合は、次のように書く方が安全です。

$id = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
    ],
]);

if ($id === false) {
    exit('不正なIDです');
}

filter_var()の戻り値をif ($value)で判定する

filter_var() の戻り値をそのまま if で判定すると、0 の扱いで問題が起きます。

$value = filter_var("0", FILTER_VALIDATE_INT);

if ($value) {
    echo "OK";
} else {
    echo "NG";
}

"0" は正しい整数ですが、PHPでは 0 が false と同じように扱われます。

正しくは、!== false を使います。

$value = filter_var("0", FILTER_VALIDATE_INT);

if ($value !== false) {
    echo "OK";
} else {
    echo "NG";
}

電話番号をis_numeric()でチェックする

電話番号を is_numeric() でチェックするのも避けた方がよいです。

$tel = "09012345678";

if (is_numeric($tel)) {
    echo "OK";
}

電話番号は数値ではなく、数字を含む文字列です。

先頭ゼロ、ハイフン、国番号などを考慮する必要があります。

$tel = "090-1234-5678";

if (preg_match('/^0\d{1,4}-\d{1,4}-\d{3,4}$/', $tel)) {
    echo "電話番号形式です";
}

PHPの数値チェック関数を選ぶポイント

数値として扱えるか確認したい場合

数値または数値文字列かをざっくり判定したい場合は、is_numeric() が使えます。

if (is_numeric($value)) {
    echo "数値として扱えます";
}

ただし、指数表記や空白付きの数値文字列も通る場合があるため、厳密な入力チェックには注意が必要です。

整数として妥当か確認したい場合

整数として妥当かを確認したい場合は、FILTER_VALIDATE_INT が便利です。

$result = filter_var($value, FILTER_VALIDATE_INT);

if ($result !== false) {
    echo "整数です";
}

範囲指定もできるため、ID、年齢、数量、ページ番号などのチェックに向いています。

数字だけの文字列か確認したい場合

数字だけで構成されているかを確認したい場合は、ctype_digit() が向いています。

if (is_string($value) && ctype_digit($value)) {
    echo "数字だけです";
}

郵便番号、会員番号、商品コード、認証コードなど、先頭ゼロや桁数が重要な値に適しています。

小数を含む数値を確認したい場合

小数を含む数値を許可したい場合は、FILTER_VALIDATE_FLOAT を使います。

$result = filter_var($value, FILTER_VALIDATE_FLOAT);

if ($result !== false) {
    echo "floatとして妥当です";
}

ただし、これは小数点を必須にするチェックではありません。

小数点を必須にしたい場合は、正規表現を使います。

独自ルールでチェックしたい場合

独自の形式を厳密にチェックしたい場合は、preg_match() が便利です。

if (preg_match('/^[0-9]{7}$/', $value)) {
    echo "7桁の数字です";
}

桁数、符号、小数点、ハイフンの有無などを細かく制御できます。

まとめ

PHPで数値チェックを行う場合は、目的に合わせて関数を使い分けることが重要です。

単に数値として扱えるかを確認したい場合は is_numeric() が使えます。

ただし、"1e3" のような指数表記や、PHP 8以降では空白付きの数値文字列も true になる場合があるため、IDや番号のチェックには不向きです。

整数として妥当かを確認したい場合は、filter_var()FILTER_VALIDATE_INT を使うとよいでしょう。

min_rangemax_range を指定できるため、年齢、数量、ページ番号、IDなどのチェックに適しています。

小数を含む数値を許可したい場合は、FILTER_VALIDATE_FLOAT が使えます。

ただし、"123" のような整数表記も通るため、「小数点を必須にしたい」場合は正規表現を使う必要があります。

郵便番号、電話番号、会員番号、商品コードのように、先頭ゼロや桁数が重要な値は、数値ではなく文字列として扱います。

この場合は、ctype_digit()preg_match() を使って、数字だけで構成されているか、指定した形式に合っているかをチェックするのが安全です。

最後に、型そのものを確認したい場合は is_int()is_float() を使います。

ただし、フォーム入力やGET/POST値は基本的に文字列として渡されるため、入力値チェックでは filter_var()ctype_digit()preg_match() を使う方が実務的です。

PHPの数値チェックでは、単に「数値かどうか」だけで判断するのではなく、その値を数値として計算したいのか、それとも数字の並びとして扱いたいのか を意識して関数を選ぶことが大切です。

以上、PHPの数値チェックができる関数についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次