PHPの文字列の中の整数の判定について

採用はこちら

PHPで「整数かどうか」を判定するときは、まず 値の型を見たいのか、それとも 文字列の中身を見たいのか を分けて考える必要があります。

たとえば、次の2つは意味が違います。

123

これは整数型の値です。

一方で、次の値は見た目は数字ですが、PHP上では文字列です。

"123"

フォーム入力、URLパラメータ、POSTデータ、GETデータなどから受け取る値は、多くの場合 "123" のような文字列です。

そのため、Web開発でよく問題になるのは、文字列の中身が整数として妥当かどうか の判定です。

目次

整数判定でよく使う関数の違い

PHPには整数判定に使えそうな関数がいくつかあります。

代表的なものは次のとおりです。

関数・方法主な用途
is_int()値の型が整数か判定する
ctype_digit()文字列が半角数字だけで構成されているか判定する
is_numeric()数値として扱える文字列か判定する
filter_var()整数として妥当か検証する
preg_match()正規表現で独自ルールに沿って判定する
(int) / intval()値を整数に変換する

それぞれ役割が違うため、使いどころを間違えると、意図しない値を通してしまう可能性があります。

is_int() は文字列の整数判定には使えない

is_int() は型を判定する関数

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

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

123 は整数型なので true になります。

しかし、"123" は文字列なので false になります。

つまり、is_int()文字列の中身が整数っぽいかどうか を見る関数ではありません。

フォームやURLパラメータでは注意が必要

たとえば、URLに次のようなパラメータがあったとします。

?id=123

PHPでは次のように受け取ることが多いです。

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

このとき $id の中身は、多くの場合 "123" という文字列です。

そのため、次のように書いても期待どおりには動きません。

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

if (is_int($id)) {
    echo "整数です";
}

$id は文字列なので、is_int($id) は基本的に false になります。

したがって、フォームやURLパラメータの整数判定では、is_int() ではなく、文字列の内容を検証する方法を使う必要があります。

ctype_digit() は半角数字だけの判定に便利

ctype_digit() の基本

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

var_dump(ctype_digit("123"));   // true
var_dump(ctype_digit("00123")); // true
var_dump(ctype_digit("0"));     // true

すべての文字が数字であれば true になります。

一方で、数字以外の文字が含まれている場合は false です。

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

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

ctype_digit() は、次のような値を許可したい場合に向いています。

"0"
"1"
"123"
"00123"

たとえば、URLパラメータのIDが半角数字だけかどうかを確認したい場合は、次のように書けます。

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

if (!ctype_digit($id)) {
    http_response_code(400);
    exit('Invalid ID');
}

$id = (int) $id;

このコードでは、"123" は通りますが、"abc""12.3""-1" などは拒否されます。

ctype_digit() はマイナスを許可しない

ctype_digit() は数字だけを見る関数なので、マイナス記号は許可されません。

ctype_digit("-123"); // false

したがって、負の整数も許可したい場合には、ctype_digit() ではなく、filter_var() や正規表現を使う方が適しています。

ctype_digit() は先頭ゼロを許可する

ctype_digit() は、文字列が数字だけであれば true を返します。

そのため、次のような文字列も通ります。

ctype_digit("00123"); // true

"00123" を許可してよい場合は問題ありません。

しかし、IDやページ番号などで 先頭ゼロを禁止したい 場合は、ctype_digit() だけでは不十分です。

その場合は、正規表現で判定するのがわかりやすいです。

$value = "00123";

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

この正規表現では、"0" は許可し、"123" も許可しますが、"00123" は拒否します。

is_numeric() は整数判定には広すぎる

is_numeric() は数値全般を判定する

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

var_dump(is_numeric("123"));   // true
var_dump(is_numeric("-123"));  // true
var_dump(is_numeric("+123"));  // true
var_dump(is_numeric("12.3"));  // true
var_dump(is_numeric("1e3"));   // true

ここで注意したいのは、is_numeric() は整数だけではなく、小数や指数表記も true にする という点です。

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

"1e3" は数値としては 1000 と解釈できます。

しかし、文字列の見た目としては、一般的な整数文字列とは言いにくいです。

整数だけを許可したい場合には不向き

次のようなコードは、一見すると整数判定に見えます。

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

if (is_numeric($value)) {
    $id = (int) $value;
}

しかし、このコードでは "12.9""1e3" も通ってしまいます。

$value = "12.9";

if (is_numeric($value)) {
    var_dump((int) $value); // int(12)
}

"12.9"12 に変換されるため、意図しない値を受け入れてしまう可能性があります。

そのため、整数だけを許可したい場合に is_numeric() を使うのは避けた方が安全です。

PHP 8.0以降では前後の空白にも注意

PHP 8.0以降では、is_numeric() は前後に空白がある数値文字列も true と判定します。

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

厳密に「空白なしの整数文字列」を判定したい場合、is_numeric() はさらに不向きです。

filter_var() は整数として妥当か検証できる

FILTER_VALIDATE_INT の基本

文字列が整数として妥当か確認したい場合、filter_var()FILTER_VALIDATE_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("+123", FILTER_VALIDATE_INT));  // int(123)

整数として妥当な場合は、int 型に変換された値が返ります。

一方で、整数として妥当でない場合は false が返ります。

var_dump(filter_var("12.3", FILTER_VALIDATE_INT)); // false
var_dump(filter_var("1e3", FILTER_VALIDATE_INT));  // false
var_dump(filter_var("abc", FILTER_VALIDATE_INT));  // false

判定は !== false で行う

filter_var() を使うときに特に注意したいのが、戻り値の判定です。

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

var_dump($result); // int(0)

"0" は正しい整数です。

しかし、PHPでは 0 は条件式の中で false 的に扱われます。

そのため、次のような書き方は危険です。

if (filter_var($value, FILTER_VALIDATE_INT)) {
    echo "整数です";
}

この書き方だと、"0" のときに条件が成立しません。

正しくは、次のように !== false で判定します。

$result = filter_var($value, FILTER_VALIDATE_INT);

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

filter_var() は前後の空白を許可する

FILTER_VALIDATE_INT は、検証前に文字列の前後の空白を取り除きます。

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

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

これは ctype_digit() とは違う挙動です。

var_dump(ctype_digit(" 123 ")); // false

前後の空白を許可したくない場合は、filter_var() だけでは仕様に合わない可能性があります。

filter_var() はプラス記号を許可する

FILTER_VALIDATE_INT では、+123 も整数として扱われます。

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

仕様として +123 を許可してよいなら問題ありません。

しかし、「半角数字だけ」「符号なしの整数だけ」を許可したい場合は、filter_var() よりも ctype_digit() や正規表現の方が向いています。

filter_var() は先頭ゼロに注意が必要

FILTER_VALIDATE_INT では、通常 "00123" のような先頭ゼロ付きの整数文字列は false になります。

var_dump(filter_var("00123", FILTER_VALIDATE_INT)); // false

一方で、ctype_digit() では true になります。

var_dump(ctype_digit("00123")); // true

そのため、先頭ゼロを許可したい場合は ctype_digit()、先頭ゼロを禁止したい場合は filter_var() や正規表現が候補になります。

filter_var() で範囲指定する方法

min_rangemax_range を使う

filter_var() の便利な点は、整数かどうかだけでなく、範囲も一緒にチェックできることです。

$value = "25";

$result = filter_var($value, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
        'max_range' => 100,
    ],
]);

if ($result !== false) {
    echo "1〜100の整数です";
} else {
    echo "不正な値です";
}

このコードでは、1 以上 100 以下の整数だけが許可されます。

ページ番号の判定例

ページ番号を受け取る場合は、1以上の整数だけを許可することが多いです。

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

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

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

不正なページ番号が渡された場合は、1ページ目に戻すような処理にできます。

年齢や数量の判定例

年齢や購入数量のように、最小値と最大値を決めたい場合にも filter_var() は便利です。

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

$quantity = filter_var($quantity, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
        'max_range' => 99,
    ],
]);

if ($quantity === false) {
    exit('数量が不正です');
}

このように書けば、整数判定と範囲チェックをまとめて行えます。

正規表現を使うと細かいルールを決められる

正規表現が向いているケース

正規表現は、整数の表記ルールを細かく決めたいときに便利です。

たとえば、次のような条件を指定できます。

マイナスを許可するか
プラスを許可するか
先頭ゼロを許可するか
0を許可するか
空白を許可するか
1以上に限定するか

ctype_digit()filter_var() では微妙に合わない仕様でも、正規表現なら明確に表現できます。

半角数字だけを許可する

if (preg_match('/^\d+$/', $value) === 1) {
    echo "半角数字だけです";
}

この正規表現では、次のような値が通ります。

"0"
"123"
"00123"

ただし、先頭ゼロも許可されます。

0以上の整数で先頭ゼロを禁止する

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

この場合、次のようになります。

"0"      OK
"1"      OK
"123"    OK
"00123"  NG
"01"     NG

1以上の整数だけを許可する

IDやページ番号などでは、0 を許可したくない場合があります。

その場合は次のように書けます。

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

この場合、次のようになります。

"1"      OK
"123"    OK
"0"      NG
"00123"  NG
"-1"     NG
"+1"     NG

負の整数も許可する

負の整数も許可したい場合は、次のように書けます。

if (preg_match('/^-?\d+$/', $value) === 1) {
    echo "整数です";
}

この場合、次のようになります。

"123"   OK
"-123"  OK
"0"     OK
"12.3"  NG
"abc"   NG

ただし、この正規表現では "00123""-00123" も通ります。

負の整数も許可し、先頭ゼロは禁止する

より厳密にするなら、次のように書きます。

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

この場合、次のようになります。

"0"      OK
"10"     OK
"-10"    OK
"001"    NG
"-001"   NG

(int)intval() は判定ではなく変換

キャストは入力チェックではない

(int)intval() は、値を整数に変換するためのものです。

整数として妥当かどうかを確認するためのものではありません。

var_dump((int) "123");     // int(123)
var_dump((int) "123abc");  // int(123)
var_dump((int) "12.9");    // int(12)
var_dump((int) "abc");     // int(0)

このように、明らかに整数文字列ではない値でも、強制的に整数へ変換されます。

変換前に必ず検証する

次のようなコードはよく見かけますが、注意が必要です。

$id = (int) $_GET['id'];

このコードでは、次のような値も変換されてしまいます。

"123abc" → 123
"12.9"   → 12
"abc"    → 0

意図しない値を受け入れないためには、まず検証してから変換します。

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

if (!ctype_digit($id)) {
    http_response_code(400);
    exit('Invalid ID');
}

$id = (int) $id;

Webアプリケーションでは、変換より先に検証する という考え方が重要です。

全角数字を受け付ける場合の注意点

全角数字はそのままだと通らない

日本語のフォームでは、ユーザーが全角数字を入力することがあります。

123

しかし、ctype_digit() では通常、全角数字は false になります。

var_dump(ctype_digit("123")); // false

半角数字のみを許可する仕様なら、このまま拒否して問題ありません。

全角数字を半角に変換してから判定する

全角数字も受け付けたい場合は、先に半角へ変換してから判定します。

$value = $_POST['age'] ?? '';

$value = mb_convert_kana($value, 'n', 'UTF-8');

if (!ctype_digit($value)) {
    exit('年齢が不正です');
}

$age = (int) $value;

mb_convert_kana() を使うと、次のように変換できます。

"123" → "123"

日本語サイトのフォームでは、全角数字を許可するかどうかを仕様として決めておくとよいです。

空文字や未入力の扱い

空文字は整数ではない

ctype_digit() では、空文字は false になります。

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

未入力をエラーにしたい場合は、次のように分けて判定するとわかりやすいです。

$value = $_POST['age'] ?? '';

if ($value === '') {
    exit('入力してください');
}

if (!ctype_digit($value)) {
    exit('整数で入力してください');
}

未入力時にデフォルト値を使う場合

ページ番号のように、未入力ならデフォルト値を使いたいケースもあります。

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

if ($value === '') {
    $page = 1;
} elseif (ctype_digit($value)) {
    $page = (int) $value;
} else {
    exit('ページ番号が不正です');
}

このように、未入力と不正入力を分けて扱うと、処理の意図が明確になります。

判定結果の比較表

代表的な値について、各関数の結果を比較すると次のようになります。

ctype_digit()is_numeric()filter_var($v, FILTER_VALIDATE_INT)
"123"truetrueint(123)
"00123"truetruefalse
"0"truetrueint(0)
"-123"falsetrueint(-123)
"+123"falsetrueint(123)
"12.3"falsetruefalse
"1e3"falsetruefalse
" 123 "falsetrueint(123)
"abc"falsefalsefalse
""falsefalsefalse
"123abc"falsefalsefalse

この表からわかるように、同じ「整数判定」に見えても、関数によって許可する範囲がかなり違います。

実務での使い分け

半角数字だけを許可したい場合

半角数字だけを許可したい場合は、ctype_digit() がシンプルです。

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

許可される値の例は次のとおりです。

"0"
"123"
"00123"

拒否される値の例は次のとおりです。

"-123"
"+123"
"12.3"
"1e3"
" 123 "
"abc"

1以上のIDとして扱いたい場合

IDのように、1以上の整数だけを許可したい場合は、正規表現を使うと明確です。

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

if (!preg_match('/^[1-9][0-9]*$/', $id)) {
    http_response_code(400);
    exit('Invalid ID');
}

$id = (int) $id;

この場合、"1""123" は通りますが、"0""00123""-1""+1" は拒否されます。

0以上の整数として扱いたい場合

0 を許可し、先頭ゼロは禁止したい場合は、次の正規表現が使えます。

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

この判定では、"0" は通りますが、"00""001" は拒否されます。

マイナスも許可したい場合

マイナスも許可したい場合は、filter_var() が使いやすいです。

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

$int = filter_var($value, FILTER_VALIDATE_INT);

if ($int === false) {
    exit('整数ではありません');
}

ただし、filter_var()"+123"" 123 " も許可します。

それらを拒否したい場合は、正規表現を使います。

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

範囲指定もしたい場合

整数かどうかだけでなく、最小値や最大値も決めたい場合は、filter_var() が便利です。

$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 0,
        'max_range' => 150,
    ],
]);

if ($age === false) {
    exit('年齢が不正です');
}

年齢、数量、ページ番号、表示件数などでは、範囲指定を一緒に行うと安全です。

おすすめの自作関数

半角数字だけか判定する関数

function isDigitString(string $value): bool
{
    return ctype_digit($value);
}

使用例です。

var_dump(isDigitString("123"));   // true
var_dump(isDigitString("00123")); // true
var_dump(isDigitString("-123"));  // false

1以上の整数文字列か判定する関数

function isPositiveIntegerString(string $value): bool
{
    return preg_match('/^[1-9][0-9]*$/', $value) === 1;
}

使用例です。

var_dump(isPositiveIntegerString("1"));     // true
var_dump(isPositiveIntegerString("123"));   // true
var_dump(isPositiveIntegerString("0"));     // false
var_dump(isPositiveIntegerString("00123")); // false

0以上の整数文字列か判定する関数

function isNonNegativeIntegerString(string $value): bool
{
    return preg_match('/^(0|[1-9][0-9]*)$/', $value) === 1;
}

使用例です。

var_dump(isNonNegativeIntegerString("0"));     // true
var_dump(isNonNegativeIntegerString("123"));   // true
var_dump(isNonNegativeIntegerString("00123")); // false
var_dump(isNonNegativeIntegerString("-1"));    // false

整数に変換して取得する関数

整数として妥当なら int、不正なら null を返す関数にしておくと便利です。

function toIntOrNull(string $value): ?int
{
    $int = filter_var($value, FILTER_VALIDATE_INT);

    return $int === false ? null : $int;
}

使用例です。

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

$int = toIntOrNull($value);

if ($int === null) {
    exit('整数ではありません');
}

1以上の整数に限定して取得する関数

function toPositiveIntOrNull(string $value): ?int
{
    $int = filter_var($value, FILTER_VALIDATE_INT, [
        'options' => [
            'min_range' => 1,
        ],
    ]);

    return $int === false ? null : $int;
}

使用例です。

$id = toPositiveIntOrNull($_GET['id'] ?? '');

if ($id === null) {
    http_response_code(400);
    exit('Invalid ID');
}

ただし、この関数では "+123"" 123 " も通る点には注意してください。

Laravelなどのフレームワークを使う場合

バリデーション機能を使うのが基本

Laravelなどのフレームワークでは、PHPの関数を直接使うより、バリデーション機能を使うことが多いです。

Laravelであれば、たとえば次のように書けます。

$request->validate([
    'id' => ['required', 'integer', 'min:1'],
]);

このように書けば、id が必須で、整数で、1以上であることを検証できます。

厳密な表記ルールが必要なら正規表現も使う

ただし、フレームワークの integer ルールがどのような表記を許可するかは、仕様によって異なります。

たとえば、次のようなルールを厳密に決めたい場合があります。

先頭ゼロを禁止したい
プラス記号を禁止したい
空白を禁止したい
半角数字だけにしたい

このような場合は、integer だけでなく、正規表現によるルールを追加すると安心です。

$request->validate([
    'id' => ['required', 'regex:/^[1-9][0-9]*$/'],
]);

目的別のおすすめ一覧

半角数字だけを判定したい

ctype_digit($value)

向いている値です。

"123"
"00123"
"0"

1以上の整数IDを判定したい

preg_match('/^[1-9][0-9]*$/', $value)

向いている値です。

"1"
"123"

拒否される値です。

"0"
"00123"
"-1"
"+1"
" 1 "

0以上の整数を判定したい

preg_match('/^(0|[1-9][0-9]*)$/', $value)

向いている値です。

"0"
"1"
"123"

拒否される値です。

"00"
"001"
"-1"
"+1"

負の整数も許可したい

filter_var($value, FILTER_VALIDATE_INT)

または、表記ルールを厳密にするなら次のようにします。

preg_match('/^-?(0|[1-9][0-9]*)$/', $value)

範囲指定もしたい

filter_var($value, FILTER_VALIDATE_INT, [
    'options' => [
        'min_range' => 1,
        'max_range' => 100,
    ],
]);

小数や指数表記も含めて数値判定したい

is_numeric($value)

ただし、整数判定には向いていません。

まとめ

PHPで文字列の中の整数を判定する場合、最も大切なのは 何を整数として許可するのかを先に決めること です。

単に「整数」といっても、次のように仕様が分かれます。

"00123" を許可するのか
"+123" を許可するのか
"-123" を許可するのか
" 123 " を許可するのか
"0" を許可するのか

半角数字だけを見たいなら、ctype_digit() がシンプルです。

ctype_digit($value)

PHPの整数として妥当か確認し、範囲指定もしたいなら、filter_var() が便利です。

filter_var($value, FILTER_VALIDATE_INT)

先頭ゼロ禁止、符号禁止、空白禁止など、表記ルールを厳密に決めたいなら、正規表現が最も明確です。

preg_match('/^[1-9][0-9]*$/', $value)

一方で、is_int()is_numeric()(int)intval() は使いどころに注意が必要です。

is_int("123");     // false
is_numeric("1e3"); // true
(int) "123abc";    // int(123)
intval("12.9");    // int(12)

実務では、次の順番で処理すると安全です。

1. 入力値を受け取る
2. 仕様に合わせて検証する
3. 必要に応じて整数へ変換する
4. 範囲チェックを行う

特に、URLパラメータやフォーム入力を扱う場合は、いきなり (int) で変換するのではなく、先に検証する ことが重要です。

以上、PHPの文字列の中の整数の判定についてでした。

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

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