gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

validateについて

頂戴したデータがvalidなのかをvalidationするvalidatorの、どっちかってぇと「理屈」部分を、少し整理してみましょう的な備忘録。
実用一点張りなのと、とりあえず「PHPメイン」で書きますんで、適宜、他言語な方々におかれましては応用したりかみ砕いたりしてご覧頂ければ、と思います。


あとまぁ毎度のことではございますが。
以下はあくまで「おいちゃんの私見」なので、その程度にご覧頂ければ幸いでございます。


んで。


validationってのはつまり「データがvalidであるように」ってな感じで、validってのは「有効であったり正当であったり妥当であったり確実であったり」するようなほにゃらら、になります。
とりあえず「明らかにイカンdata様におかれましては、謹んでお引き取り願いたくここに抹殺いたします」的なノリ。


ただンじゃ「このデータ様は、有効であったり正当であったり妥当であったり確実であったりするのでございましょうか?」という判断基準がまぁ色々あるので。
その辺の、個人的な整理とか個人的なmemoとか個人的な備忘録とか、その辺を書いておこうかなぁ、と思います。
以下読んでもらえればわかると思いますが、「ナニをもってvalidとするか?」ってのは、結構な確率で「業務要件に直結する」ので、システム的な視点に加えて「発注者という意味合いでのお客様目線」を忘れずに、厳しくも優しいvalidatorを書いていきたいものでございます。


ってなわけで、業務的に割とあるんじゃないかなぁ? と、おいちゃんの鶏頭が記憶しているあたりを、つれづれっと。

必須チェック

これはまぁなんていうか基本中の基本。
PHPの場合、$_GETないし$_POSTに
・そのkeyがあるかどうか(issetないしarray_key_exists)
・値が空っぽではないか
の2つのチェックになることが多いと思うざます。


ちなみに文字列の場合

if(false === isset($_GET['hoge'])) {
  return false;
}
if('' === $_GET['hoge']) {
  return false;
}

なんて書き方をすると「じゃぁ半角スペース1文字入れて」とかやられるのが嫌ならせめて

if(false === isset($_GET['hoge'])) {
  return false;
}
if('' === trim($_GET['hoge'])) {
  return false;
}

なんて書き方をして「じゃぁ全角スペースで」ってあたりからイタチごっこの始まりになるざますので、その辺は「なんで必須にしたいの?」ってあたりを、お客様にヒアリングしてみましょう。
ちなみに「全角スペース」についてはmb_convert_kana()のオプション's'で片付けますが、そうすると次は「abc」とか突っ込まれるので B-p


あと。if文をわざわざ分割しているのは「後で初心者に教えるのに都合がいいから」ってダケなので。
「orの左辺がtrueなら右辺は評価されずにifん中に入るよねぇ、って発言がPHPでは使えるよね」って単語が違和感なく理解できるレベルの御仁におかれましては素直に

if((false === isset($_GET['hoge'])) or ('' === trim($_GET['hoge'])) ) {
  return false;
}

とか記述いただければ幸いでございます。
あと「左辺値と右辺値の云々」については「おいちゃんはヨーダ記法応援委員会メンバーなので」ってことで以下略。

数値チェック

正規表現とか色々ありそうなのですが、PHPならおいちゃんはfilter_var()を強くお勧め。
整数オンリーならFILTER_VALIDATE_INT、小数点数ありありならFILTER_VALIDATE_FLOAT。
filter_var()で上述のVALIDATEフィルタ使うと、文字列渡しても、戻ってくるのがintないしfloatになるんで、その点は留意しませう。
後は「負の値の許容度合い」によって確認をしておきたいのと、特に整数をちゃんとintで扱いたい場合、丁寧にいくんなら PHP_INT_MAX 以下であることを確認しておくと、より「心安らかに」いけるやもしれませぬ。

ページ数

いやまぁ丁寧に考えるんなら色々あるんですが、ぶっちゃけるとおいちゃんはこんな風にしてまふ。

$page_num = abs((int)$_GET[ページ数が入ってるパラメタの名前]);

「何が何でもint」でがっつりいくんなら、こんな雑コード。

$page_num = (int)(abs((int)$_GET[ページ数が入ってるパラメタの名前]) % PHP_INT_MAX);

いやまぁ「n Page以上にはなり得ない!」って数値決め打ちしてもよいのですが。実際その手のってコケる可能性高いし、実際これで「でかい数字」入ってきても困らないケースのほうが多いと思うので、こゆのは割と「雑に」validationしたりしまふ。


この辺、validationってよりは、「無毒化」って意味合いにおけるsanitizeに近い概念のような気もいたしますが。
いちいち「エラーで突き返す」のも面倒な気がするので、「強制的に揃える」でもいいんじゃないかなぁ? と個人的には思ってたりします。
もしその辺がお嫌で「丁寧にエラーを返すべきである!」って場合、filter_var()で判定しておくと幸せに過ごせるのではないか、と思います。

金額

ページ数準拠以上終了(笑
いやだって「負の値」を許容するケースは少ないし。
許容される場合、abs外してくらはい。
「金額」系は「むりしゃり揃える」と些か困るケースもあるので、「filter_var()で判定」をチョイスして「駄目ぽいならerrorでつっかえす」ほうが、より適切やもしれませぬよし。


あと、金額の場合「int無理だったからfloatで」は、浮動小数点数型における誤差の問題あたりに起因する理由で「はげしく」お勧めいたしませんので、特に経理系とかで「お金扱う」システムのときは、諸々、ご注意くださいませ。
四則演算など些か手間ではございますが、「全面的に GMP関数 使う」ってほうに振り切ってみるのもひとつの考え方なのではないか? と存じます。

郵便番号

雑に考えるなら「数字3桁」「空白またはハイフンのどちらか1つが、あるかないか」「数字4桁」というフォーマットで確認。
ぶっちゃけ「正規表現のお勉強にちょうど良い」レベルのvalidateが可能で、学習にも最適でございます。


少し踏み込みますと「つい間違えて数字とかハイフンとか空白が全角に」ってあたりに対応して、予めmb_convert_kana()で整えておく方法が、状況とサイトによっては有効です。
ただ、ハイフンは「長音(ー)」になってくることも多いので、考察とテストはしっかりとしておきましょう。


更に踏み込むと「実在チェック」のvalidateを望まれるケースが、実際にございます。
その場合は「郵政省もとい日本郵政グループが出してる郵便番号情報」をどこかに持っての実在確認となりますが、メンテナンスが色々と鬼大変でございますので、その辺しっかりと「おかくご あそばせ」状態になります。


参考)
http://www.post.japanpost.jp/zipcode/download.html (郵便番号データダウンロード)
http://www.post.japanpost.jp/zipcode/dl/readme.html (郵便番号データの説明)
http://d.hatena.ne.jp/gallu/20080310/p3 (■[その他技術]郵便番号にまつわるエトセトラ)


この辺り「どれくらい厳密に郵便番号が欲しいのか」っていう業務要件に結構直結しますので、お客様にがっつりヒアリングして「どする?」って確認をとるとよいでせう。

電話番号、FAX番号

実在を確認するわけにもいきにくい、厄介なものでございます。
とりあえず
・市外局番は2〜5桁の整数
・市内局番は1〜4桁の整数
・加入者番号は4桁の整数
・「市外局番+市内局番は6桁(なんだけど携帯だと7桁)」
ってルールが以前にあったように思うのですが、現状どうなってるか不明。よかったら誰か突っ込んで下さい。


なので、まぁだいたい「そんな感じの文字列」ならvalid、って程度の実装が現実的かなぁ、と思うのです正直。

日付

月と日は割と簡単にvalidateが出来る…のですが、もし「年情報がない」場合、「2月29日」をどうするか、だけ、決めておきましょう。
年情報がある場合には楽なんですが…じゃぁ「西暦0年はvalid? invalid?」ってのと、「西暦9999年は?」ってあたりの「上と下の数値限界」が横たわります。
この辺割と気付かないのですが気付いたときに引っかかると面倒くさいので、悩んでおきましょう。
PHPの場合、checkdate()関数が割と使い勝手がよくて。「閏年を判定してくれる」ほかに、関数の仕様として「年は 1 から 32767 の間となります」って決め打ちをしてくれているので。
お客様に「特段の事情と理由がありましたら別途実装いたしますが、差し障りがなければ、PHPが提供している標準関数ですし、素直に長いものにまかれちゃいませんか?」って辺りをオブラート20枚くらい包んだ上にお砂糖とか振りかけたような「丁寧な」提案で進めていけると、色々と楽ができます。
なお、渡す引数は「事前に数値かどうかチェック」してもよいのですが、どのみち「0が入ってきたらエラー」なわけですし。「intでキャストして揃える」くらいの雑さでも、今のところ特に困った事はないように記憶をしています。


後は。
業務要件によって「明らかに未来日付である必要がある」「過去じゃないとおかしい」「この100年間の出来事だからもっと時間帯を絞れる」などの状況があり得ますので、その辺はお客様及び該当サービスの性質内容対象顧客などを鑑みながら、適宜、幅を狭めていくとよいと思われます。

年齢と誕生日

validateで、割と悩み出すところのひとつです。
ん…実話。
「整数二桁、っていうvalidateをしていたら、100歳のユーザから"登録できねぇ"ってクレームが来た」。
割とおっかないことに実話です。


とりあえず「整数ではない」「負の値である」は確実にinvalidなのですが…じゃぁそれ以降どうするか。
上限と下限を決めるのが、割と面倒です。
上限についてひとつあり得るのは「ギネスの記録にある122歳」なのですが、じゃぁ「ギネスをチェック、更新されたらシステム改修するかね?」って話になって、微妙に嬉しくない感じでございます。
下限についても厄介で、昨今2歳児が普通に「スマートフォンいじってる」風潮ですと、果たして「おいくつまでならvalidなんだろう?」という。
なので、おいちゃんは割と面倒なんで「とりあえず0〜200歳、くらいにしときません?」って話をしますが、最近、一部の研究では「寿命を5倍に増やせる可能性」とかいうお話に従うと500歳くらい?
まぁ「とりあえずこの5年10年じゃ手が届かんだろう」程度の数値にしておくと、とりあえず気楽なんじゃなかろうか? と思いますが、あとは業務要件次第。
年齢が「すげぇ重要」ならそれこそ「年齢確認」から入るべきでしょうし、そうでないんなら「まぁ気にスンナ」でいいようにも思います。


下限の場合、ひとつ重要なのが「本サービスは12歳未満は登録不可」とかいう規約がある場合、当然ながら「11歳はinvalid」なので、この辺も露骨に「業務要件直結」なので、しっかりとヒアリングしておきましょう。


誕生日についてはとりあえず
・過去日付であること
・あとは気にスンナ
がベース。
誕生日が「未来日付」は流石にあり得ないのでinvalid(いやタイムマシンが開発されたら変わりそうですが)。
どれくらい過去か? については「年齢の上限」と一緒。で「わざわざ計算する」手間を入れる必要が、あとは、あるかないか。


「年齢の下限」があるサービスの場合は「計算して、0歳とかならinvalid」…なんだけど、ここ、実は落とし穴があるので注意。
簡単に計算する場合「今日の日付を数字8桁 − 生年月日の数字8桁 / 10000(端数切り捨て)」で、大体求まるのですが。
普通のサービスなら、まぁ「規約に書いておく」とかして、これでもよいかも、なのですが。
これが「法務とかに関わる」お堅い系の場合、「年齢算出の方法」については、しっかりと聞き出しておいたほうがいいです。
http://www.shugiin.go.jp/internet/itdb_shitsumon.nsf/html/shitsumon/a154154.htm

わが国では、「年齢のとなえ方に関する法律」に基づき、昭和二十五年以降数え年による年齢計算を止め、満年齢によって年齢を計算している。しかし、この満年齢の考え方について、国民の常識と法律上の取扱いとの間、さらには各法令相互の間において、齟齬や混乱が見られるように思う。

的な。
http://ja.wikipedia.org/wiki/%E5%B9%B4%E9%BD%A2%E8%A8%88%E7%AE%97%E3%83%8B%E9%96%A2%E3%82%B9%E3%83%AB%E6%B3%95%E5%BE%8B
この辺も参照。


この辺があるので。
「年齢がvalidである、ことを保証するための生年月日のvalidation」は、色々と気をつける箇所が増えるので…逃げられるときは逃げましょう(笑

チェックボックスとかselect/radioボタンとか

ホワイトリストで存在チェック以上終了。
後は「listが増えた時」に、出来るだけDRY( http://ja.wikipedia.org/wiki/Don%27t_repeat_yourself : 1つの変更は1箇所の変更ですむように )になるように近づけるように心がけましょう。

文字

世の中の全ては数値でなければ文字なので、すべて文字です以上終了。
…で終わっても仕方が無いので。
真面目に考えると、文字のチェックでよくあるのが「文字長」で、ここが多分色々と引っかかったりするので、そこを軽く。


文字長には、少なくとも「3つの」意味合いがあって、そこが混在している上に「なんで文字長を制限したいのか?」によって状況が変わるので、その辺りをしっかりと意識しながらvalidationしていくとよいでせう。


ひとつが「システム上、バイト数制限がある」ようなケース。
PHP的にはstrlen()関数を用いて「バイト数」で確認をしましょう。


ひとつが「業務要件上、文字数を制限したい」ケース。
ん…実際あったのが占いのシステムで「質問者の質問は500文字以内」「占い師の返信は1000文字以上」ってなケース(文字数はバミってます)。
別口だと、twitterの「140文字」が、このチェック方式。
この場合は「1つの文字を1とカウント」なので、mb_strlen()を使ってvalidateしましょう。
「プリンタブル(printable)ではない文字、及び空白は文字数に含まない」的なカウントをするケースもあり得て、上述だと「占い」の時はそういうチェックをしていたので(そうしないと、困った占い師さんが「スペース大量に使って文字数を埋める」とかってケースがあるのを懸念されていたので)。
その辺もかなりガチに「業務要件に密接に影響する」ので、しっかりと確認をとるようにしましょう。


最後が「表示上の文字幅を制限したい」ケース。
厳密には等幅フォントではなくプロポーショナルフォントである事も多いとは思うのですが、とはいえ、特にデザイナーさんが、HTMLっつかCSSを組む時なんかに、結構この辺を気にされます。
「ここの値でこれ以上の幅になるとレイアウトしゃれになんねぇから断じて許すまじ!」って方向のチェックで、デザイン性を大切にするような業務要件だと、急激にこのニーズが増えます。
この場合はmb_strwidth()を使ってvalidateして、「デザイン的に許容範囲の幅であろう」という正しさを追求してくださいませ。

文字コード

いやまぁ別にどんな文字コードでも基本問題はないのですが。その文字コードが問題の無いものであれば(具体的にはsjisutf-7は滅べ)。
それ以外に、いくつか「半端な先行バイト問題」とか「非最短形式のUTF-8」とかがあるので。
これはどちらかというと「エラーならはじく」よりは「揃えちゃう」ほうが楽なので、mb_convert_encoding()で一度「文字コード変換」しておくと良いと思うです。
例えば「HTML(から入ってくるデータ)がUTF-8」で「内部の文字コード想定がUTF-8」だったとしてもなお、「UTF-8からUTF-8への変換」ってのをやっておくと「変なのが一通り削られたりして」出てくるので、色々と気軽なんでやっといたほうがよいと思われるです。


この辺もまた、「無毒化」って意味合いにおけるsanitizeに近い概念のような気のする処理ではありますが、やっておくと色々と、心が安らかになる事が多いです。

名前とかハンドルとか

一個あるのが「重複チェック」なのですが…最近減ったなぁ。
後は「長さ」くらい?
「半角スペースのみ、は認めない」とかも割合あるので、文字種について軽く確認をしておくとよいです。

お名前とかででてくる、ひらがな縛りとかカタカナ縛り

以前に書いたので、リンク使って省略(笑
■[PHP]カタカナをvalidateしてみる
http://d.hatena.ne.jp/gallu/20150111/p1


ひらがなの時の「長音(ー)」が抜けやすいような気もするのでお気を付けて。

個人的には「カタカナって書いてある箇所にひらがなを入れた」ら、それくらいはmb_convert_kana()で揃えてあげりゃいいじゃん、って思うんですが、どうなんですかねぇ?
半角カタカナもおいちゃんは「システムで揃えて」全角カタカナにしちゃうほうです。
お客様に提案して「それは嫌だ」って言われた事ないんで、そーゆー方法もあるでよ、って事で。

全角文字縛り

たまに見ますが「使いにくいことおびただしい」のでやめましょう。
なので、考察もすっとばします(笑

フリーな文章

ここについてはもう「なんでもどうぞ」としか言えない。
明らかにSQL-InjectionやXSSを誘うような文字列だったとしても、それが例えば「掲示板に"こういう入力がきたらクラックされちゃうよ"っていう警告を書いた」ものであるのなら、文字列としてはそれを「invalidである! 反逆である!」とは言いにくいものがあろうかと思うので。
なので、ここについては「ナニをもってvalidとするかね?」ってのが難しいので、基本、文字長くらいしかみません。


が、ひとつ業務要件としてちょいちょい出るのが「NGワード」の類い。
「このあたりの単語は非常にふさわしくなく好ましくないので、書き込みできないように」なんてお話は、まぁちょいちょいあります。
なので「NGリストを用意」「strpos()ないしstripos()で検索」「ひとつでも見つかったらinvalidってことでエラー」って実装を見かけるのですが。
これをやると、やまもといちろうさんが「不適切な名前です」って言われてゲームが出来なくなったところをBlogで晒されたりします(笑
http://kirik.tea-nifty.com/diary/2014/07/gmo-a7df.html
とりあえず「最後の3文字だけ」切り抜いて適宜ググったりしていただけますると、理由と意味が見えてくるのではないかと思われますがこれ以上は私の口からはとても…。


閑話休題


まぁNGワードは本当に難儀ぃなので「明らかにアウト」な文字だけとは限らないんですね。
なのでおいちゃんは
・レッドカードリスト:含まれてたら一発アウトでinvalidなので突っぱねろ!
イエローカードリスト:含まれててもとりあえずinvalidにはしないけど運営側に自動で連絡がいくので後はマンパワーで確認してやばきゃハネてね
っていう2段構えで実装を提案することが多いです。


あとはまぁ「明らかにNGなワード」にちょっと小細工をしてみるケースとか。
例えば「NGワード」がレッドカードリストにあるとして、ありがちなのが「N Gワード」とかスペースで回避。
これをブロックすると、極端な話例えば「たぬきでNたGたワたーたド」とか、まぁどうとでもなってしまうので、あんまり深追いしても疲れますが業務要件的に重要な事もあるので、しっかりとお客様とのお打ち合わせを擦り合わせていきましょう。


掲示板系で電話番号なんかですと「全角数字」はおろか「漢数字」とか「ひらがな(ぜろ いち にぃ さん)」とか、その混在とか、まぁ色々と手口が広がるので。
運用コストと相談しつつ「ナニをもってvalidとし、ナニをもってinvalidとするか」という部分の丁寧な調整と設計と実装と変更と追記は、重要なポイントなのではなかろうか? と推測いたしまする。

IPアドレス

filter_var()でFILTER_VALIDATE_IP。
IPv6すらチェックできるという仕様なので、大変に便利すぎて乱用必須のレベル。


後は例えば「Cクラスのみ」とか「プライベートIPアドレスは刎ねたい」とかってニーズがあるのであれば、適宜、チェックしませう。
…で、注意点。
IPv4の場合とか、ip2long()やPEAR::Net_IPv4()を想起するケースもあろうかと思うのですが。
この辺、PHPのINTの仕様で「時々困る」ケースがあって。
具体的には、ip2long()の場合「環境によっては負の値がreturnされる」とか、PEAR::Net_IPv4()の場合は「PEAR::Net_IPv4::ip2double()はIP アドレスを PHP の倍精度数に変換する」とか、色々。
以前、その辺に「イラッ」として、ついこんなモンを作ったけど後悔はしていない(笑
https://github.com/gallu/MagicWeapon/blob/master/ip_util.inc


まぁこの辺も、丁寧にチェックしたい場合は気にするようにしておきましょう。

ドメイン

結構幅広い「validation方法」が考えられますので、ちぃと丁寧に。


まず最低限「使っていい文字種」の確認で、ここで駄目なら確実にinvalid。
使っていいのは「英字、数字、ハイフン、ドット」。最近、実務の仕様書だかソースコードだかに「アンダースコア」って書いてあるのみてずっこけた記憶がありますので、皆様におかれましては是非お気を付けくださりませ。
あとは「ドットの連続はNG」とか「先頭は英字」とか「ドットで区切られてるラベルのおけつがハイフンは駄目よ」とか。
日本語ドメインは面倒なんでオミット。


ここからが「どこまでやるの?」ってお話。
まず文字長。RFC1035(より新しいのなかったっけ?)を真面目に捕らえると
・ラベル(ドメインの、ドットで区切られたうちの1パーツ)は63文字以内(正確に書くんなら63オクテット以下)
・ドットを含む全体として255文字以内(正確に書くんなら255オクテット以下)
になるのですが、まぁこれをどれくらい丁寧に考えるか。
ただここは「実際にもうちょっと長くなっちゃった」っていう状況は考えにくいような気もするのですが、どうなるんでしょうねぇ?
「大丈夫がっちがちだよ」って人はこの数値で、「世の中って動くからねぇ」って思う人は、もうちょっと長めで確認をすればいいんじゃないでしょうか。
「かっちり」実装した場合は、定期的にRFCとかをチェック、数値が動いたら「ちゃんとメンテナンス出来る」準備だけはしておくとよいでしょう(営業的ネゴシエーションを含めて)。


次に「そこまでやれば」ってのが実在チェック。
「文字列として書式があっている」と「そのドメインが実在している」には大きく乖離があるのと、でも大体どっちも「validなドメインである」という言い方をして、意味合いが混ざる事が多いので。
「書式があってればいいのか」「実在を確認する必要があるのか」を、確認しておくと、よいvalidatorが書けるでしょう。

メアド

はい業務要件で「よく出てきて」「一番死ねる」ブツです。
先に晒しておきますと。googleさんあたりで「PHP mail 調べる」あたりで上位に出てくる
正規表現:メールアドレスかどうか調べる( phpspot.net/php/pg%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE%EF%BC%9A%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E8%AA%BF%E3%81%B9%E3%82%8B.html )
・かなり使えるPHP正規表現まとめ( www.ideaxidea.com/archives/2009/03/practical_php_regexs.html )
・メールアドレスの書式が正しいかチェックする正規表現( blog.pinkmonky.net/detail/?id=45 )
あたりの正規表現を使うと………


phpspotの人は正規表現について語らないほうがいいのでは( http://developer.cybozu.co.jp/akky/2007/10/phpspot-bad-regex/ )
正規表現のサンプルが更新されていた( http://d.hatena.ne.jp/elf/20090323/1237742585 )
PHP使いはもう正規表現をblogに書くな」と言わせないでくれ( http://blog.livedoor.jp/dankogai/archives/51189905.html )
さっきPHPerの正規表現を見たが、本当に唖然とした。Disりたい…( http://webkit.seesaa.net/article/129745981.html )


などというお話の渦中に突入する可能性があるので、熟考の上「使うか使わないか」を決めるとよいでしょうっていうか使うの辞めた方が多分身のためよ?
ちなみに「間違えているほうのPage」のhttp://を削ってるのは当然ながら「これ以上googleで上位に上がってくれるな」という強い主張に基づくものです。


さて上述を踏まえまして。
ひとつ、PHPには有難い関数がございます今回使用頻度高いなぁfilter_var()でございますこちらのFILTER_VALIDATE_EMAIL。
非常に丁寧につくられておりまして、かなり頓狂なアドレスでも適切に「RFC的にvalidかどうか?」を判定してくれます。
「じゃぁこれでイイじゃん」と言えればよいのですが…日本の、いわゆるガラケーのメアドに「RFC非準拠なアドレス」がありまして、その辺が大変に邪魔臭ぉございます。


docomo
http://ke-tai.org/blog/2009/04/03/docomorfc/
http://www.rbbtoday.com/article/2009/04/03/59059.html


AU
http://ke-tai.org/blog/2009/09/25/aurfc/


ちなみに余談。
http://www.snow-flake.jp/%E3%80%8C%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%AE%E4%BB%95%E6%A7%98%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E3%81%A4%E3%82%82%E3%82%8A%E3%81%AF%E3%81%82%E3%82%8A/
なんていうか「滅べ」。


さてまぁこんな「素敵な仕様を考えやがりやがった馬鹿どももとい御方々」のおかげで、filter_var()が使えませんコンチクショー。
じゃぁどうする? ってあたりで、後は考えてみてください…ってのも雑なんで、ざっくり「我留流(おいちゃん流)」を。
http://d.hatena.ne.jp/gallu/20141120/p1
にもありますが
・domain-partは、ドメインの仕様に従ってチェック
・local-partはあきらめて「文字があればいいや」
程度。
ざっくりの極みでございます。
実装例、こちら。
https://github.com/gallu/MagicWeapon/blob/master/is.inc
のis_email()。


で…最大のポイント。
ドメインでもちょろりと書きましたが、メアドのvalidateって「書式があっている」と「実在している」の2つのニュアンスがあって。
で、emailの場合特に「実在している」のほうにニュアンス的重きを置くケースが圧倒的に多いんですね。
今時のSMTPサーバでVRFYコマンドが通るような設定がありゃいいのですが、550以外が帰ってくるSMTPサーバ、どれくらいあるんですかねぇ? 正直。
いや、あるんなら「ドメインのMXレコード引いてAレコード引いて、local-partはVRFYコマンドで確認」すりゃ、実在確認取れるんですがね。


現実問題、実在確認をするのであれば、実際に「emailのやりとり」が必須になるので、かつ、業務要件的に「書式があっていること」ではなく「実在すること」がvalidな条件である、というケースは「極めて多い」ので。
そうするとやり口としては
・こちらで用意したemailアドレスに空メールを送ってもらう
・送られてきたmailのFromからメアドを抜き出す
・Fromのメアドに「token付きのURL」付きのmailを返す
・ユーザが「token付きのURL」をclickする
・実在を確認する


か、或いは
・メアドを入力してもらう
・メアドに対して「書式が適切か?」のvalidationを行う
・入力されてvalidなメアドに「token付きのURL」付きのmailを返す
・ユーザが「token付きのURL」をclickする
・実在を確認する


って流れにならざるを得ないわけですな。
やれやれ。

まとめ

なんか「ふと思いついた」から書いた割にはすげぇ量になりました。
ぶっちゃけますと推敲していないので、typoとかの可能性もありますし、ナニカが抜けている可能性もあります。
その辺、突っ込みをいただけるときっとこの原稿の精度があがるので、お気づきの方におかれましては突っ込んでいただければ幸いです。
おいちゃんも、自分で気付いたら追記とか(日付入りで)書きます。


「ふと思いついた」んで書いた、にしてはえらいこと分量が増えましたが。
なんかの役にでも立てば幸いでございます。