がるの健忘録

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

後で推敲する用memo:空メールを経由するユーザ登録処理

一般的な流れとMagicWeapon特有の処理をだらりんこと。


まず。
(一般)
「ユーザが特定のアドレスに空メールを送る」で、サーバ側で「メール受信を契機にプログラムを起動する」処理を書きます。
例えば、hogera@foo.com にメールを送った場合。
foo.comを持ってるSMTPサーバの、大抵は /etc/aliases あたりに、こんな記述をします。

hogera: | "/usr/local/bin/php /opt/www/hogeramugera/batch/gumomo.php"

これは「hogeraってアドレスにメールが来たら "/usr/local/bin/php /opt/www/hogeramugera/batch/gumomo.php" を実行する」という意味で。
"/usr/local/bin/php /opt/www/hogeramugera/batch/gumomo.php" は「PHP として、/opt/www/hogeramugera/batch/gumomo.php を実行してね」という意味あいです。
実際の細かいパスとかはサーバによって違うので適宜よみかえてちょ。


次に。
(一般)
/opt/www/hogeramugera/batch/gumomo.php での処理ですが。
メールは「標準入力」に文字列としてだらりんこと入ってきます。
PHP的には、fopen('php://stdin', 'r') とか、もうちょっとざっくりいくなら file_get_contents("php://stdin") とかで、その文字列を取得します。
後は気合い入れてmail header部を解析します。
(MW)
ざっくりいくなら empty_mail_analysis クラスがお便利です。
以下のソースから大体なにかをイメージしてみてください。

require_once('empty_mail_analysis.inc');

$obj = new empty_mail_analysis;

$obj->set_token_separator('-');
//$obj->analysis();
$obj->analysis_from_stdin();

$from = $obj->get_mail_from();
$to = $obj->get_mail_to();
$tkn = $obj->get_mail_to_token();

print "from: $from \n";
print "to  : $to \n";
print "tkn : $tkn \n";

んと。
fromは「メールを送ってきた人のアドレス」。多分一番重要。
toは「どこ宛のメールか」。今回は「hogera」って解ってるから使わない。複数のアドレスを一気に処理したいなら使うかな。
tknは。えと…例えば。SMTPにもよるですが、hogera@foo.com の代わりに、例えば hogera-regist@foo.com(qmail)、hogera+regist@foo.com(postfix)、というアドレスが使えます。−なり+なり以降はお好きな文字列をどんぞ。
ちなみにこれを「拡張メールアドレス」と言って。
qmailは(多分)−で固定。
postfixは、デフォは+、main.cfの「recipient_delimiter」に設定をかますと − にもできます。
sendmailは…sendmail.cfを書き換えると可能(やったことあるし)なのですが…レシピ無くした orz。再構築する必要性をあんまり感じてないので、ここでは省略。


閑話休題


で。その拡張メールアドレスの+なり−なり以降の文字列が、tknに入るです。
ようは「mail headerの分析を少しやってくれる」クラスだとおもいねぇ。


んで。
その後は…まぁお好みで処理して下さい、なのですが。とりあえず「メールの送り主のアドレスはゲトれてるわけですし」。
(MW)
MWの場合、その後の処理も一応フォローしてます。
empty_mail_cushion というクラスがそれに当たるのですが。
何をやるかというと…


・empty_mail_analysisを使って、from, tknの情報をげとる。
・適当に、IDを作成する(tokenizer使用)
・上述のIDをpkにして、fromとtknをDBに格納する
・IDを暗号化する(デフォはrijndael256。っつかrijndaelしか今ロジック作ってない orz)。暗号化は、別途設定ファイルを用いる。
・同じく設定ファイルに書いてある内容を用いて、fromアドレスにメールを送る。
 返信するメールのテンプレートはconvフォーマットで。
 %%%token%%%に、暗号化されたIDを記述する。


ってな処理を行うです。
なので、後は、上述の「暗号化されたID」をプログラム側で受け取って。
・tokenの暗号を複合して
・IDを取り出して
・DBに問い合わせると
先ほどげとった、ユーザさんのメールアドレスがゲトれるわけです。


さて…どこでwikiにフォーマット変換しつつまとめよう orz

後で推敲する用memo:編集 -> 確認( -> 完了)

edit

//処理
public function execute_auth() {
  // もし「confから来たわけではない」場合、DBからの情報を設定する
  if (false === $this->is_bag('edit_from_cgi')) {
    $cobj = new hogehoge_clump();
    $cobj->set_db($this->get_db());
    $cobj->set_value('pk', $this->get_cgi_request()->find('pk'));
    if (true === $cobj->get()) {
      // データ一式ぶち込む
      $cobj->set_all_to_conv($this->get_conv());
    } else {
      // XXX どこにエラーすっ飛ばしてやろうかねぇ?
      $this->recursive_on();
      $this->set_recursive_command('purgatorium');
      return ;
    }
  }

  //
  return ;
}

conf

//処理
public function execute_auth() {
  // cgiリクエストが詰まったオブジェクト取ってくる
  $req = $this->get_cgi_request();
  // convインスタンス取ってくる
  $conv = $this->get_conv();
  // DBインスタンス取ってくる
  $dbh = $this->get_db();

  // clump作る
  $cobj = new hogehoge_clump();

  // まずはDBから情報を
  $cobj->set_db($this->get_db());
  $cobj->set_value('pk', $this->get_cgi_request()->find('pk'));
  if (false === $cobj->get()) {
    // XXX どこにエラーすっ飛ばしてやろうかねぇ?
    $this->recursive_on();
    $this->set_recursive_command('purgatorium');
    return ;
  }

  // 情報を上書くように取得
  $cobj->set_from_cgi($req); // この一行でformの所定のデータを一式ゲトる

  // vlidate
  if (false === $cobj->is_valid($conv)) { // validate & エラーならエラー出力用の設定を一式
    // editコマンドに流す
    $this->set_bag('edit_from_cgi', true); // YYY
    $this->recursive_on();
    $this->set_recursive_command('edit'); // YYY
    //
    return;
  }

  // うまくいったっぽなので…

  // 「今の日付」を入れてみる
  $cobj->set_value_nowdate('update_date', false);

  // セッションにデータをいったんぶち込む
  $cobj->set_db($dbh);
  $cobj->set_to_tmp($this->get_session_obj());

  // 確認画面用の表示を設定する
  $cobj->set_all_to_conv($conv);

  //
  return ;
}

CONVの使い方:複置換変:余談

本来的には、multiDicは「要素部分を"ごにょごにょする"」ものなので。
自作クラスを渡せば結構遊べまふ。最低限必要なのは

function  m_conv($body, $param) {

ここだけだし。$bodyに要素がはいるとおもいねぇ。
$paramは…しばらく使ってないけど。例えば

$$$loop$$$

$$$loop:20$$$

って書くと、20が渡されるです。loop数の制御にしようかと思ったんだけど…プログラムで最近制御しちゃってるからなぁ、的な感じ。


質問疑問その他あったらお気軽によろろです ノ

CONVの使い方:複置換変:エラー出力とかの「特定条件出力」

こっちはちょ〜簡単。
例えばエラーメッセージがあるとするです。テンプレート的には、こう。

$$$if_error_name_must$$$
<FONT color="#ff0000">ちょっとちょっと名前くらい名乗っていきなよ<br>
$$$/if_error_name_must$$$


プログラムで、下記記述があれば、出力されるです。なきゃ出力しないだけ。

CONVインスタンス->multiDic('if_error_name_must', new simple_put());


だから、普通はエラーチェックん中で、こうやりまふ。

$name = CGIリクエストインスタンス->find('name');
if ("" === $name) {
  CONVインスタンス->multiDic('if_error_name_must', new simple_put());
  err_flg = true;
}

CONVの使い方:複置換変:ループ

ループ系複置換の基本は
「"ハッシュ配列"の配列」を作って渡すこと。
で、data_clumpと組み合わせた方法。…ちなみに「SQL文使ってpkのlistを作る」のは、おいちゃん的には結構いや(笑
なにがしか今度作りたいなぁ。
ページ処理したい時は、page_controll_limit クラスと適宜併用してちょ。


まずはテンプレート書式。適当に、掲示板的なものを想定(一行横に並ぶ掲示板ってのもどうかとは思うけど)。
デザインがなってないとかいう突っ込みは禁止(笑

<TABLE border="1">
<tr>
  <td>タイトル
  <td>名前
  <td>投稿日時
  <td>email
  <td>本文
$$$bbs_loop$$$
<tr>
  <td>%%%title%%%
  <td>%%%name%%%
  <td>%%%insert_date%%%
  <td>%%%email%%%
  <td>%%%body%%%
$$$/bbs_loop$$$
</table>


んで、プログラム側。ぢつは結構シンプル。PKは1つであると仮定〜。
ID配列の作成とぶん回しは一緒にしてもいいんだけど、説明のしやすさを基準に分離してまふ。

// PKなID配列の取得
$sql = SQL文; // "SELECT bbs_id FROM bbs WHERE 適当な条件;" とかね。
$ids = array();
$res = DBハンドルインスタンス->query($sql);
wihle($res->fetch()) {
  $ids[] = $res->get_data(0);
}

// データを設定する
$data = array();
$obj = new bbs_clump();
foreach($ids as $id)
{
  // データをゲト
  $obj->init();
  $obj->set_db(DBハンドルインスタンス);
  $obj->set_value('bbs_id', $id, false);
  // 直前でIDげとってるんでエラー処理は省いてまふ。チェックするなら、booleanでfalseが返ってきたらNG処理ってことで
  $obj->get();

  // ハッシュ配列に情報をげと
  $wk = $obj->get_all_data_to_hash();

  // XXX 情報を追加するなら、ここで$wkに対してkeyとvalueを適宜設定

  // データを設定
  $data[] = $wk;
}

// CONVに設定
// XXX 知っている人に。simple_loop、インスタンス生成でいきなりset_data出来るように改造した〜
CONVインスタンス->multiDic('bbs_loop', new simple_loop($data));

こんなげ。

CONVの使い方:特殊置換変

radioボタンとかselectボタンとかで「入力された値を保持したい」時に使うあれ。
都道府県の場合は、 がらくた箱( http://www.gjmj.net/ )ん中にある http://www.gjmj.net/area.html が端的に楽なんだけど(笑
書式は、こんな感じ。

<!-- 都道府県の場合:SELECTの場合、ともいう -->
<SELECT name="area">
  <option value="1" ***area:1*** >北海道</option>
  <option value="2" ***area:2*** >青森県</option>
  <option value="3" ***area:3*** >岩手県</option>
  <option value="4" ***area:4*** >宮城県</option>
  <option value="5" ***area:5*** >秋田県</option>
  <option value="6" ***area:6*** >山形県</option>
  <option value="7" ***area:7*** >福島県</option>
  <option value="8" ***area:8*** >茨城県</option>
  <option value="9" ***area:9*** >栃木県</option>
  <option value="10" ***area:10*** >群馬県</option>
  <option value="11" ***area:11*** >埼玉県</option>
  <option value="12" ***area:12*** >千葉県</option>
  <option value="13" ***area:13*** >東京都</option>
  <option value="14" ***area:14*** >神奈川県</option>
  <option value="15" ***area:15*** >新潟県</option>
  <option value="16" ***area:16*** >富山県</option>
  <option value="17" ***area:17*** >石川県</option>
  <option value="18" ***area:18*** >福井県</option>
  <option value="19" ***area:19*** >山梨県</option>
  <option value="20" ***area:20*** >長野県</option>
  <option value="21" ***area:21*** >岐阜県</option>
  <option value="22" ***area:22*** >静岡県</option>
  <option value="23" ***area:23*** >愛知県</option>
  <option value="24" ***area:24*** >三重県</option>
  <option value="25" ***area:25*** >滋賀県</option>
  <option value="26" ***area:26*** >京都府</option>
  <option value="27" ***area:27*** >大阪府</option>
  <option value="28" ***area:28*** >兵庫県</option>
  <option value="29" ***area:29*** >奈良県</option>
  <option value="30" ***area:30*** >和歌山県</option>
  <option value="31" ***area:31*** >鳥取県</option>
  <option value="32" ***area:32*** >島根県</option>
  <option value="33" ***area:33*** >岡山県</option>
  <option value="34" ***area:34*** >広島県</option>
  <option value="35" ***area:35*** >山口県</option>
  <option value="36" ***area:36*** >徳島県</option>
  <option value="37" ***area:37*** >香川県</option>
  <option value="38" ***area:38*** >愛媛県</option>
  <option value="39" ***area:39*** >高知県</option>
  <option value="40" ***area:40*** >福岡県</option>
  <option value="41" ***area:41*** >佐賀県</option>
  <option value="42" ***area:42*** >長崎県</option>
  <option value="43" ***area:43*** >熊本県</option>
  <option value="44" ***area:44*** >大分県</option>
  <option value="45" ***area:45*** >宮崎県</option>
  <option value="46" ***area:46*** >鹿児島県</option>
  <option value="47" ***area:47*** >沖縄県</option>
</SELECT>

<!-- 性別の場合、或いはradioボタンともいう -->
<input type="radio" name="sex" value="male" ***sex:male*** >男性
<input type="radio" name="sex" value="female" ***sex:female*** >女性

チェックボックスがちと手元になかったんだけど(後で書き足すかも)、書式は全く持って一緒。
nameアトリビュートアトリビュート値とvalueアトリビュートアトリビュート値が書いてある思えばOK。
で、プログラムは…今は conv_util.incにある

// checked用:radio , checkbox用
static public function monoDicChecked($conv, $name, $value)

// selected用:select option 用
static public function monoDicSelected($conv, $name, $value)

を使えば足りるザンス。例えば性別の場合

$sex = $_POST['sex'];
conv_util::monoDicChecked($conv, 'sex', $sex);

って感じ。checkboxの場合、第三引数を配列で渡せばOK(っつか本来配列が基本なので)。
この子の便利なのは。要素が増えてもプログラムを変更しないですむってところ。

CONVの使い方:単置換変

一番単純な、一対一の置換。
テンプレート側

名前:%%%name%%%

に対して、プログラム側では

CONVインスタンス->monoDic('name', $name);

とかってcall。


data_clumpとのコンボ技の場合、まとめて

clumpインスタンス->set_all_to_conv(CONVインスタンス);

CONVの代わりにsecure_convを使えば、上述のセットは全てエスケープされるので無問題。
現状は、radioボタンとかのあたりには対応していないけど。その辺は、data_clump側のset_all_to_convメソッドを上書きして色々ギミックを足すと便利だと思う(最低限については、後で追加予定)。
逆に「ど〜〜〜〜〜〜〜〜〜〜してもHTMLタグを直接書きたい」場合、monoDicの代わりにmonoDic_unsecure_rawを使えば一応OK。
でも使わないように。