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

2016-07-24

[]超ピンポイントノウハウ

AWSのS3にファイルをアップする、ってのを含む処理を書いていたのですが。

php-timecop(の、timecop_freeze())を使った状態でアップをしようとすると

The difference between the request time and the current time is too large.

って怒られました(笑


いやまぁ結構細かい所見てるんだなぁと思ったのですが、興味深かったので。


とりあえず一端、(どのみち局所的なテスト用だったので)timecop_freeze()をコメントアウトしてみたのですが。

timecop_return()を呼んだほうがいいかもなぁ、とかは思いました(ちなみにtimecop_return()でもちゃんとうまくいきました)。


「あと何回こんなケースがあるかね」いうくらいレアケースな気がするのですが、面白かったので、メモり。

2016-07-12

[][]低糖質レシピのメモ書き

まだ作ってないのも多いので、簡単なmemo書き。


大豆

調べるとなんかスィーツが多いんだよなぁ。蒸しパンとか、割と(材料的にも時間的にも)手軽でよさそう。


豆腐グラタン

「豆腐クリームに牛乳とかチーズとか入れる」「鶏肉とか具材に使う」で、割とボリューミーなブツができそう。


ココナッツミルクカレー

ココナッツミルク、粉があるので、それ使うと保存も楽そうなので便利そう。

ココナッツミルク糖質も、100g中3〜5gくらいらしいので、結構、低糖質。小麦粉使わないと、カレーは結構いい感じ!!


まぁうちは「ロカボよりさらにゆるい糖質制限( http://d.hatena.ne.jp/gallu/20140705/p2 )なので。

ロカボの糖質が大体130gに対して、うちの想定が225gグラムくらいなので(これで、体重にもよるんだけど、一日摂取カロリーの50%が糖質)。


まぁ「炭水化物の量」以上に「炭水化物の質(=GI値)」とか、吸収の速度に少し重点置いてる感じでやってるので、こんなもんでしょ。

[]物騒なタグ始めますw

いやまぁせっかくなので、備忘録程度に。

ネットに転がしておけば、いつかどこかで誰かの訳に立つ日もくるでしょw


とりあえずおいちゃんのお話をすると…悪性リンパ腫にひっかかりました。

基本は「びまん性大細胞型B細胞リンパ腫」なのですが、43%だかくらい「バーキットリンパ腫」が混ざっているという、割と珍しいタイプだそうです。

普通は「1つのがん細胞から増殖する」から「全部型が一緒」ってのが普通らしいので、二種混合はレアw


発端は胃の激痛。「胃潰瘍かなぁ」と思ったのですが、2〜3週間ほど続いたので(確か3月中旬から胃に激痛)。

ちと別件もあって「転院」した直後だったので(転院前の病院では「胃が痛い」「胃潰瘍でしょう」で流されていたのですが B-p)。

転院先の病院で「念のために胃カメラ飲みましょう」で胃カメラ飲んで発覚。


レアケースだとは思うけど「胃の激痛が悪性リンパ腫の可能性もある」ってのはテストに出るから要チェック!!w


その後調べて「とりあえずびまん性だろう」と一旦判定。

で、そこからPET検査*1を経て、入院したのが4月後半かな。GW直前だったので。

そのタイミングで「ステージ4」であることが判明して、当初予定よりも長めの入院が決定。


誤解されると危ないんで特記。

「ステージ4」ってのは概ね「あちこち転移している」程度の意味合いみたい。

他のガンだと結構生存率が下がるっぽいんだけど、悪性リンパ腫の場合は「進むの早いから転移も早いけど抗がん剤が効きやすいからい治療もしやすい」ようなので、そんなにびっくりしないで頂きたいのところw

まぁ「五年生存率」って単語が出てくるレベルではあるので「十全に安泰」とまでは言わないけど、生存率は高めなので、それほど心配する事でもなし。


まぁそんなこんなで治療がstartいたしました。

[]治療方法(現在進行中)

とりあえず入院直後は「多分びまん性なんだけど、ちょっと検査結果が怪しいので追加検査中。追加検査には時間がかかるから、とりあえずびまん性前提で抗がん剤開始」との事。

コースが大体

抗がん剤ぶちこんで

・治療効果が出ると共に白血球(好中球)が下がって免疫が一旦ダダ落ちして(これが投与から7〜10日後くらい)

・体の回復をまって(1週間)

って感じで、所要時間3週間が目安。

これを「数コース(6〜8)」ってのが、抗がん剤治療の定番みたい。


で、ポイントとしては

・体力が落ちるとあんまり強い抗がん剤が使えなくなる(から体力落とすな)

・体の回復が遅い(=体力が落ちてる)と1コースを4週間とかにする必要もある(から体力を落とすな)

ってあたり。

つまり「体力を落とすな」が、とりあえずの至上命題


おいちゃんは、1コース目はCHOP療法。2コース目はR-CHOP療法*2

コース目でRの薬剤をすっ飛ばしたのは「腫瘍崩壊症候群」を懸念して、らしい。

腫瘍崩壊症候群ってのは、もっそかんたんに書くと「薬が効きすぎてがん細胞が一気に大量に死ぬと少し困るよ!!」ってモン。

具体的には

・腸とかにがん細胞があって一気に壊れすぎると最悪穴があく(レアだけど、なるとめっさ怖いらしい)

・「壊れたがん細胞の破片」が血中にあふれるんだけど、それが排出しきれないと体が色々とへこたれる(特に腎臓)

ってあたり。

まぁ「腸に穴」は、それがあるので「Rを1コース目抜いた」になって。

排出については、1コース目は「大量の生理食塩水の点滴」+「尿量の管理」+「利尿剤」。いや点滴の利尿剤すげぇすげぇw


コース目は尿量のチェックはなく(大量の生理食塩水点滴はあったw)、少し楽でした。


ちなみに[R-]CHOPの場合、点滴自体は2日で終わります。

…R-CHOP療法のままなら「通院でいけた」んだけどねぇ(苦笑


コース目が落ち着いたころ出てきた検査結果が「バーキット拠り」の判定。

「ガチバーキット」ではないのが救いではあったんだけど、その時点で医師からの提案が2つ。

・弱いけどR-CHOP療法で通院

・強い療法だけど、3週間ごとに1週間入院


ってのが。

びまん性は「月単位で病気が進行する「中悪性度」」なんだけど、一方でバーキットは「週単位で病気が進行する「高悪性度」」に分類されるので。

先生に曰く「R-CHOP療法だと、ちょっと叩く速度が不安(叩くよりも増殖する速度のほうがあがる可能性がある」ってお話しでした。


色々悩んだのですが、お仕事的にギリ許容範囲だったので、後者を選びました。

…まぁ「ガチバーキット」なら「長期入院」って言われてたので、それよりはマシだわw


という訳で選んだのがEPOCH-R療法。EPOCH療法+R(リツキシマブ)だね。エポックと呼ばれると「エポック秒?」って考えるのはエンジニアとして基本的な習性w

CHOPと違うのが、CHOPは「高濃度の薬剤に短時間曝露」なんだけど、EPOCHは「低濃度で長時間曝露」。

具体的には「24時間点滴を4日間連続でEPOCH入れて、その翌日にRを入れる」って感じ。

「1時間あたり、20mlとか10mlとか」っていう超スローペースな点滴なので、輸液ポンプって機械使いますw


んで…「薬って毒だし毒って薬」…なんだけど、抗がん剤、皮膚から漏れると皮膚が腐るらしくw、それもあって、CVライン(中心静脈静脈路確保)っていう、おいちゃんは「腕から40〜45cmくらいの血管カテーテルぶち込まれて体の中心まで管を通す」ような手術して、そこから抗がん剤点滴*3

プラスして「バーキット入ってるから脳髄にがん細胞が行きやすい」らしく、そこの予防込みでルンバール(脳脊髄液採取)+脳髄液に薬液ぶち込みw


そんなこんなで、結構高度な医療を駆使していただいて、現在生存しておりますw

ちなみにコレ書いてるのは、EPOCH-R療法2コース目、トータルだと4コース目が終わる直前の病院w


…で気づいたんだけど。

EPOCH-R療法の話って、どうも割と最近(2013年くらい)にわき出たっぽいんだよねぇ…ふむぅ。興味深い。

医療も進歩してるんだなぁ当たり前だけど。こーゆーちゃんとした治療が「受けられるアンテナ張ってるよい病院」か「受けられない旧態依然とした病院」かって、結構でかい気がする……

[]食事とか日常生活とか

さてここからが本題でございますw

食事周りは色々と困ったり悩んだりも多いんだろうなぁ、と思われるので、少し書いておきたかったので。


先に。

ググりますと…例えば「食事療法 悪性リンパ腫」とか「食事療法 抗がん剤」とか「食事療法 癌」とか。

なんていうか「極端なの」が多いんですよねぇ(苦笑

個人的見解ではありますが「食事療法だけで治す!!」とか、多分色々苦行ですし失笑ですし、まずエビデンスベースで言えや、と。

とある医師さんに曰く「ネット情報は話半分」とか言われたのですが…半分も聞けないなぁ正直(苦笑


もちろん「よりよい食事がある可能性」は否定をしないのですが。

医師とも色々話をしていたのですが、結局の所一番でかい目標は

・食えるもん食ってまず体力を落とさない!

至上命題で、その上で「選択肢があるんなら、まぁ色々とオプションを重ねていく」くらいがよろしいのではないかなぁ、という。

内容的にも大変に穏当な所にしか話は落ちないわけでございます。


ちなみによく見る限りのいわゆる「食事療法()」で怖いのが…


玄米食え:内臓弱ってると消化できねぇよ!!

肉食うな:蛋白質どっから補給すんねん!!

脂質とりすぎるな:カロリー補給ルート潰すな!

砂糖とかのとるな:カロリー補給ルート潰すな!

塩分最低限しかとるな:味覚によっては「塩分控えめ」はきついしねぇ。腎臓その他、他の病気との兼ね合いしだいかねぇ

生野菜とか:好中球の値によってはNGよ? あと「生野菜のほうがいい」ってのは結構幻想だよ?

コスト:そもそも治療費にがっつり金かかってるのに「金がかかる食事療法無自覚に勧めるとか、どうなん?


などなど。

ちなみに「がん細胞の主な栄養分はブドウ糖」って話はあちこちであり、そこから「糖質NG」って話もあるようですが。

医師の見解だと「大差ないよ」で一蹴(笑

まぁ「体力落としますか?」との天秤なんだろうなぁ、って思う。

ちなみにその医師に曰く「笑う、ってのは、意外と馬鹿に出来ないよ」と言っていたので、臨床経験的に色々あるんだろうなぁ、と改めてしみじみ。


糅てて加えて。

多分「抗がん剤治療一般」に言えるっぽいのですが…便秘します。っつかおいちゃんの体感的には「内蔵機能が落ちます」。止まる、っておいちゃんは表現している。

なので、当然ですが消化能力も落ちると思われます。…吐き気するらしいのですが、それはおいちゃん全くナッシングwww

その辺を考えるとなおのこと「まず食えるものを食う!」が第一にあるし、「内臓的にキツイものを食っても」多分、消化できなくて、下すか吐くかの二択にしかなりません。


上述の(あやしい)記述群と医師との会話とおいちゃんのベース知識とおいちゃんの状態から、大体、以下の指針をベースにしています。

なお、上に行くほど上位規則ですw


・「食える時に食えるものを食う」!!(ただし、食い過ぎると腹痛起こす事もあるから、食欲バリバリなら程々に)

・可能なら、糖質よりは脂質を取る

・可能なら、どっちかというと糖質は「GI値が低めのもの」を取るようにする

・胃にくるんなら、素直に単糖をとる。我慢しない

蛋白質も「可能な範囲でしっかり」取る。胃がしんどい時とか、お豆腐お勧め!! あとプロテインドリンク!!

・他が無理なら、カロリーメイトドリンクタイプとかゼリータイプとか、とにかくまず「カロリー摂取」する!!

ビタミンは、多めに取るように意識する:錠剤ありありで

乳酸菌製剤も、定期的に取る(か、ヨーグルトとか納豆とかを定期的にいただくか、その併用)

・「調味料に金かける」のは割とお勧め。質の良い「油(うちはオリーブオイルとごま油)」「醤油」「味噌」「お酢」「砂糖」「塩」を揃えると、かなりイケます

・お出しも地味にお勧め。変なモン入ってない、良質なものを選びましょう。うちは築地寿屋商店さんの粉末だし(3種まぜまぜ)がお好み

・以下、無農薬とか減農薬とか、余裕があるならお好きに


こんな所?

この辺をベースに、後悔と懺悔を兼ねて、今までの経過をw


コース

ずっと入院中だったので、さほど問題もなく。

一度便秘になったのですが…センナ系の便秘薬いただいて「腹痛でのたうち回った」のもいい思い出ですが二度とごめんだw

その後、理系の面白い医師先生から(いい意味で面白い医師が多い病院だ)「大建中湯」ってのをおしえてもらって処方してもらって、そこから割と順調。

大建中湯はマジでよいげなので、記憶の片隅に入れておくといいかもしれません!!


とはいえ「胃が止まって」しんどい時も数度あり。具体的には「胃に食い物が残りっぱなしで全然下に流れていかない」感じ。

面倒なんで、8時間くらい経過したところで「とっとと吐いた」ら楽になりました。

…が、吐き癖が付くらしいので、微妙に最終手段…とはいえ、以降数回、お世話になりました(苦笑


吐いた後は「水とか飲んでる」と胃が落ち着くので、そこからゆっくり復食すると、回復しましたおいちゃんの場合。


コース

CHOP2回目なので(正確にはR-CHOP)、入れたら退院しました。

……えぇ出来心だったんです。

金曜日退院。土曜日、一人だったのでつい、つい、つい「インスタントラーメン(キャベツ入り)」を食ったんです。

そこから怒濤の水下痢(苦笑

5日くらい続いたかなぁ…体重落ちた落ちた(苦笑

そのときはとにかく

・冷たいものを飲まない

・水分を沢山とる(ポカリ大活躍)

って感じだったかな。固形物は胃が受け付けませんでした。

途中から「煎り玄米粥(柔らかめ)」を、ごく少量食べて、ギリギリ生きながらえました(苦笑


お粥の量を段々増やしていって、そこから普通のお米*4にゆっくりと戻していきました。

おかずは…油物がアウトだったんで、油使わないおかずを作ってもらってました。


ちなみに後半で「行けるかなぁ」と思ってモスバーガーをお昼に頂いたのですが…美味しかったのですが、胃がうけつけず、後で吐きました(苦笑

もうちょっと胃が元気になってから再チャレンジですなぁ。


コース

ここからEPOCH-R療法開始。

個体差がありまくる世界のようなので「おいちゃんの場合」なのですが

・内臓の停滞はR-CHOPより緩やかな感じ

口内炎が出来た。かつ、ヒドイ orz

って感じでした。

なので、退院した直後に口内炎が出来てからの5日くらいは「スープ生活」。固形物一切アウトだったので(具なし茶碗蒸しプリンの堅さがアウト、なレベル(苦笑) )。


具体的には…

・お湯で溶いて飲めるコーンスープ

がまずあって。これをがぶ飲み。

そこから

・お湯で溶いて飲めるコーンスープ+とろけるチーズ

でカロリー増加(笑

(次回対策に「こしあん」を作って冷凍しておきましたw。デザート用でございます)


お次は

・シチューの元+ジャガイモでシチュー:をマジックジューサーで液化 + とろけるチーズ(+バター入れたり)

経由で

ビーフシチューの元+ジャガイモ+冷凍ブロッコリーでシチュー:をマジックジューサーで液化 + とろけるチーズ


こんな感じでカロリー摂取してました。

塩分は染みなかったんだよねぇありがたい事に。

医師からは「スープっていうからコンソメとか連想してたのに」と笑われましたw

コンソメじゃカロリー足りないんだもんwww


コース目の後半は結構胃が回復していたので。後半一週間は結構普通に食ってましたねぇ。

最後のほう「スープカレー」とか食えたしw


コース

現在絶賛治療中w

これからリツキサン

胃の調子は…波がある感じ。食えたり、食えなかったり。


段々ペースが見えてきて

・食えない時は無理しない。無理すると後に響いて返って収支がマイナスになりやすい

・食えるときは食う。ただステロイドの影響で食欲増大ってのもあるので、「普通の量」+αくらい食っても空腹なら、一旦休憩。大体その後「腹痛」がくるw

こんな感じ。

病院食なので油分は控えめなので、油分に関するチェックはまだ。ただ…もらったおかし程度の油分だと「1〜2コース目ん時はアウトだったのが今は平気」なので、まぁ病変が治ってきてるってのもあるんだろうけど、あんまり心配しないでもいい感じ。

間食でチーズとか食ってるしなぁw


中間まとめ

いずれにしても。

改めて実感するのですが、抗がん剤治療は間違いなく「体力勝負」なところがあります。

素人見解ですが…まず「癌を退治する薬」があって、でもこれは間違いなく毒を含みますっていうか薬って毒だし。当然副作用もあります。

ただこれって「8を捨てて10を取る(数字は適当)」な世界だと思うので。まぁそこで「8を捨てるのが無理」って人が治療を拒否する自由はあると思う多分死期を早めるんだろうけどンなものはしらにゃい。

おいちゃんは「8を捨ててでも、治療の可能性という10を取りたい」って選択をしただけ。


で…そこに加えて、ある程度まで「捨てた8を別の薬剤で6まで拾う」とかやる結果として、結構大量のお薬を使う事になります(笑)。吐き気止めとか、一時的に痛風用の薬とか(フェブリク。尿酸を排出するお薬)。

ただ、そこでもやっぱり「ガード仕切れないでこぼこ」はあって、その最後のでこぼこは「体力でどうにかする」という、力技w

そのために、体力が不可欠になると思うのです。


「食う」ことは、本当に、心の底から大事だと思うので。

それが苦痛にならず、出来れば少しはよいものになればよいかなぁ、と思うので。

おいちゃんの個人的知見を、残しておきます。


どっかで誰かの役に立つとよいなぁ、と願って。

*1放射能物質飲むの!! 放射線じゃないよ! 放射能物質だよ!!w

*2:細かく話をすると、別件でおいちゃん少し心臓に疾患があるので、心毒性の問題で薬剤を変える話も出てたんだけど、脇道なので一旦オミット

*3:ちなみにCVライン。いま伺っている病院さん、なんかすげぇ「ガチで色々とやっている(CVラインセンターとか)」らしく、改めて「いい病院に来たなぁ」と思ったもんです。 http://hospinfo.tokyo-med.ac.jp/shinryo/cvline/

*4:ってもうちは玄米だけど、「芳醇炊き」ってので柔らかく炊けるので割と無問題 http://d.hatena.ne.jp/gallu/20150510/p1

2016-06-23

[]牛のしぐれ煮

レシピは割とあちこち色々あるんだけど。

「笠原 将弘さん」ってあたりに信頼性があるのと(笑)、実際作って美味だったので。

散逸すると多分、死ぬるほど困るので、転記&微妙に改変。


元ネタ

http://www.kyounoryouri.jp/recipe/11621_%E7%89%9B%E8%82%89%E3%81%AE%E3%81%97%E3%81%90%E3%82%8C%E7%85%AE.html


材料

・牛:200g

・しょうが:20gってあるけど多分もっといっぱいw

・粒山椒(あれば)

*水:大さじ4

*酒:大さじ4

*しょうゆ:大さじ2弱(原本だと 大さじ1+2/3)

*みりん:大さじ2弱(原本だと 大さじ1+2/3)

*砂糖:20g


作り方

・しょうがは針ショウガにしておく

(・牛肉をさっと熱湯にくぐらせる:やったりやらなかったり)

・煮汁+針ショウガ+粒参照を軽く煮詰める

・牛肉いれて煮からめる


おかゆさん生活の時に、大変に便利でございます。

2016-06-20

[]メールアドレスの確認(前置き)

発端は「拡張メールアドレス」周りのお話し。

直接的には…まず「拡張メールアドレス弾くところあるよねぇやだよねぇ」から始まって、それに対して

・新規流入することがお金に直結するようなサイトではNG

・具体的には「新規ユーザー紹介キャンペーン」とか打つ可能性があるところ

・その対策しないとTorelloみたいに無限に自分自身を紹介して、有料会員と同じだけのサービスを受けられてしまう。

というお話しがあって。


上述の理屈としては理解できる所と理解しにくいところがあって。

理解出来るのは「馬鹿が無限に自分自身を紹介して、ってやられるのを防ぎたい」って動機。これには一編の曇りもなく、是。

ただ一方で拡張アドレスは「どこからこのメアド漏らしたねん馬鹿野郎」という意味を込めた「マーキング」としての使い方もあって、それが出来ないのはそれはそれで怖い。


ちょっと整理すると。

拡張アドレスを使うケースはおそらくいくつかあり得て

フィルタを簡単にしたい

・「個人情報流出」用ユーザ側対策

・キャンペーン等の悪用

ってあたりが、ざっくりと思いつくあたりかなぁ、と。


下段は潰したいところなんだけど、上段は微妙で(SubjectでフィルタすりゃいいじゃんとかFromでフィルタすりゃいいじゃんとか:でもいくつかのメアドで「拡張アドレスによるフィルタリング」を説明してたりするから、微妙)。

一方で中段は割と切実なので、あんまりガードされたくないの感じ(個人情報のお漏らし多いしねぇ………)。


糅てて加えて。

+付きの拡張アドレスのinvalidが「それなりに考察して練られた結果」であればまだ多少、ごくわずかに、飲み込めなくもないんだけど(濁した理由は下記参照:端的には「効果とコストのバランス」がどんなもん? ってあたり)。

可能性として「キーワード"PHP email 正規表現"でググるとよく出てくる」、いわゆる「PHP使いはもう正規表現blogに書くな」問題がありまして。

端的には「熟考の結果なの? 未熟の結果なの?」ってのが、色々と判別しにくかったりかったり。

熟考した結果なら………微妙なんだけど(完全に是とはあんまり思わないけど、完全に非って訳でもないと思うので)。

未熟の結果だとすると、色々とサービスを使う事にためらいがあったりあったりあったり。


で…上述を(大体)満たすには

・拡張アドレスは認める

・重複したアドレスは認めない

ってのが理想かなぁ。

ただまぁ、上述を言っていた子がもう一つ言っている

正しい事じゃなく儲かる事をするのが会社だからねぇ。 儲かるための正しい事なら躊躇なくやるのですよ?

という話も、わからんではない。

……がそこでちょっと疑問もある。

理解できるのは「"正しい"なんて曖昧な定義で無限にコストをかけて良い会社とかあり得ない」ってのがあって、これはYes。

ただ一方で「そのためには鐚一文すら出せないのか?」という辺りには色々と疑問もあって。

上述を言っていた子(がいる現場)は違う、ってのを前提においてなのだけど、人によっては「正しい事じゃなく儲かる事をするのが会社」のニュアンスから、安全とか(ある程度までの)法準拠とかプライバシとか、色々なものが欠落している所を見ていたりするのをそれなりに散見していると、とりあえず「コスト計算してみたいねぇ」って所が出てくる。

ってか「学習含めて一切のコストも支払わずに"儲かるのが前提で、正しさは求めない"」とか言われると、やっぱり「ちょっと怖い会社さんのサービスだよねぇ」って見解に、どうしてもなりがち。


で、端的には「じゃぁ"重複アドレスを出来るだけ除去しつつ"、"拡張アドレスは原則として認める"」の実装をやってみればいいんじゃなかろうか?

ってのが、この記事の端的な趣旨ざます。

で、あわせて「ググった正規表現、どれくらいOKでどれくらい駄目?」ってのを調べてみたのを付記いたします(こっちの方が時間コストかかってるw)。


ついでに、「email 拡張アドレス」でググると上の方に出てくる、おいちゃんの見解に近いBlogがあったので、合わせて。

http://n.h7a.org/blog/entry/1509

[]メールアドレスの確認(本当の本題:拡張アドレスの除去方法)

とりあえず「拡張アドレスの拡張部分を除去」が命題になるのですが…もうちょっと正しく書くとおそらくは違って、さらっとぶっちゃけると「DBでprimary keyだったりUNIQUE制約かかってたりするのにちゃんと引っかかる」状況を作り出したい、ってのが、おそらく本筋。

で…ここから少し面倒なお話し。


とりあえず標的に上がってるのは「+(プラス)」記号で、これが割と拡張アドレスとしてつかわれ「やすい」。

ただ、拡張アドレスはqmailだと「−(ハイフン)」だし、Postfixだと「設定によっては+も−も使える(Postfix 2.11以降)」。一方で「普通にlocal-partの文字として−(ハイフン)が使える」所もあるので、色々と悩ましい。

それ以外にも「quoted string(ダブルクォートで囲った文字列)をlocal-partにする」ってやり方もあってこれも割とあちこちで使えるし(斜め調査の限りでは、AUスマホの「quoted stringアドレスへの送信」のみNGで、後は送受信とも割といける)。

あと、これはgmail以外では見ていないんだけど「Gmail ではユーザー名のピリオドを文字として認識しません( https://support.google.com/mail/answer/10313?hl=ja )」という謎仕様もあるので、ドット位置変えると割と色々いける(ドットの連続はどうもGoogle側で(も?)弾いてるぽい。「Final-Recipient: rfc822; "h..oge"@gmail.com」ってエラーがきた)。


この辺を考えると、細かくいくと「ドメイン毎に処理を変える」必要が出てきそうではあるんだけど。

大まかな「local-partからいらんものを引っぺがす!」ってのを前提に

・前後のダブルクォートは、あったら引っぺがす

・domain-partの文字は(小文字側に)揃える。local-partは「区別したりしなかったり」だと思うので、一旦「小文字に揃える」をデフォに。「小文字に揃えない(大文字と小文字を区別するMTA(RFC822準拠、RFC5321非準拠?)」の可能性が0ではないので、ギミックだけ作って運用対処としておく。

・+以降は「拡張アドレスの可能性が高い」ので、+以降を引っぺがす(後で「引っぺがさないドメインリスト」とか作って運用で足し込む)

・−以降は「拡張アドレスの可能性がある」ので、−以降を引っぺがす準備をする(「引っぺがすドメインリスト」とか作って運用で足し込む。初期は空でいいのかも)

Googleアカウントについては「ドットを消す」:いるかなぁ? とも思うんだけど、可能なのでせっかくだしやっとく。これも「引っぺがすリスト」作って、後で追加対応にしておくといいのかなぁ?


こんな感じ。

ちなみに前提として「アドレスは到達する」事を期待しているので、ドメイン周りは未着手。

ドメイン周りに手を出したい場合、最終的には「DNS引くよねぇ」って話になるので、それはまた「やるならやって」的なところw


文章書きながらで実装3時間ちょい…うち1時間弱が「調査+書き物」、1時間が「コーディング」、1時間がテスト。

テストの大半は「テストデータの用意ミス」という切ないっぷり(苦笑

とりあえず「さほどコストかからずに書けるよ」って程度なので、一旦「プロトタイプ的なブツだけどこんなもんぢゃね?」程度のものを。


っつわけで、コード(+下の方に簡単なテスト付き)

<?php

// 拡張アドレスっぽいもの除去クラス
class extended_address_removal {

// 本当は「外部ファイルから読み込み」とかで適切に読み込んでくだせぇ(苦笑
public function __construct() {
  //
  $this->not_lower_list([
    'not-lower-example.com',
  ]);
  //
  $this->plus_unremove_list([
    'plus-unremove-example.com',
    'hyphen-only-remove-example.com', // XXX ここ「二重登録」だなぁ…直感的じゃない…後で変えたほうがいいかも:これは「ハイフンのみ除去対象、+は生かす」設定用
  ]);
  //
  $this->hyphen_remove_list([
    'hyphen-only-remove-example.com', // XXX ここ「二重登録」だなぁ…直感的じゃない…後で変えたほうがいいかも:これは「ハイフンのみ除去対象、+は生かす」設定用
    'hyphen-remove-example.com', // こっちだと「ハイフンもプラスも除去する」になる
  ]);
  //
  $this->dot_remove_list([
    'gmail.example.com',
  ]);
}

// 設定する系メソッド
// 検索しやすいようにarray_flipしてるのでほんのり注意
public function not_lower_list($awk) {
  $this->not_lower_list_ = array_flip($awk);
}
public function plus_unremove_list($awk) {
  $this->plus_unremove_list_ = array_flip($awk);
}
public function hyphen_remove_list($awk) {
  $this->hyphen_remove_list_ = array_flip($awk);
}
public function dot_remove_list($awk) {
  $this->dot_remove_list_ = array_flip($awk);
}

// 除去本体
public function remove($email) {
  // local-partとdomain-partに切り分ける
  $awk = explode('@', $email);
  // 万が一「@無しの文字列」が入ってきた時用
  // XXX このチェックいるか?
  if (1 === count($awk)) {
    return '';
  }

  //
  $domain = array_pop($awk);
  $local = implode('@', $awk);
//var_dump($local);
//var_dump($domain);

  // domain-partの文字は(小文字側に)揃える。local-partは「区別したりしなかったり」だと思うので、一旦「小文字に揃える」をデフォに。「小文字に揃えない(大文字と小文字を区別するMTA(RFC822準拠、RFC5321非準拠?)」の可能性が0ではないので、ギミックだけ作って運用対処としておく。
  if (false === isset($this->not_lower_list_[$domain])) {
    $local = strtolower($local);
  }

  // 前後のダブルクォートは、あったら引っぺがす
  // XXX 不完全なquoted stringは一旦弾こう
  $flg = ('"' === $local[0]); // 先頭の"チェック
  $flg2 = ('"' === $local[strlen($local) - 1]); // 最後の"チェック
  // 「どっちかだけtrue」は不完全なquoted stringなのでエラー
  if ($flg xor $flg2) {
    return '';
  }
  // quoted stringなら除去処理
  if (true === $flg) {
    $local = substr($local, 1, -1);
  }
//var_dump($local);
//var_dump($domain);

  // ・+以降は「拡張アドレスの可能性が高い」ので、+以降を引っぺがす(後で「引っぺがさないドメインリスト」とか作って運用で足し込む)
  if (false === isset($this->plus_unremove_list_[$domain])) {
    $awk = explode('+', $local);
    $local = $awk[0];
  }

  // ・−以降は「拡張アドレスの可能性がある」ので、−以降を引っぺがす準備をする(「引っぺがすドメインリスト」とか作って運用で足し込む。初期は空でいいのかも)
  if (true === isset($this->hyphen_remove_list_[$domain])) {
    $awk = explode('-', $local);
    $local = $awk[0];
  }

  // ・Googleアカウントについては「ドットを消す」:いるかなぁ? とも思うんだけど、可能なのでせっかくだしやっとく。これも「引っぺがすリスト」作って、後で追加対応にしておくといいのかなぁ?
  if (true === isset($this->dot_remove_list_[$domain])) {
    $local = str_replace('.', '', $local);
  }

  //
  return $local . '@' . $domain;
}

//private:
private $not_lower_list_;
private $plus_unremove_list_;
private $hyphen_remove_list_;
private $dot_remove_list_;
} // end of class

/*
 * 以下テスト
 */

// テスト用アドレス群
$add = [
  // ノーマルなパターン
  'nobody@example.com' => 'nobody@example.com',
  'NObody@example.com' => 'nobody@example.com', // local-partは小文字に揃える
  'no+body@example.com' => 'no@example.com', // 拡張なので除去
  'no-body@example.com' => 'no-body@example.com', // ハイフンはデフォでは除去しない
  'no.body@example.com' => 'no.body@example.com', // .(dot)はデフォでは除去しない
  '"no+body"@example.com' => 'no@example.com', // (quoted string)拡張なので除去
  '"no-body"@example.com' => 'no-body@example.com', // (quoted string)ハイフンはデフォでは除去しない
  '"no.body"@example.com' => 'no.body@example.com', // (quoted string) .(dot)はデフォでは除去しない
  '"no.body@example.com' => '', // 不完全なquoted string
  'no.body"@example.com' => '', // 不完全なquoted string
  '"nob@ody"@example.com' => 'nob@ody@example.com', // quoted stringの除去

  // local-partを小文字にしない
  'NObody@not-lower-example.com' => 'NObody@not-lower-example.com',

  // プラスを除去しない
  'no+body@plus-unremove-example.com' => 'no+body@plus-unremove-example.com',
  '"no+body"@plus-unremove-example.com' => 'no+body@plus-unremove-example.com',

  // ハイフンを取り除き、プラスは除去しない
  'no-body@hyphen-only-remove-example.com' => 'no@hyphen-only-remove-example.com',
  '"no-body"@hyphen-only-remove-example.com' => 'no@hyphen-only-remove-example.com',
  'n+o-body@hyphen-only-remove-example.com' => 'n+o@hyphen-only-remove-example.com',
  '"n+o-body"@hyphen-only-remove-example.com' => 'n+o@hyphen-only-remove-example.com',

  // ハイフンもプラスも取り除く
  'no-body@hyphen-remove-example.com' => 'no@hyphen-remove-example.com',
  '"no-body"@hyphen-remove-example.com' => 'no@hyphen-remove-example.com',
  'n+o-body@hyphen-remove-example.com' => 'n@hyphen-remove-example.com',
  '"n+o-body"@hyphen-remove-example.com' => 'n@hyphen-remove-example.com',

  // dotを取り除く
  'no.b.ody@gmail.example.com' => 'nobody@gmail.example.com',
  'no.b.ody+test@gmail.example.com' => 'nobody@gmail.example.com',
  '"no.b.ody"@gmail.example.com' => 'nobody@gmail.example.com',
  '"no.b.ody+test"@gmail.example.com' => 'nobody@gmail.example.com',
];

//
$error_count = 0;
$obj = new extended_address_removal();
foreach($add as $email => $expectation_email) {
  $r = $obj->remove($email);
  if ($r === $expectation_email) {
    //echo "ok\n";
    echo "ok: {$email} => {$r}\n";
  } else {
    echo "ng orz\n";
    //echo "{$email} => {$r} !: {$expectation_email}\n";
    $error_count ++;
  }
}
if (0 === $error_count) {
  echo "...................ALL OK!!\n";
} else {
  echo "................NG( {$error_count} )!!\n";
}

とりあえずこのクラス使うと「なんとなし拡張っぽいものを一通り引っぺがせる」ので。

実装するemailアドレスは別カラムにちゃんと保存しておいてもらうとして、こいつで引っ張ってきたアドレスをUNIQUE制約したりしておくと、当初目的である

・拡張アドレスは認める

・重複したアドレスは認めない

が、ある程度いけるんではないかなぁ、と。

あとは運用でドメインを追加したりすれば、まぁまぁいけるんじゃないかなぁ、って思うざます。


…多分、そのうちMagicWeaponに追加するような気がするw

[]メールアドレスの確認(少し本題:あちこちググってのメアド正規表現の是非)

あちこちググって、正規表現を色々と無造作にひっかけてきてみました。

で、大まかな検証結果と、前提になる「mailの規約関連」。


http://saboten009.blogspot.jp/2008/02/php_25.html

1. メールアドレスは local part と domain が @ で区切られて構成されている。(RFC2822)

2. local partは、アルファベット、数字、以下の記号 ! # $ % & ' * + - / = ? ^ _ ` { | } ~ で、ドット「 . 」で区切られている場合がある。ただし、最初、最後、他のドットの隣にあってはいけない。(つまり連続しないことということか) (RFC2822 3.2.4)

3. local partは、 クオーテーションマーク「 " 」のなかにスペースを含むいろんな文字が入った quoted string を含むことがある (RFC2822 3.2.5)

4. \@ などの "quoted pair" をlocal partに含むものも「正しい」が "obsolete" (RFC822、RFC2822 4.4) (RFC2822はRFC822をsupersedeしてobsoleteにした、とある)

5. local partの最大長は 64文字 (RFC2821 4.5.3.1)

6. domainは、labelがドットで区切られたものである (RFC1035 2.3.1)

7. domainは、アルファベットで始まり、その後に、0文字以上のアルファベット、数字、ハイフン「 - 」が続き、最後の一字はアルファベットか数字。(RFC1035 2.3.1)

8. labelの最大長は 63字 (RFC1035 2.3.1)

9. domainの最大長は 255字 (RFC2821 4.5.3.1)

10. domainは、fully qualifiedで、DNSのtype AもしくはMXレコードでresolvableでなければならない (RFC2821 3.6)

大まかに。

1番は「これを外してるのは見たことがない」ので、よいと思う。

2番が結構色々と悲喜交々で、「どの記号がいけるのか」の実装依存が強い感じ。

3番の対応は「必要だと思う」んだけど、あんまり見た記憶がないかも(苦笑

4番はそもそもobsoleteなので、あんまり気にしない。

5番はまぁ「必要ならチェックしてくれ」って感じだ。

6〜10までは正確には「ドメイン記法」だと思う…んだが、ここでも結構色々と、ある。


細かいコードは後述するとして。

大まかに…まず「メアドで各処理をチェック」してくと。


@がない

これを外す表現はさすがにないw


local-part先頭の.(ドット)はNG

割とあちこち外してる…「意図的」な可能性があるやつや、スイッチ次第で「是とも非とも」ってのもあるんだけど。

言っちゃなんだが、phpspotさんがOKなのは少しだけ意外w


local-part最後の.(ドット)はNG

「先頭dot」と微妙に結果が違うのがとても面白いw

連続した.(ドット)はNG

これはとても悩ましいところ(AUDocomoが以下略)。

なので、微妙に言及を避けますw


domain-part先頭の.(ドット)はNG

これコケる実装系があるのが不思議。

でも「コケる実装」書いてあるBlogがあるんだよ?


domain-partの最後はアルファベットまたは数字のみ

これコケる実装系があるのが不思議。ちなみにこれは「最後にアンダースコアいれた」ケース。

symfony_1さんがコケてるのが大変に意外。なんかあるのかなぁ?


domain-partの最後はアルファベットまたは数字のみ(その2)

これは「最後にdot」のケース。…ある意味正しいんだけどねぇwww

これもいくつかの実装系でコケてます。


domain-partのアンダースコアはNG

これは、厳密には少し議論が出そうなテストなんだけど。

細かい話はこちらを。

http://ya.maya.st/d/200605c.html#s20060529_2

ただまぁ「事故りそうな可能性も多々」なので、どっちかってぇと「弾く前提」のほうがいいような気がせんでもない。


nobody@example.com(普通のアドレス)

エラーが出る処理系があるこの不思議感w


0nobody@example.com (先頭を数字に)

これも謎エラーがいくつか発生。


!nobody@example.com (先頭を記号に)

エラーが増えるねぇ…


nobody@1st.example.com (ドメイン名の先頭を数字に(昔は駄目だった))

微妙な所だけど、いくつかがinvalidにしてきた。


Abc\@def@example.com (quoted pair(ただしこれはobsolete))

これは一通りエラー。まぁ順当な気もする。


!#$%&'*+-/=?^_`.{|}~@example.com (使用可能な記号の群れ)

pear、symfony_1、php_filterが優秀だなぁ。


"da.me.."@example.com (これは「quoted string」なのでvalidなパターンその1)

この辺もガチ勢の健闘が目立ちますw


"nobody@nobody"@example.com (これは「quoted string」なのでvalidなパターンその2)

大体一緒。symfony_2がNGなのが微妙に目立つかな。


nobody+regexp@gmail.com (いわゆる拡張アドレスその1)

気になるところ…いわゆる「unガチ勢」が割とこぞってNG出してきやがるのが大変に気にかかります。


nobody-regexp@gmail.com (いわゆる拡張アドレスその2)

一方でこっちは「NGがほとんど無い」あたりも、違う意味で気になります。

ideaさんが「どっちもNG」なので、ある意味あり。でもdocomoとかの携帯のアドレスって普通にハイフン入ってなかったっけ?


"nobody+regexp"@gmail.com (いわゆる拡張アドレスその3(拡張+quoted))

まぁ言わずもがなのガチ勢優勢。


"nobody-regexp"@gmail.com (いわゆる拡張アドレスその4(拡張+quoted))

いっしょ。


で、次に「処理系単位で」意図に反する動作をする子を見ていると…

unガチ勢は、割とinvalidを通す一方でvalidを通さない、っていうある意味わかりやすい状況が色々とつまびらかに。

ガチ勢は「invalidは大体はねる」「validが通らないことが稀に(でもまぁ歴史的状況的経緯もあるしなぁ…な子が通らないくらい)」。

php_filter意外が、quoted string系通さないのが、意外なのか順当なのか。


まぁかように「unガチ勢ほど拡張アドレスをNGにしやすい」ので。

もちろん「ちゃんと分かった上で丁寧に書いている」所もあろうかとは思うんだけど……割と定期的に「駄目な正規表現コードを拝見したり」、あまつさえ「プログラムなんてググってコピペすればいいんだよ」とかいう話を見たり聞いたりすると、色々ともにょったりするわけなのです。


blogからコピペしてもいいけど、コードの読み込みと検証はちゃんとやろうね!!

[]メールアドレスの確認(余談? 本題? ググったコードの実検証)

実コードです。

後で修正いれる可能性があるんで、気づいた事とかあったら突っ込んでくださいませ。

<?php

// http://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
function phpspot($text) {
  if (preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $text)) {
    return true;
  } else {
    return false;
  }
}
// http://www.ideaxidea.com/archives/2009/03/practical_php_regexs.html
function idea($email) {
  if (preg_match('/^[^0-9][a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)*[@][a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)*[.][a-zA-Z]{2,4}$/',$email)) {
    return true;
  } else {
    return false;
  }
}

//http://qiita.com/tabo_purify/items/aa279cd28abdeba0873c
function tabo_purify($mail) {
  if (preg_match('|^[0-9a-z_./?-]+@([0-9a-z-]+\.)+[0-9a-z-]+$|', $mail)) {
    return true;
  }else{
    return false;
  }
}
function doli($email) {
  if ( preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/i', $email) ){
    return true;
  } else {
    return false;
  }
}

// http://www.seesaa.hitsug.net/article/406595659.html
function seesaa($email){
  if(preg_match('/^[a-zA-Z0-9_.!~*\'();\/?:\&=+\$,%#-]+@[A-Za-z0-9_.-]+\.[A-Za-z0-9]+$/', $email)){
    return true;
  } else {
    return false;
  }
}

// http://d.hatena.ne.jp/yachin29/20130410/p1
function yachin29($email){
  if (preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $email)) {
    return true;
  } else {
    return false;
  }
}

// http://doremi.s206.xrea.com/php/tips/preg.html
function doremi_1($email) {
  if (preg_match('/^[\w\-\.]+@[\w\-\.]+$/D', $email)) {
    return true;
  }
  //else
  return false;
}
function doremi_2($email) {
  if (preg_match('/^\w+(?:[\.\-]\w+)*@\w+(?:-\w+)?(?:\.\w+(?:-\w+)?)+$/D', $email)) {
    return true;
  }
  //else
  return false;
}

// http://qiita.com/mpyw/items/257eabe0b43b1e02e6f7
function mpyw($email, $strict = true) {
    $dot_string = $strict ?
        '(?:[A-Za-z0-9!#$%&*+=?^_`{|}~\'\\/-]|(?<!\\.|\\A)\\.(?!\\.|@))' :
        '(?:[A-Za-z0-9!#$%&*+=?^_`{|}~\'\\/.-])'
    ;
    $quoted_string = '(?:\\\\\\\\|\\\\"|\\\\?[A-Za-z0-9!#$%&*+=?^_`{|}~()<>[\\]:;@,. \'\\/-])';
    $ipv4_part = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
    $ipv6_part = '(?:[A-fa-f0-9]{1,4})';
    $fqdn_part = '(?:[A-Za-z](?:[A-Za-z0-9-]{0,61}?[A-Za-z0-9])?)';
    $ipv4 = "(?:(?:{$ipv4_part}\\.){3}{$ipv4_part})";
    $ipv6 = '(?:' .
        "(?:(?:{$ipv6_part}:){7}(?:{$ipv6_part}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){6}(?::{$ipv6_part}|:{$ipv4}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){5}(?:(?::{$ipv6_part}){1,2}|:{$ipv4}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){4}(?:(?::{$ipv6_part}){1,3}|(?::{$ipv6_part})?:{$ipv4}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){3}(?:(?::{$ipv6_part}){1,4}|(?::{$ipv6_part}){0,2}:{$ipv4}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){2}(?:(?::{$ipv6_part}){1,5}|(?::{$ipv6_part}){0,3}:{$ipv4}|:))" . '|' .
        "(?:(?:{$ipv6_part}:){1}(?:(?::{$ipv6_part}){1,6}|(?::{$ipv6_part}){0,4}:{$ipv4}|:))" . '|' .
        "(?::(?:(?::{$ipv6_part}){1,7}|(?::{$ipv6_part}){0,5}:{$ipv4}|:))" . 
    ')';
    $fqdn = "(?:(?:{$fqdn_part}\\.)+?{$fqdn_part})";
    $local = "({$dot_string}++|(\"){$quoted_string}++\")";
    $domain = "({$fqdn}|\\[{$ipv4}]|\\[{$ipv6}]|\\[{$fqdn}])";
    $pattern = "/\\A{$local}@{$domain}\\z/";
    return preg_match($pattern, $email, $matches) &&
        (
            !empty($matches[2]) && !isset($matches[1][66]) && !isset($matches[0][256]) ||
            !isset($matches[1][64]) && !isset($matches[0][254])
        )
    ;
}

// http://fdays.blogspot.jp/2007/10/rfc-2822-j0hn-d0e-10-pregmatch-9.html
function fdays_1($m) {
  //local part
  $atext = "[-!#-'*+/-9=?^-~]";
  $atom = $atext . '+';
  $dot_atom = $atom . '(//.' . $atom . ')*'; //2

  $qtext = '[]-~!#-[]';
  $text = '[ -~]';
  $quoted_pair = '////' . $text; //4
  $quoted_string = '"(' . $qtext . '|' . $quoted_pair . ')*"'; //3

  $local_part = '(' . $dot_atom . '|' . $quoted_string . ')';

  //domain
  $label = '[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?'; //7, 8
  $domain = $label . '(//.' . $label . ')*//.([a-z]{2,4}|museum)'; //6

  //全体
  $addr_spec = $local_part . '@' . $domain; //1

  $regexp = '/^' . str_replace('/', '\\/', $addr_spec) . '$/i';

  if (preg_match($regexp, $m)) {
    return true;
  }
  //else
  return false;
}
function fdays_2($value) {
  if (preg_match('/^[-+\\w]+(\\.[-+\\w]+)*@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]{2,6}$/i', $value)) {
    return true;
  }
  //else
  return false;
}
function fdays_3($value) {
  if (preg_match('/^[-+.\\w]+@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]{2,6}$/i', $value)) {
    return true;
  }
  //else
  return false;
}


// ---------------------------------------------

// http://catbot.net/blog/2007/06/re_php.html
// ソースは PEARから直接( http://download.pear.php.net/package/Mail-1.3.0.tgz )
function pear($data, $strict = false)
{
    $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
    if (preg_match($regex, trim($data), $matches)) {
        //return array($matches[1], $matches[2]);
        return true;
    } else {
        return false;
    }
}


// http://catbot.net/blog/2007/06/re_php.html


// 多分こういう関数だと思う
define('VALID_EMAIL', '/\\A(?:^([a-z0-9][a-z0-9_\\-\\.\\+]*)@([a-z0-9][a-z0-9\\.\\-]{0,63}\\.(com|org|net|biz|info|name|net|pro|aero|coop|museum|[a-z]{2,4}))$)\\z/i');
function cake($s) {
    if (preg_match(VALID_EMAIL, $s, $matches)) {
        //return array($matches[1], $matches[2]);
        return true;
    } else {
        return false;
    }

}
//
function Ethna($mailaddress)
{
    if (preg_match('/^([a-z0-9_]|\-|\.|\+)+@(([a-z0-9_]|\-)+\.)+[a-z]{2,6}$/i', $mailaddress)) {
        return true;
    }
    return false;
}
// XXX 第二引数は省略
// XXX classメソッドぽいので、$thisをcallするあたり省略
// ドメインチェックは(今回の趣旨から外れるし)省略
function symfony_1(&$value /*, &$error*/) {
  return _symfony($value, false);
}
function symfony_2(&$value /*, &$error*/) {
  return _symfony($value, true);
}
function _symfony(&$value, $strict)
{
  $error; // XXX
  //$strict = $this->getParameterHolder()->get('strict');
  if ($strict == true)
  {
    $re = '/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i';
  }
  else
  {
    /* Cal Henderson: http://iamcal.com/publish/articles/php/parsing_email/pdf/
     * The long regular expression below is made by the following code
     * fragment:
     *
     *   $qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
     *   $dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
     *   $atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c'
     *         . '\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
     *   $quoted_pair = '\\x5c\\x00-\\x7f';
     *   $domain_literal = "\\x5b($dtext|$quoted_pair)*\\x5d";
     *   $quoted_string = "\\x22($qtext|$quoted_pair)*\\x22";
     *   $domain_ref = $atom;
     *   $sub_domain = "($domain_ref|$domain_literal)";
     *   $word = "($atom|$quoted_string)";
     *   $domain = "$sub_domain(\\x2e$sub_domain)*";
     *   $local_part = "$word(\\x2e$word)*";
     *   $addr_spec = "$local_part\\x40$domain";
     */

    $re = '/^([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-'
         .'\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c\\x00-'
         .'\\x7f)*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-'
         .'\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80'
         .'-\\xff]|\\x5c\\x00-\\x7f)*\\x22))*\\x40([^\\x00-\\x20\\x22\\x28\\x29'
         .'\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^'
       .'\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c\\x00-\\x7f)*\\x5d)(\\x2e([^\\x00-'
         .'\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-'
         .'\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c\\x00-\\x7f)*'
         .'\\x5d))*$/'
    ;
  }

  if (!preg_match($re, $value))
  {
    //$error = $this->getParameterHolder()->get('email_error');
    return false;
  }

/*
  $checkDomain = $this->getParameterHolder()->get('check_domain');
  if ($checkDomain && function_exists('checkdnsrr'))
  {
    $tokens = explode('@', $value);
    if (!checkdnsrr($tokens[1], 'MX') && !checkdnsrr($tokens[1], 'A'))
    {
      $error = $this->getParameterHolder()->get('email_error');

      return false;
    }
  }
*/
  return true;
} 

// PHP
function php_filter($email) {
  $r = filter_var($email, FILTER_VALIDATE_EMAIL);
  if (false === $r) {
    return false;
  }
  // else
  return true;
}

/*
// 未テストだけど本来的にvalid
// 「お名前 <アドレス>」は、うち(MagicWeapon)には「引っぺがすメソッド」あるから一旦無視
<foo@example.com>
foo <foo@example.com>
// 「ドメインpartにIPアドレス指定」は今回の趣旨から外れるから無視
canada@[192.168.0.1]
*/

// 本来invalid
$invalid = [
  'dameexample.com' => '@がない',
  '.da.me@example.com' => 'local-part先頭の.(ドット)はNG',
  'da.me.@example.com' => 'local-part最後の.(ドット)はNG',
  'da..me@example.com' => '連続した.(ドット)はNG',
  'da.me@.example.com' => 'domain-part先頭の.(ドット)はNG',
  'da.me@example.com_' => 'domain-partの最後はアルファベットまたは数字のみ',
  'da.me@example.com.' => 'domain-partの最後はアルファベットまたは数字のみ(その2)',
  'nobody@add_example.com' => 'domain-partのアンダースコアはNG',
];

// 本来valid
$valid = [
  'nobody@example.com' => '普通のアドレス',
  '0nobody@example.com' => '先頭を数字に',
  '!nobody@example.com' => '先頭を記号に',
  'nobody@1st.example.com' => 'ドメイン名の先頭を数字に(昔は駄目だった)',
  'Abc\@def@example.com' => 'quoted pair(ただしこれはobsolete)',
  "!#$%&'*+-/=?^_`.{|}~@example.com" => '使用可能な記号の群れ',
  '"da.me.."@example.com' => 'これは「quoted string」なのでvalidなパターンその1',
  '"nobody@nobody"@example.com' => 'これは「quoted string」なのでvalidなパターンその2',
  'nobody+regexp@gmail.com' => 'いわゆる拡張アドレスその1',
  'nobody-regexp@gmail.com' => 'いわゆる拡張アドレスその2',
  '"nobody+regexp"@gmail.com' => 'いわゆる拡張アドレスその3(拡張+quoted)',
  '"nobody-regexp"@gmail.com' => 'いわゆる拡張アドレスその4(拡張+quoted)',
];

$funcs = [
  'phpspot',
  'idea',
  'tabo_purify',
  'doli',
  'seesaa',
  'yachin29',
  'doremi_1',
  'doremi_2',
  'mpyw',
  // 'fdays_1', valid系が全部エラーになるので、一旦未使用にする
  'fdays_2',
  'fdays_3',
  'pear',
  'cake',
  'Ethna',
  'symfony_1',
  'symfony_2',
  'php_filter',
];

// ぶん回し: メールアドレス単位での「各処理」の具合
foreach(['invalid' => false, 'valid' => true] as $data => $expected_ret) {
echo "{$data}\n";
  foreach($$data as $email => $comment) {
    echo "  {$email} ({$comment})\n";
    foreach ($funcs as $func) {
      printf("    call %-15s:", $func);
      $r = $func($email);
      if ($r === $expected_ret) {
        echo "... OK!!\n";
      } else {
        echo "... NG orz\n";
      }
    }
    echo "\n";
  }
  echo "\n\n";
}

// ぶん回し:各処理単位での、メアドの具合
// ついでにカウント
$count = [];
foreach ($funcs as $func) {
  $count[$func] = ['OK' => 0, 'NG' => 0];
  printf("call %-15s\n", $func);
  foreach(['invalid' => false, 'valid' => true] as $data => $expected_ret) {
    echo "  {$data}\n";
    foreach($$data as $email => $comment) {
      //printf("    %-30s", $email);
      $r = $func($email);
      if ($r === $expected_ret) {
        //echo "... OK!!\n";
        $count[$func]['OK'] ++;
      } else {
        printf("    %-30s... NG orz(%s)\n", $email, $comment);
        //echo "... NG orz({$comment})\n";
        $count[$func]['NG'] ++;
      }
    }
  }
  echo "\n";
}

//
foreach($count as $func => $types) {
  echo "{$func}\n";
  foreach($types as $type => $i) {
    printf("    %s => %d\n", $type, $i);
  }
}

結果


invalid

 dameexample.com (@がない)

  call phpspot    :... OK!!

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... OK!!

  call doremi_1    :... OK!!

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 .da.me@example.com (local-part先頭の.(ドット)はNG)

  call phpspot    :... OK!!

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... OK!!

  call seesaa     :... NG orz

  call yachin29    :... OK!!

  call doremi_1    :... NG orz

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... OK!!

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... NG orz

  call php_filter   :... OK!!

 da.me.@example.com (local-part最後の.(ドット)はNG)

  call phpspot    :... NG orz

  call idea      :... OK!!

  call tabo_purify  :... NG orz

  call doli      :... OK!!

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... NG orz

  call php_filter   :... OK!!

 da..me@example.com (連続した.(ドット)はNG)

  call phpspot    :... NG orz

  call idea      :... OK!!

  call tabo_purify  :... NG orz

  call doli      :... OK!!

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... NG orz

  call php_filter   :... OK!!

 da.me@.example.com (domain-part先頭の.(ドット)はNG)

  call phpspot    :... OK!!

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... NG orz

  call yachin29    :... OK!!

  call doremi_1    :... NG orz

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 da.me@example.com_ (domain-partの最後はアルファベットまたは数字のみ)

  call phpspot    :... NG orz

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... NG orz

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 da.me@example.com. (domain-partの最後はアルファベットまたは数字のみ(その2))

  call phpspot    :... NG orz

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 nobody@add_example.com (domain-partのアンダースコアはNG)

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... NG orz

  call symfony_1   :... NG orz

  call symfony_2   :... OK!!

  call php_filter   :... OK!!



valid

 nobody@example.com (普通のアドレス)

  call phpspot    :... OK!!

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... OK!!

  call doremi_1    :... OK!!

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 0nobody@example.com (先頭を数字に)

  call phpspot    :... OK!!

  call idea      :... NG orz

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... OK!!

  call doremi_1    :... OK!!

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 !nobody@example.com (先頭を記号に)

  call phpspot    :... NG orz

  call idea      :... OK!!

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... OK!!

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... OK!!

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 nobody@1st.example.com (ドメイン名の先頭を数字に(昔は駄目だった))

  call phpspot    :... OK!!

  call idea      :... OK!!

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... OK!!

  call doremi_1    :... OK!!

  call doremi_2    :... OK!!

  call mpyw      :... NG orz

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 Abc\@def@example.com (quoted pair(ただしこれはobsolete))

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... NG orz

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... NG orz

  call symfony_2   :... NG orz

  call php_filter   :... NG orz

 !#$%&'*+-/=?^_`.{|}~@example.com (使用可能な記号の群れ)

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... OK!!

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 "da.me.."@example.com (これは「quoted string」なのでvalidなパターンその1)

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 "nobody@nobody"@example.com (これは「quoted string」なのでvalidなパターンその2)

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... NG orz

  call php_filter   :... OK!!

 nobody+regexp@gmail.com (いわゆる拡張アドレスその1)

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 nobody-regexp@gmail.com (いわゆる拡張アドレスその2)

  call phpspot    :... OK!!

  call idea      :... NG orz

  call tabo_purify  :... OK!!

  call doli      :... OK!!

  call seesaa     :... OK!!

  call yachin29    :... OK!!

  call doremi_1    :... OK!!

  call doremi_2    :... OK!!

  call mpyw      :... OK!!

  call fdays_2    :... OK!!

  call fdays_3    :... OK!!

  call pear      :... OK!!

  call cake      :... OK!!

  call Ethna     :... OK!!

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 "nobody+regexp"@gmail.com (いわゆる拡張アドレスその3(拡張+quoted))

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!

 "nobody-regexp"@gmail.com (いわゆる拡張アドレスその4(拡張+quoted))

  call phpspot    :... NG orz

  call idea      :... NG orz

  call tabo_purify  :... NG orz

  call doli      :... NG orz

  call seesaa     :... NG orz

  call yachin29    :... NG orz

  call doremi_1    :... NG orz

  call doremi_2    :... NG orz

  call mpyw      :... OK!!

  call fdays_2    :... NG orz

  call fdays_3    :... NG orz

  call pear      :... NG orz

  call cake      :... NG orz

  call Ethna     :... NG orz

  call symfony_1   :... OK!!

  call symfony_2   :... OK!!

  call php_filter   :... OK!!



call phpspot    

 invalid

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

  da.me@example.com_      ... NG orz(domain-partの最後はアルファベットまたは数字のみ)

  da.me@example.com.      ... NG orz(domain-partの最後はアルファベットまたは数字のみ(その2))

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call idea     

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  0nobody@example.com      ... NG orz(先頭を数字に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  nobody-regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call tabo_purify  

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call doli     

 invalid

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call seesaa    

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

  da.me@.example.com      ... NG orz(domain-part先頭の.(ドット)はNG)

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call yachin29   

 invalid

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

  da.me@example.com_      ... NG orz(domain-partの最後はアルファベットまたは数字のみ)

  da.me@example.com.      ... NG orz(domain-partの最後はアルファベットまたは数字のみ(その2))

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call doremi_1   

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

  da.me@.example.com      ... NG orz(domain-part先頭の.(ドット)はNG)

  da.me@example.com_      ... NG orz(domain-partの最後はアルファベットまたは数字のみ)

  da.me@example.com.      ... NG orz(domain-partの最後はアルファベットまたは数字のみ(その2))

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call doremi_2   

 invalid

  da.me@example.com_      ... NG orz(domain-partの最後はアルファベットまたは数字のみ)

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  nobody+regexp@gmail.com    ... NG orz(いわゆる拡張アドレスその1)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call mpyw     

 invalid

 valid

  nobody@1st.example.com    ... NG orz(ドメイン名の先頭を数字に(昔は駄目だった))

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

call fdays_2    

 invalid

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call fdays_3    

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call pear     

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

 valid

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call cake     

 invalid

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call Ethna     

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  !nobody@example.com      ... NG orz(先頭を記号に)

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  !#$%&'*+-/=?^_`.{|}~@example.com... NG orz(使用可能な記号の群れ)

  "da.me.."@example.com     ... NG orz(これは「quoted string」なのでvalidなパターンその1)

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

  "nobody+regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその3(拡張+quoted))

  "nobody-regexp"@gmail.com   ... NG orz(いわゆる拡張アドレスその4(拡張+quoted))

call symfony_1   

 invalid

  da.me@example.com_      ... NG orz(domain-partの最後はアルファベットまたは数字のみ)

  nobody@add_example.com    ... NG orz(domain-partのアンダースコアはNG)

 valid

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

call symfony_2   

 invalid

  .da.me@example.com      ... NG orz(local-part先頭の.(ドット)はNG)

  da.me.@example.com      ... NG orz(local-part最後の.(ドット)はNG)

  da..me@example.com      ... NG orz(連続した.(ドット)はNG)

 valid

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

  "nobody@nobody"@example.com  ... NG orz(これは「quoted string」なのでvalidなパターンその2)

call php_filter  

 invalid

 valid

  Abc\@def@example.com     ... NG orz(quoted pair(ただしこれはobsolete))

phpspot

  OK => 7

  NG => 13

idea

  OK => 9

  NG => 11

tabo_purify

  OK => 9

  NG => 11

doli

  OK => 13

  NG => 7

seesaa

  OK => 9

  NG => 11

yachin29

  OK => 7

  NG => 13

doremi_1

  OK => 5

  NG => 15

doremi_2

  OK => 10

  NG => 10

mpyw

  OK => 18

  NG => 2

fdays_2

  OK => 13

  NG => 7

fdays_3

  OK => 10

  NG => 10

pear

  OK => 12

  NG => 8

cake

  OK => 11

  NG => 9

Ethna

  OK => 9

  NG => 11

symfony_1

  OK => 17

  NG => 3

symfony_2

  OK => 15

  NG => 5

php_filter

  OK => 19

  NG => 1

2016-06-19

[][]phpのsession関数を使ってのギミック

PHPセッションは、便利な反面色々と気になる事もあるのですが。

そのうち

・「セッション有効時間」をきっちりしたい(有効時間が切れたら問答無用で切断したい)

アカウントロックの実装をかけて、ロックしたら「ログイン出来ない」だけじゃなくて「現在ログイン中のアカウントもシャット」したい

のあたりを解決しようかなぁ、と思いました。


細かい所をかくと

・「自分のアカウントに紐付いているセッションIDのうち、現在のセッション以外を全部遮断」(gmailの「アカウント アクティティ」の「他のすべてのWebセッションからログアウト」)

も実装したいのだけど…ここに手を入れるのは些か難儀なので、一旦見送り。


で、実装方法を簡単に。

基本は、いわゆる「認可(authorization):ログイン中のユーザかどうか」の判断ロジックのところで、少しギミックを入れました。

その手前として、ログイン(認証:authentication)のタイミングでも、ギミック追加しています。


寿命については、大まかには

ログイン時に、$_SESSIONに「有効時間」を入れる

・認可の度に

・・有効時間を超えてたら問答無用でログアウトさせる

・・有効時間範囲内なら「有効時間を更新して」認可する

ってロジック


また合わせて、認可のタイミングのラスト付近で「ロックテーブル」なるテーブルを1枚用意して。最低限としては「ユーザID」だけがカラムにあればよくて、判断としては

・ロックテーブルに「認可を試みてるユーザのユーザIDが存在してたら、認可しない」

って感じで「問答無用でロック」が出来るようにしました。

DBアクセスが1つ増えるのが微妙っちゃぁ微妙なんだけど、まぁその辺はトレードオフかなぁ、と。

基本「KVS的なアクセス」でよいので、その辺で色々と小細工をしてみてもよいだろうし。


たいした実装でもないのですが、色々と「ちょっともにょってた」ところが実装できたので、割と満足でございます。


実装サンプルはこちらからどんぞ。

https://github.com/gallu/MagicWeaponTest

の「lib/common/base_model_admin_base.inc」と、

https://github.com/gallu/MagicWeapon

の「authorization_session.inc」

あたりを追いかけていくと、大体、処理が点在しています(笑


あとは…微妙っちゃぁ微妙なのですが

・そもそも「Anonymouse セッション(ログインしてなくても始まるセッション)」ってどうなんだろう?(セッションアダプションはやっぱりちょっと気になる)

・「他のすべてのWebセッションからログアウト」入れたいよねぇ

あたりになると、セッション周りを「自作しなおすかねぇ( http://d.hatena.ne.jp/gallu/20160402/p1 )」という話になって。

ただ、この辺になると上述で言うところの「authorization_session.inc」の処理にも手を加える(ってか多分、別クラスにする)ってのもあって…ってのが、「セッションIDを作ったり設定したりする」辺りを全部自前実装にする気ぃ満々なのでw

興味はあるんだけど少し時間がかかりそうで……次回短期入院の時の課題かなぁ、とw


まぁ「なければ作る」はUNIX系列の伝統的作法だと思うので。

便利なPHPの機能は最大限「そのまま使いつつ」、不便な所をラップしていくのが最近のおいちゃんの好みかなぁ、という辺りでの邁進を想定しておりまするる。