さくらインターネットでメールを受信すると、それをトリガーにして、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];
}