tohokuaikiのチラシの裏

技術的ネタとか。

添付メールの自動処理をPHP噛ませてやらせたら、PipeでつないでuudeviewにやらせるよりPHPでデコードした方が速かった件

昨日の続き。

Propmailでメールの添付ファイルを自動処理させたいんだけど、添付されたファイルをデコードするのにPHPでやった方がいいか、uudeviewという外部システムを使ったやった方が良いかっていう。

とりあえず、PHPのみで完結させる場合

PHPでやる場合は、id:ya--madaこのライブラリを使った。

コードはこんな感じ。
proc_test_php.php

<?php
$stdin = fopen("php://stdin", "r");
$header = "";
$_header_write = true;
$raw_mail = "";
do {
	$line = fread($stdin, 4096);
	if (strlen($line) == 0) break;

	if ( $n = strpos($line, "\n\n")){ // 空行を見つけた
		$header .= substr($line, 0, $n);
		$_header_write = false;
	}
	if ($_header_write) $header .= $line;
	$raw_mail .= $line ;//. PHP_EOL;
} while(true);

fclose($stdin);

require_once 'Projects/279be/itoh/showtie/showtie/lib/ReceiptMailDecoder.class.php';
$decoder = new ReceiptMailDecoder($raw_mail);
// To:アドレスのみを取得する
$toAddr = $decoder->getToAddr();
// To:ヘッダの値を取得する
$toString = $decoder->getDecodedHeader( 'to' );
// Subject:ヘッダの値を取得する
$subject = $decoder->getDecodedHeader( 'subject' );
// text/planなメール本文を取得する
$body = mb_convert_encoding($decoder->body['text'],"eucjp-win","jis");
// text/htmlなメール本文を取得する
$body = mb_convert_encoding($decoder->body['html'],"eucjp-win","jis");
// マルチパートのデータを取得する
if ( $decoder->isMultipart() ) {
	$tempFiles = array();
	$num_of_attaches = $decoder->getNumOfAttach();
	for ( $i=0 ; $i < $num_of_attaches ; ++$i ) {
		/*
		 * ファイルを一時ディレクトリ _TEMP_ATTACH_FILE_DIR_ に保存する
		 * 一時ファイルには tempnam()を使用する
		 * この部分は使用に合わせて変更して下せい
		 */
		$fpath = tempnam('./hoge', "todoattach_" );
		if ( $decoder->saveAttachFile( $i, $fpath ) ) {
			$tempFiles["$fpath"] = $decoder->attachments[$i]['mime_type'];
		}
	}
}

んで、コマンドラインから

$ time cat Maildir/new/1251350204.25353_0.test.example.com |php -d memory_limit=10M proc_test_php.php

って感じで添付ファイル付きのメール(1251350204.25353_0.test.example.com)をパイプして渡してやる。ちなみに、メールの添付ファイルは1M程度。

結果は

real 0m0.081s
user 0m0.062s
sys 0m0.021s

結構速い。

uudeviewを使った場合

じゃあ、uudeview一発でっていうわけにもいかなくて、uudeviewはメールのヘッダ情報なんかを結果としてSTDOUTに出さない(Subjectのみかろうじて)ので、PHPの中を一回通してHeaderを拾ってやる。

こんな感じ。
proc_test.php

<?php
$stdin = fopen("php://stdin", "r");
$uudeview = popen('uudeview - -p ~/hoge 2>&1', 'w');
$header = "";
$_header_write = true;

do {
	$line = fread($stdin, 4096);
	if (strlen($line) == 0) break;

	if ( $n = strpos($line, "\n\n")){ // 空行を見つけた
		$header .= substr($line, 0, $n);
		$_header_write = false;
	}
	if ($_header_write) $header .= $line;

	fputs($uudeview, $line);
} while(true);

fclose($stdin);
fclose($uudeview);

で、コマンドラインからは

$ time cat  Maildir/new/1251350204.25353_0.test.example.com |php proc_test.php

って感じ。


結果は、

real 0m0.162s
user 0m0.129s
sys 0m0.024s

・・・遅っ!ってほどでもないけど、PHPより遅いんだ。

PHP内でパイプしないで、やってみる

PHP内を通したから遅いのかなーとか一瞬思って
proc_test2.php

<?php
$stdin = fopen("php://stdin", "r");
$header = "";
$_header_write = true;

do {
	$line = fread($stdin, 4096);
	if (strlen($line) == 0) break;

	if ( $n = strpos($line, "\n\n")){ // 空行を見つけた
		$header .= substr($line, 0, $n);
		$_header_write = false;
	}
	if ($_header_write) $header .= $line;

	echo $line;
} while(true);

fclose($stdin);

ってしてみて、コマンドラインから

$ time cat  Maildir/new/1251350204.25353_0.sc440.local.junoe.jp |php proc_test2.php|uudeview - -p ~/hoge 2>&1

ってしてみる。

結果、

real 0m0.147s
user 0m0.126s
sys 0m0.022s

・・・変って無い。


まぁ、そもそもuudeviewだけでやっても

$ time uudeview -i Maildir/new/1251350204.25353_0.sc440.local.junoe.jp

real 0m0.117s
user 0m0.107s
sys 0m0.009s

なんだけどね。

ということで、

PHPすごいよ、PHP

あとは、ライブラリの精度次第だな。PHPPEAR::Mail_mimeDecodeが取りこぼし多かったら、uudeviewにしてみるとか。

あと、PHPが意外とメモリ食うのが気になる。1M程度のメールの処理に10Mもmemory_limitを使うなんて。うーん。