がるの健忘録 このページをアンテナに追加 RSSフィード

2015-04-22

[][][]共通化は二回目

久しぶりの「がるパタ」でございます(笑


「同じ意味を持つ処理」をコピペするような輩は、オロドルインの中腹にある火室サンマス・ナウアにでも放り込んでしまうとしまして。

ちなみに「違う意味だけど同じ処理」の場合に「まとめるかまとめないか」は、議論に値するところではございます(おいちゃんとしては「意味レイヤーで別のメソッドにしつつ」「実装レイヤーで一端一箇所にまとめる」事が多いですが、例外もあります)。


割とあるのが「これは絶対に共通化して後で使うだろうから先に共通化しておこう」っていう発想でクラスとかメソッドとかを増やすケース。

先に言っとく「やめとけ」。


プログラムの世界には「YAGNI」という原則がございます。

You ain't gonna need it、という英文の略語ですな。「どうせ使わねぇよ」って超訳を入れておきます。


「あとで使う」は、大半が使われないものになります。

でも「それを作るために」時間が費やされます。


あぁなんで無駄なんでしょ。

だからがるパタではこう言います「共通化は二回目」。


「あとで絶対にもう一度使うだろう」処理は、そこをまとめて「寄せて」書いて、上下に、大体2〜4行くらいの空改行をあけて、後で「余裕でカット&ペーストが可能」なように、準備だけしておきます。

で「やっぱり予想通りだよ」って状況になったら、自分の先見の明を褒め称えつつ、切り取って共通化すりゃいいんです。


この方法だと「どこまでを切りだそう?」って悩む事もありませんだって「直面している」状況だから、悩みようがないし。

かつ、絶対に「共通化が必要なタイミング」でやってるから、無駄もありません。

ついでに「もし予測が外れた時」も「うん何となくここは1つ固有の処理をしているから、少し見やすくしておいたんだよ」と言い訳がききます B-p


「うまく共通化できない」「どうやってメソッド切りだそう」とお悩みのあなた、是非、一度試してみませんか?

2015-02-09

[][][][]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とかの可能性もありますし、ナニカが抜けている可能性もあります。

その辺、突っ込みをいただけるときっとこの原稿の精度があがるので、お気づきの方におかれましては突っ込んでいただければ幸いです。

おいちゃんも、自分で気付いたら追記とか(日付入りで)書きます。


「ふと思いついた」んで書いた、にしてはえらいこと分量が増えましたが。

なんかの役にでも立てば幸いでございます。

2012-07-25

[][]開発手順草案

開発手順の草案とか考えてみました。

色んな手順あるかと思うのですが、一度、自分の手順を色々と「揉んでみたい」なぁ、って思ったので。

目的

ある程度低コストで、かつ「複数のエンジニア&デザイナ」が存在していることを前提に。

運用が楽で、且つある程度の安全性が保てるような「開発手順」について、それを定めることを目的とする。

注意

マニュアルっぽく書こうと思ったのでなんか断定的な書き方がありますが、基本的にこれは「草案」です。

検討して議論してもむための「素体にする」ため、程度のものなので、色々とご注意ください。

事前準備

サーバは、物理的には2台用意する(仮想マシン2台でもOK)。これがミニマム。

論理的には、以下の役割がそれぞれ存在する。

  • gitサーバ(ないし、適切なバージョン管理用サーバ):redmineなどを合わせて載せても良い
  • 個人用開発Webサーバ(個人毎なので複数)
  • 個人用開発DBサーバ(「開発用DB」で1つ作り、これをみんなで使う。あと、必要に応じて「個人専用」を、臨機応変に)
  • ステージング用Webサーバ
  • 本番用Webサーバ
  • 本番用DBサーバ

ミニマムで進める場合、「gitサーバ+個人用+ステージング」で1台、「本番用」で1台とする。

ざっくりした開発の流れ。

  • 開発者は、git pullしてから適宜個人開発環境で開発/デバッグ
  • 個人開発環境で問題が無ければ、gitのbare/masterに対してpush
  • ステージングにて、bare/masterからpull
  • ステージングに持ち上げてDBを「開発用DBサーバ」に向けてテスト
  • ステージングに持ち上げてDBを「本番用DBサーバ」に向けてテスト
  • 本番化判定を行う(どっちかというと政治的行為)
  • 「ステージングのファイルをrsyncする事で」本番化

前提

環境に依存する設定ファイルは「シンボリックリンクで解決する」ものである、とします。簡単な説明は http://d.hatena.ne.jp/gallu/20120724/p1 をご覧ください。

別に環境変数とif文でもいいのですが、シンボリックリンクのほうが「1ファイルのサイズが肥大化しない」ので、色々と気が楽です。

これは「強制」ではなく「推奨」なので、状況その他的に「どうしても環境変数が良い」という場合、それを「何が何でも禁止する」ものではありません。

詳細:前準備

ドメインを用意する

本番、ステージング、各個人用のドメインをそれぞれ用意しておきます。

サーバを用意する

本番、ステージング、各個人用のサーバをそれぞれ用意しておきます。DBは、本番と開発用DBと、各個人用とをそれぞれ。個人開発環境(と、多分ステージング)は、httpd.confでvirtual_hostの設定とかがいると思われますので、適宜。

バージョン管理はgitを想定。SVNでも多少の修正で行けると思うけど。

バグ管理システムはあった方がよいと思う。別にredmineである必要はないにしても(でもメジャーだよねぇ)。

ポイントは「本番とステージングは、絶対パスまでを全部クリソツ*1にしておく」事。圧倒的に面倒が減るから。


・gitの準備

とりあえず適当なところに1つ、bareなレポジトリを作成。ここを「絶対主」とします。

個人環境でもとりあえず各自 git init なんぞを。ステージングも git initしておきまふ。


・configの準備(これは初手じゃなくて、開発が始まってからになるかも)

シンボリックリンクを「環境毎に」適切に張っておきます。

詳細:開発本番

開発する

・開発内容を確認

・何はともあれ git pull で、お手持ちを最新にする

・お好みでブランチを切ってもいいし、このタイミングではとりあえず切らなくてもいいし

・開発する。もし「DBにcreate tableとかalter tableとか」する場合、memoって置くと楽。configに追加修正するときもmemoっておこう。「DBの修正」が入る場合、開発環境のDB接続用configを「開発用DB」から「自分専用DB」に切り替えておきましょう。

 >まずテストコード書くとよいよ

 >「これが通ったら完了」なシナリオを先に準備しよう

 >まずは「日本語プログラミング」で、プログラムの流れを整理整頓

 >実装!!

 >「予定されたシナリオ」クリアチェック!!

・デバッグまで含めて「いい感じに実装が終わったら」git pull & git pushする。もしgit pullでなんかmergeされたら、pushするまえにちゃんとテストし直すんだよ?

・さっきのDB修正とかconfig修正とかのmemoを周囲に連絡。configあたりは自分で修正して「修正したからpullっといて〜」ってのもあり。DBの場合、開発用DBには(周りに声をかけた上で)速やかに修正。本番用DBはPM等との相談になるけど、基本「とっととやっといたほうが」安全な事が多い。

・DBの設定が「個人用」になってたら、「開発用DB」にスイッチ切り直し。このタイミングで念のためにもう一度テストしておくと丁寧。

・redmineとかで管理してるなら、チケットを「おわた〜」的statusに変更。


で、終了。


ステージングに持ち上げて確認する

・BEGIN(トランザクション的な意味で)。本番化が近々行われる場合だけ、注意。「ステージングのチェック中に本番化が平行で行われる」とまずいので、そこはちゃんとスケジューリングを。

・ステージングのディレクトリで、まずはgit pull

・DBを「開発用DB」用にスイッチ

・テスト。ここでこけたら速やかに開発者に突っ返して修正してもらう

・DBを「本番環境」用にスイッチ。

・もう一回テスト。DBの修正漏れとか、ここで見つかる。100%とは言わないけど、テーブル追加とかカラム追加とか、大抵「現在の本番には影響が出ない」事がおおいから、先にやってとっととこの辺で見つけとくほうが楽なんだよね

・もし「ステージング会議」的なものがある場合、ここで「会議に差し出す」準備が出来た感じ。無ければ「本番化」を、タイミング見繕って開始する!!


政治的問題がなければここで「本番化準備」完了。

政治的問題がある場合、ここから「稟議の場」に意向して「リリースOK」の判子をもらいに行きます。


本番化への道のり

・大丈夫なはずだけど、一応「ステージングでソースコードチェック中」なら本番化中止。

・エンジニア&運用さんに待機してもらう(多分10分くらい)

・rsyncコマンド(あらかじめファイルに用意しておこう)で、本番化。具体的には以下の通り。

 >※「シンボリックリンク」はちゃんと --exclude つかって「除外」しておくこと!

 >rsyncで「本番マシンのコードを」「ステージング環境のバックアップディレクトリに」一通りcopy

 >rsyncで「ステージング環境のコードを」「本番マシンに」copy

・待機しているエンジニア&運用さんが、本番サイトをチェック

 >万が一「やべぇ」な場合、急ぎ「ステージング環境のバックアップディレクトリ」を「本番マシンに」copyして、なかったことにする。でも出来れば、情報は少しでも得ておきたいところ。


問題なければ「本番化終了」。

お疲れ様でした!!


開発中に「緊急バグ修正」とか入ったら?

・gitのブランチを切る

・ブランチ先で「バグ修正」をやる

・ブランチをbareレポジトリのmasterにpushしてステージングチェックまでをやる

・localのブランチ(緊急バグ修正)とlocalのmaster(開発中のブツ)をmergeする

・一応バグ修正が消えてない&エンバグしてない事を確認する

・開発を続ける


疑問点

・gitのレポジトリ管理どする?

http://keijinsonyaban.blogspot.jp/2010/10/successful-git-branching-model.html

とかが元ネタ。

メインブランチ+サポートブランチのやり方とか、綺麗だと思うしできれば載せたいんだけど…上述の開発の流れで実際に「じゃぁどこに載せよう?」って時に、悩みっぱなし。

分散型だからってのもあるんだけど。「localのmasterで開発して、問題なければremoteのmasterにpushする」で事足りちゃうような気がしてならない。

いやまぁもちろん「メインタスク着手時に緊急タスクが来たら」ってのはあるんだけど、その場合、そのタイミングで適宜、localにブランチを切り直してそっちで作業すればよいかなぁと思うので。まぁ個人が「あらかじめ手元に持ち込むときはブランチ切っておく」事について、否定するつもりは0.1mmたりとてないのですが。

「origin/master は、製品として出荷可能な状態を常に反映する、ソースコードの HEAD のありかであるメインブランチ」「origin/develop は、次のリリースのための最新の開発作業の変更を常に反映する、ソースコードの HEAD のありかであるメインブランチ」だとすると。

多分おいちゃんの開発手順では「"ステージングにあるソース"がorigin/master相当で」「origin/masterがorigin/develop」なんだよなぁ、っと。

この辺大分と色々懐疑的なので、突っ込み大歓迎いたします。

できれば、少し突っ込んで議論したいところなので(苦笑

# それこそ、場合によっては「別ブランチ」で議論したいw

*1:…言い方古い?

2012-07-24

[][]シンボリックリンク関連2種

Windows系の人には「ショートカット」って言うと通じるのかしらん?

割とおいちゃんは多用するのですが、あんまり使わない人が多いぽいので、備忘録。


前提条件

シンボリックリンクの説明はよいよね?

ln -s

コマンドで作るアレでおま。

  • sつけないハードリンクは…そも、多分見たことないぞい?(笑

その1:インフラでシンボリックリンク使いやがれ

daemonのinstallなんかでよくつかいます。

実例。

例えばapacheをinstallします。デフォだと /usr/local/apache2 とかになるのかな?

おいちゃんは、configureで指定して、これを、例えば /usr/local/apache2.2.22 とかいうディレクトリにしておきます。今時は2.4系だろうって? まぁ気にしない。

んで、installした最後に

ln -s /usr/local/apache2.2.22 /usr/local/apache2

ってやっておきます。

後日、例えば2.2.23が出たとします(…でるのかねぇ?)。

その場合、/usr/local/apache2.2.23にmake installしてから

rm -i /usr/local/apache2

ln -s /usr/local/apache2.2.23 /usr/local/apache2

ってやります。

ディレクトリは一通り置いておいて、スイッチを「がちゃこん」と切り替える感じですね。


これだと、万が一「やべぇ2.2.23だめぢゃん」って時に、

rm -i /usr/local/apache2

ln -s /usr/local/apache2.2.22 /usr/local/apache2

と、スイッチ一つで簡単に切り替えがきくので。


一方で。

普通に/usr/local/apache2にmake installしちゃうと、切り戻し大変でしょ?


この手法は、UNIX系の「魔物」な先達から教わりました。

今でも重宝をしております。


その2:configでシンボリックリンク使いやがれ

で、上述の応用。

configファイルの、例えば「本番とステージング」とかさらには「個人開発環境用」とかって、皆様どんな風に管理なさってらっさるのかしらん?

割とよく見るのが「環境変数による」if分岐なんだけど…とりあえず「8000行のconfig」とか、なんていうか心が痛む前に書いたやつを痛めたくなっちゃうお!!

で、おいちゃんがよくやる手段。

プログラムでは「config.php」をincludeします(いや別にfile_get_contentsして自力でパースとかでも全然OKだし一緒なのですが)。

実際のファイルは

re_config.php 本番用

st_config.php ステージング用

gallu_config.php おいちゃん個人環境用

hoge_config.php hogeさん個人環境用

って感じ。

あとは、例えばおいちゃんの個人環境なら

ln -s gallu_config config.php

ってやっておけばOK。

ファイル管理とかrsyncとかでは、シンボリックリンクのファイル(上述だとconfig.php)を除外するようにする設定だけ忘れずにね。

gitだと「.gitignore」ファイルに書いておけばいいし、rsyncなら--excludeオプションでファイル名を指定して除外すればOK。


この方がごちゃごちゃしないと思うんだけど、どうですかしらん?

[][]configの話

で、ついでにconfig関連のおいちゃん的見解。

端的には「いいじゃん一カ所にまとめなくても」。


んと…分解して。

DRYはとても大事だと思うです。ここに異論はなし。

ただ、現実問題として「ン千行とかのconfigファイル」を見るにつけ、なんていうか「倦んずあり」なんですわ、正直。

過去にみたconfig、いくつかの設定が「2度も3度も書かれていたり」して、明らかに「管理できてない」感じだったし。


で。

そんなこんなを経て、「これくらいがちょうどよくね?」的な落としどころを考えてみました。

っつかおいちゃん自身は大分前からこのパターンなので、一度書いておこうかなぁと思ってね。

今、絶賛「暗黙知を形式知に変換すること推奨期間」なのでw


・「運用さん」とか、プログラマ以外が変更する可能性のある設定

問答無用でconfigに。できれば、形式も「name=value」のような、わかりやすいものにしましょう。

よくあるあたりで例えば「mailを送るときのmail_fromのメアド」とか。

コメントが書けるようにすることも重要。


・「プログラマしか変更しない」けど、環境によって値が異なる設定

これもconfig。ただ、上述とは「別の」ファイルがいいと思うなぁ。で、シンボリックリンクでスイッチングする、っと。

DBのアカウント情報とかがこの辺。ドメイン情報系(自分のドメインとかmxドメインとか)もここだねぇ。


・1クラスでしか扱わない設定

これはconfigに「切り出さない」で、クラス内で持っちゃう。クラス定数にするのかメソッドにするのかプロパティでfinalにしとくのか、とかはまぁ色々好みもあるだろうけどさ。

どうせ「そのクラスが知ってればいい」んだから、下手に「共通config」なんていう「なんでもぶち込めるところ」に入れないで、可能な限り、ギリギリまで「クラスの中で」持っておく。

どうしても「いかんともしがたくなって」から、外に切り出したって遅くはないんだし。


・「プログラマしか変更しない」けど、複数箇所で扱う可能性の高い設定

いわゆる一般的に、定数とかconfigファイルで持ち回るのが、ここ。

ただここはよっぽど気をつけないとあっというまに「膨らむ」ので、できるだけ「クラスの中に閉じ込められないか」を検討したほうがいいと思う。


こんな感じかなぁ。

みなさんは、どんな風にconfigを持って、整理しているのかしらん?

2012-01-13

[][]芥子の種を持ってきてもらう

システムを作っていると、時々*1、無茶な、或いは無理な要求に遭遇します。

無理であることを説明してもよいのですが…いまひとつ、説明が理解いただきにくいシーンというのが、極めてごく稀に、ほんのわずかばかり、微々たる例外的ケースとして、可能性があります*2


実力行使で(精神的を含む)暴力に訴えるのもまた一興ではあるのですが、あまり楽しい結果にはならないことも少なくないので。

最近つかっているメソッドを紹介いたします。


端的には

・相手に、(彼ら的には)一見可能そうで、その実不可能な「依頼」をしてください

・で、「その依頼の成果がでたら」実装可能です、と、にこやかに伝えます


「うちのサイトの被リンク数が知りたいので、googleとかを使わず、独自で計測できるプログラムを作成してください」とか言われてみたとします。

被リンク数とは「他のサイトから当該サイトへのリンク、が張られている数」です。

母数は、基本的に多分「全世界にあるすべてのWeb Page」です。

いっぱしのエンジニアであれば、考える前に「母数 = 全世界にあるすべてのWeb Page? はぁ?」となるのですが。

これを前提に「ンなもん無理です」といっても、聞き分けのよくない子ちゃんが、ごく稀にいます。


ですので、このように話をします。

「なるほど被リンクですね。では、ベースになる"全世界のすべてのWeb pageのURI"のリストを頂戴できますか? そのリストがあれば、時間はかかりますが(HTMLクロールするからねぇw)、できなくもありません」。


…いやいっそ素直に「無理」って言ってあげたほうが、って気もするのですが。

時々、本当にごく稀に、きわめてレアなケースとして「自分の思い付きが最上位にあって、人の忠告がいまひとつ耳と脳に入り込まない」タイプが、ごく少量、いらっさるので。


元ネタは、お釈迦様の逸話。

こちらのサイトを引用させてもらいまふ。


http://homepage3.nifty.com/junsoyo/hiros/hiro49.htm

キサーゴタミーは,やっと授かった子どもを亡くした。「死んだ子どもを生き返らせる薬」を求めて尋ね歩き,半狂乱でやった来たキサーゴータミーにお釈迦さまは,「芥子(カラシ?)の種を貰っておいで。但し死人を出したことの内ない家から」と言うので,彼女は必死になって死人を出したことのない家を村中探すのだけれど,そんな家があるわけがない。そのうちに彼女は「死者を出したことのない家はない」ことに気づき,「生あるものは必ず死ぬ」というお釈迦さまの言葉を悟って仏弟子となった。


つまり「気付きがあるまで、芥子の種を探し回ってもらいましょう」と。


まぁ「万が一、芥子の種がみつかっちゃうと」もちろん「実装できるんだよね」といわれるので、その辺はちゃんと真摯に設計と論理的考察をする必要があるのですが(別に、無茶を吹っかけることそのものが目的ではないので)。

「100%無理」ではなくても「ちょっと工数膨大ぢゃね? その話」って時は、一度「芥子の種を探してみて」って言ってみると、もしかすると、今までよりは「少しだけ」楽になる、かも、しれません。


保証はしませんが B-p


あぁ真面目な余談。

この辺の話をふって「ちゃんと考えてくれる」お客さんは、なんだかんだ、貴重です。

そういうお客様は、大切に関係性を作っていきましょう。


追記

芥子は、「カラシ」ではなくて「ケシ」であるそうです(岩波仏教辞典、を引いてくださった方がいらっしゃいました。詳しくはコメントを参照してください)。

*1:正確な頻度については言及を避けます B-p

*2:つまり「しょっちゅう」