これなんて PHP ならではだと思うのだが、「ある変数に入っている文字列が 0 を含む 10 進数の自然数かを判別したい」といったことがよくある。 例えば POST で渡ってきた id が DB の auto increment な primary key (1, 2, ...) として信頼できる値かを判別したい、などだ。
初心者の時大体 is_numeric() を使えなどと教えられるような気がするが全然ダメである。 is_numeric() は「引数が数値であるかを判定する」ものであり、小数点や 16 進数なども通ってしまう:
echo is_numeric(8); // true
echo is_numeric(1.5); // true
echo is_numeric('0x18'); // true
echo is_numeric('0x18e5'); // true
// 厳密にやるには正規表現を書くしか無いのか...
echo preg_match('/^\d+$/', '0x18'); // false
##0 から 9 までのみで構成されているかどうかが知りたいだけなのに
大抵の場合判別したい内容は以下なのであるが、いずれにしても正規表現を書かずに関数一発でできないのが歯がゆい:
- 15 は当然 true
- 0 は数値であり 0 を含む自然数なので true
- -1 は数値であり自然数ではないので false
- '01' は文字列だが自然数 (=1) なので true
- '1.5' は文字列だが小数なので false
- 0x18 は有効な 16 進数だが 10 進数ではないので false
と思ったら CakePHP 2 の Hash::numeric() のソースを見たら ctype_digit なるものを使っていた。 引数に string を渡さないと正しく動作しないのが気になるが、これが一番シンプルか。 単純に 0 から 9 で構成されているかを検査する と:
function f($val) {
// strval してしまうと array を渡された時に Notice エラーが発生するのを指摘頂いた
// return ctype_digit(strval($val));
// リクエストパラメータに対応する場合は strval 要らない
// それでも対応したければ filter_var かければよい
return ctype_digit(filter_var($val));
}
var_dump(f(15)); // true
var_dump(f(0)); // true
var_dump(f(-1)); // false
var_dump(f('01')); // true
var_dump(f('1.5')); // false
var_dump(f('0x18')); // false
var_dump(f([1])); // false. strval かけると Notice エラー
厳密に 0 を含まない自然数のみ通す
……うーん、やっぱり素直に正規表現の方が良いのかも。頭 0 も許可せず 0 も含まない 完全に自然数のみ取得したい場合は以下か:
function is_natural($val) {
return (bool) preg_match('/\A[1-9][0-9]*\z/', $val);
}
var_dump(is_natural(15)); // true
var_dump(is_natural(0)); // false
var_dump(is_natural(-1)); // false
var_dump(is_natural('5')); // true
var_dump(is_natural("5\n")); // false
var_dump(is_natural('01')); // false
var_dump(is_natural('1.5')); // false
var_dump(is_natural('0x18')); // false
注釈
\A
や \z
の意図は @mpyw 様のこちらの正規表現の記事を参照。
また、自然数は一般的に認知されているのは 0 を含まない正の整数 で primary key の値として求められるのはコレだが、実は自然数の定義としては 0 を含んでも良いようなので自然数に関して言及する際は集合に 0 を含むか否かを明記する必要がある。
そういえば大学の時自然数の集合を N とすると N - {0} などと明記していた気がする。