Qdmailは使うときだけnewするかvar $is_qmailを変更しよう (特にCakePHPで使う場合)

snapshot.png

ゼロ設定でほぼ文字化けせず、主要PHPフレームワーク(CakePHP,Symfony,CodeIgniter,ZendFrameWork,Ethna)に直接組み込めるのが魅力のPHPのメール送信ライブラリ「Qdmail」についてのTIPSです。対象のQdmailのバージョンは1.2.6bです。

きっかけはアクセス毎にmaillogが発生する現象から

最近CakePHPでメール受信系のシステムを構築したときのこと。sendmailのログを/var/log/maillogで追っかけていたら「なぜかサイト(Apache)にアクセスがあるたびにmaillogにログが1行挿入される」という現象を偶然確認しました。もちろん、アプリからメール送信は行っていません。

maillogに入力されるログは以下のようなものです。

Mar 30 10:41:26 example sendmail[27821]: n2U1fQGv027821: from=daemon, size=0, class=0, nrcpts=0, msgid=<200903300141.n2U1fQGv027821@example.com>, relay=daemon@localhost

sendmail.mcでconfLOG_LEVELを15にしてもログはこのように1行しか吐かれませんでした。ログレベルを上げればわかるだろうと思っていたので、困りました。

Qdmailの初期化でログが発生する

そのまま放置しておくわけにもいかないので、どういうことだろうと突き詰めていくと、どうやら「Qdmailの初期化時にログが1行発生する」ということがわかりました。

メール送信を行っていないのに初期化だけでなぜログが発生するのだろう...とさらにコードを追ってみました。すると、実はQdmailはコンストラクタの処理中に内部で使用する改行コードを決定するためにsendmailを一度実行していて、それでログが発生していたことを突き止めました。

ログの発生箇所はQdmailBase::isQmail()

sendmailを実行しているコードはQdmailBase::__construct()から呼ばれるQdmailBase::isQmail()内です。

// qdmail.php
class QdmailBase extends QdmailBranch{
  (省略)
  function __construct( $param = null ){
    (省略)
    if($this->isQmail()){
      $this->LFC = $this->LFC_Qmail;
    }
    (省略)
  }
  (省略)
  function isQmail(){
    if(!is_null($this->is_qmail)){
      return $this->is_qmail;
    }
    $this->is_qmail = false;
    $ret = ini_get ( 'sendmail_path' );
    if(false !== strpos($ret,'qmail')){
      $this->is_qmail = true;
    }
    $sendmail_path = ini_get('sendmail_path');
    if(false !== @system($sendmail_path.' -d0.1 < /dev/null > /dev/null',$ret)){
      if(is_array($ret)){
        $ret = reset($ret);
      }
      $code = (int) substr($ret,0,3);
      if( 100 === $code || 111 === $code){
        $this->is_qmail = true;
      }
    }

    return $this->is_qmail ;
  }

修正方法はQdmailBase::$is_qmailのデフォルト値を変更する

修正方法は、ソースコードの直接書き換えとしました。今回の環境ではsendmailを使うので、QdmailBase::$is_qmailのデフォルト値をfalseとしました。

// qdmail.php
class QdmailBase extends QdmailBranch{
  (省略)
  // var $is_qmail = null;
  var $is_qmail = false;

qmailの場合はtrueとすればいいかどうかは動作未確認です。

Qdmailを使うときだけ初期化する場合は問題にはならない

ちなみにフレームワークに組み込まずにメール送信処理を行うときだけnewしている場合は、ほとんどの場合たいして問題にはなりません。当たり前ですが、Qdmailを呼び出さなければ、ログが発生しないからです。

今回の場合、CakePHPのコンポーネントとしてQdmailを利用していたので、アプリの起動時に必ずQdmailのコンストラクタが走っていました。それでアクセス毎にmaillogが発生していたんですね。他のフレームワークではどうなるかは未確認です。

実行時間が30msec~100msec短縮された!

また、ログだけではなく毎回system関数でsendmailを実行していたため、修正前と修正後では30msec~100msecほどアプリの実行時間が短くなりました。これはかなり無視できない数字です。QdmailをCakePHPのコンポーネントとして組み込んでいる方は、ぜひ修正を検討して欲しいと思います。

CakePHPであってもコンポーネントとして組み込まなければならないわけではなく、使用時に呼び出す手順に変更するのも一つの手です。

ちなみに僕はQdmailにはお世話になりまくっています。Qdmailは公式のマニュアルやTIPSがすごく充実していて、クラスの設計も扱いやすく、本当に助かっています。個人的にはかなりおすすめなので、ぜひどうぞ。

コメント / トラックバック

コメント / トラックバック 2 件

  1. [...] っと挑発的なタイトルになってしまいました。 akiyan.comのQdmailは使うときだけnewするかvar $is_qmailを変更しよう (特にCakePHPで使う場合)という記事を見て、自分の環境で試したところ性能に無 [...]