さくらインターネットでメールを受信すると、それをトリガーにして、CakePHPのControllerを起動させます。簡単に言えば、さくらインターネットの指定のメールアドレスにメールを送信すると、折り返し、送信元のアドレスに自動返信したり、メールを受けると、それを指定の数カ所に転送したり、空メールが登録できるようになります。この機能をCakePHPを用いて実装します。
動作環境
- cakephp-2.5.2
- PEAR-1.9.4
- さくらインターネット
まずはメール側の話をします。さくらインターネットでメールを受信できるように設定すると、「aaa@アカウント名.sakura.ne.jp」なら「/home/アカウント名/Mailbox/aaa」としてサーバにフォルダが作成され、その直下に.mailfilterファイルが作成されます。.mailfilterファイルに以下のように受信したメールを処理するための記述を設定します。ここでは、receivers_contoroller.phpに記述されたReceiversContorollerを起動することを意図してみます。
to “| /usr/local/bin/php -q /home/アカウント名/www/cake/receiverscontoroller/index.php /receivers_contoroller”
次にCakePHP側の話をします。さくらインターネットの受信メールでCakePHPを起動させるには、次の設定が必要となります。
- /www/cake/receiverscontoroller/index.php :ここには、Webrootのindex.phpのディレクトリを記述します。
- /receivers_contoroller :コントローラのアクションを動作させる場合、半角スペース+コントローラ名とアクション名を記述します。
メールを受け取った CakePHPのControllerのアクション(この場合、index)で、標準入力の内容を読めば、メール受信時の処理を行なえます。今回の設定には、PEAR のMail_mimeDecodeを使用します。
class ReceiversContoroller extends AppController { public function index() { $path = '/(rootからPEARまでのパス名)”/'; set_include_path(get_include_path() . PATH_SEPARATOR .$path); require_once 'Mail/mimeDecode.php'; #-- メールデータ取得 $params['include_bodies'] = true; $params['decode_bodies'] = true; $params['decode_headers'] = true; $params['input'] = file_get_contents("php://stdin"); // 標準入力 $params['crlf'] = "\r\n"; $mail_data = Mail_mimeDecode::decode($params); #-- From フィールドの取得 $FromAddress = $mail_data->headers['from']; #-- To フィールドの取得 $ToAddress = $mail_data->headers['to']; #-- Subject フィールドの取得 $Subject = $mail_data->headers['subject']; $Subject = mb_convert_encoding($Subject,"UTF-8","JIS"); #-- 本文の取得 $MailBody = $mail_data->body; $MailBody = mb_convert_encoding($MailBody,"UTF-8","JIS"); } }
CakePHPでPEARのパッケージを利用する場合、次の方法が考えられます。
- ディレクトリ Vendorsに格納して、「App::import()」で呼び出す
- CakePHPとは無関係なPEAR格納用ディレクトリを指定して、「App::import()」で呼び出す
- include(あるいはrequire)で呼び出す
今回は、3番目の方法で、アクションの先頭でrequire_onceを利用する方法で、アクションの先頭でパスを設定し、require_onceを使用して、mimeDecodeを呼び出しました。
ここまでの設定で、/home/アカウント名/www/cake/receiverscontoroller/index.phpには制御が移り、index.phpでDispatcherのインスタンスが生成されるところまでは正常に動作します。しかし、ReceiversContorollerが起動されません。この確認には、トレーサーとしてデバッグ用のファイルを作成し、このファイル実行された部分を示す番号を書き込み、どこまで実行されたかを確認しました。なおこのデバッグ用のファイルは、「/home/アカウント名/Mailbox/aaa」に作成されます。
index.phpからコントローラーまで
/home/アカウント名/www/cake/receiverscontoroller/index.phpには制御が移ったが、ReceiversContorollerが起動されないので、CakePHPでexample.com/controller/actionの様な形式のURLが、どう扱われて、コントローラーのアクションが呼ばれるのか、ソースコードを読んで追いかけてみました。
index.php
CakePHPへのアクセスは、mod_rewriteで全部app/webroot/index.phpに集約されて、Dispatcherオブジェクトでdispatchされます。
$Dispatcher = new Dispatcher(); $Dispatcher->dispatch( new CakeRequest(), new CakeResponse() );
CakeRequest – cakephp\lib\Cake\Network\CakeRequest.php
Dispatcherに渡されるCakeRequestオブジェクトが生成される時、スーパーグローバル変数の$_SERVERから、コントローラー名やアクションを表すパス部分を取り出してます。パス部分は、.htaccessでindex.phpに引き継がないが、PATH_INFOやREQUEST_URIなどには残っています。また、$_SERVERから取得しているため、.htaccessで、コントローラー名やアクションを表すパス部分を引き継いでいません。
protected function _url() { if (!empty($_SERVER['PATH_INFO'])) { return $_SERVER['PATH_INFO']; } elseif (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '://') === false) { $uri = $_SERVER['REQUEST_URI']; } elseif (isset($_SERVER['REQUEST_URI'])) { $qPosition = strpos($_SERVER['REQUEST_URI'], '?'); if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) { $uri = $_SERVER['REQUEST_URI']; } else { $uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseUrl'))); } } elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) { $uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']); } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { $uri = $_SERVER['HTTP_X_REWRITE_URL']; } elseif ($var = env('argv')) { $uri = $var[0]; } $base = $this->base; if (strlen($base) > 0 && strpos($uri, $base) === 0) { $uri = substr($uri, strlen($base)); } if (strpos($uri, '?') !== false) { list($uri) = explode('?', $uri, 2); } if (empty($uri) || $uri === '/' || $uri === '//' || $uri === '/index.php') { $uri = '/'; } $endsWithIndex = '/webroot/index.php'; $endsWithLength = strlen($endsWithIndex); if ( strlen($uri) >= $endsWithLength && substr($uri, -$endsWithLength) === $endsWithIndex ) { $uri = '/'; } return $uri; }
CakePHPをさくらインターネットの受信メールで起動させるには、次のロジックの変更が必要となります。通常のWebブラウザからの起動では、上記に示すfunction _url()の4行目の条件を満足します。
- function _url()の13行目:$_SERVER[‘PHP_SELF’]と$_SERVER[‘SCRIPT_NAME’]にはデータが入っているので、条件を満足して、設定されていないデータで処理するため、正常動作しない。
- function _url()の18行目:$uri = $var[0]を$uri = $var[1]に変更します。.mailfilterに設定されたコントローラ名は、$var[1]に入ってきます。
変更した部分を次に示します。コメントにしている部分と上記のfunction _url()とを比較して参考にしてください。
if (!empty($_SERVER['PATH_INFO'])) { return $_SERVER['PATH_INFO']; } elseif (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '://') === false) { $uri = $_SERVER['REQUEST_URI']; } elseif (isset($_SERVER['REQUEST_URI'])) { $qPosition = strpos($_SERVER['REQUEST_URI'], '?'); if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) { $uri = $_SERVER['REQUEST_URI']; } else { $uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseUrl'))); } //} elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) { // $uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']); } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { $uri = $_SERVER['HTTP_X_REWRITE_URL']; } elseif ($var = env('argv')) { // $uri = $var[0]; $uri = $var[1]; }