<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 15103 -->
<sect1 id="zend.mail.read">
    <title>メールメッセージの読み込み</title>

    <para>
        <classname>Zend_Mail</classname> を使用すると、
        ローカルあるいはリモートに保存されたメールを読み込むことができます。
        すべての保存形式に共通の基本 API では、メッセージ数を数えたり
        メッセージを読み込んだりすることができます。また、
        いくつかの保存形式では、特殊な追加機能も実装されています。
        各保存形式で実装されている機能の概要については以下の表を参照ください。
    </para>

    <table id="zend.mail.read.table-1">
        <title>メール読み込み機能の概要</title>
        <tgroup cols="5">
            <thead>
                <row>
                    <entry>機能</entry>
                    <entry>Mbox</entry>
                    <entry>Maildir</entry>
                    <entry>Pop3</entry>
                    <entry>IMAP</entry>
                </row>
            </thead>
            <tbody>
                <row>
                    <entry>保存形式</entry>
                    <entry>ローカル</entry>
                    <entry>ローカル</entry>
                    <entry>リモート</entry>
                    <entry>リモート</entry>
                </row>
                <row>
                    <entry>メッセージの取得</entry>
                    <entry>Yes</entry>
                    <entry>Yes</entry>
                    <entry>Yes</entry>
                    <entry>Yes</entry>
                </row>
                <row>
                    <entry>MIME パートの取得</entry>
                    <entry>エミュレート</entry>
                    <entry>エミュレート</entry>
                    <entry>エミュレート</entry>
                    <entry>エミュレート</entry>
                </row>
                <row>
                    <entry>フォルダ</entry>
                    <entry>Yes </entry>
                    <entry>Yes</entry>
                    <entry>No</entry>
                    <entry>Yes</entry>
                </row>
                <row>
                    <entry>メッセージ/フォルダ の作成</entry>
                    <entry>No</entry>
                    <entry>対応予定</entry>
                    <entry>No</entry>
                    <entry>対応予定</entry>
                </row>
                <row>
                    <entry>フラグ</entry>
                    <entry>No</entry>
                    <entry>Yes</entry>
                    <entry>No</entry>
                    <entry>Yes</entry>
                </row>
                <row>
                    <entry>容量制限</entry>
                    <entry>No</entry>
                    <entry>Yes</entry>
                    <entry>No</entry>
                    <entry>No</entry>
                </row>
            </tbody>
        </tgroup>
    </table>

    <sect2 id="zend.mail.read-example">
        <title>Pop3 によるシンプルな読み込み例</title>

        <programlisting role="php"><![CDATA[
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'localhost',
                                         'user'     => 'test',
                                         'password' => 'test'));

echo $mail->countMessages() . " messages found\n";
foreach ($mail as $message) {
    echo "Mail from '{$message->from}': {$message->subject}\n";
}
]]>
        </programlisting>

    </sect2>
    <sect2 id="zend.mail.read-open-local">
        <title>ローカルに保存されたメールのオープン</title>

        <para>
            ローカルのメール保存形式としては、Mbox および Maildir
            をサポートしています。これらはともに、もっともシンプルな形式です。
        </para>
        <para>
            Mbox ファイルからメールを読み込むには、そのファイル名を
            <classname>Zend_Mail_Storage_Mbox</classname> のコンストラクタに渡すだけです。
        </para>

        <programlisting role="php"><![CDATA[
$mail = new Zend_Mail_Storage_Mbox(array('filename' =>
                                             '/home/test/mail/inbox'));
]]>
        </programlisting>

        <para>Maildir もほぼ同様ですが、こちらはディレクトリ名を指定します。</para>

        <programlisting role="php"><![CDATA[
$mail = new Zend_Mail_Storage_Maildir(array('dirname' =>
                                                '/home/test/mail/'));
]]>
        </programlisting>

        <para>
         どちらのコンストラクタも、もし読み込めなかった場合は
         <classname>Zend_Mail_Exception</classname> をスローします。
        </para>

    </sect2>
    <sect2 id="zend.mail.read-open-remote">
        <title>リモートに保存されたメールのオープン</title>

        <para>
            リモートの保存形式としては、もっとも有名なふたつである
            Pop3 と Imap をサポートしています。それぞれ、
            ホスト名とユーザ名を指定して接続、ログインします。
            デフォルトのパスワードは空の文字列で、デフォルトのポート番号は
            そのプロトコルの RFC で指定されているものです。
        </para>

        <programlisting role="php"><![CDATA[
// Pop3 での接続
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'example.com',
                                         'user'     => 'test',
                                         'password' => 'test'));

// Imap での接続
$mail = new Zend_Mail_Storage_Imap(array('host'     => 'example.com',
                                         'user'     => 'test',
                                         'password' => 'test'));

// 非標準のポートの例
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'example.com',
                                         'port'     => 1120
                                         'user'     => 'test',
                                         'password' => 'test'));
]]>
        </programlisting>

        <para>
            どちらの保存形式についても、SSL や TLS をサポートしています。
            SSL を使用する場合、デフォルトのポートは RFC
            にあるとおりに変更されます。
        </para>

        <programlisting role="php"><![CDATA[
// Zend_Mail_Storage_Pop3 の例ですが、Zend_Mail_Storage_Imap でも同様です

// SSL を使用する場合はポートが異なります (デフォルトは Pop3 なら 995、Imap なら 993)
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'example.com',
                                         'user'     => 'test',
                                         'password' => 'test',
                                         'ssl'      => 'SSL'));

// TLS を使用します
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'example.com',
                                         'user'     => 'test',
                                         'password' => 'test',
                                         'ssl'      => 'TLS'));
]]>
        </programlisting>

        <para>
            どちらのコンストラクタも、エラーの形式によって
            <classname>Zend_Mail_Exception</classname> あるいは <classname>Zend_Mail_Protocol_Exception</classname>
            (<classname>Zend_Mail_Exception</classname> を継承したもの) をスローします。
        </para>

    </sect2>
    <sect2 id="zend.mail.read-fetching">
        <title>メッセージの取得およびシンプルなメソッド</title>

        <para>
            ストレージをオープンしたら、メッセージを取得することができます。
            メッセージを取得するには、メッセージ番号が必要です。
            これは、最初のメッセージを 1 番とする連番となります。
            メッセージを取得する際に使用するメソッドは <code>getMessage()</code> です。
        </para>

        <programlisting role="php"><![CDATA[
$message = $mail->getMessage($messageNum);
]]>
        </programlisting>

        <para>
            配列形式のアクセスもサポートしていますが、<code>getMessage()</code>
            に追加のパラメータを渡すことはサポートしていません。
            なにも気にせずデフォルトでいいなら、このように使用します。
        </para>

        <programlisting role="php"><![CDATA[
$message = $mail[$messageNum];
]]>
        </programlisting>

        <para>全メッセージについて順に処理するために、Iterator インターフェイスも実装されています。</para>

        <programlisting role="php"><![CDATA[
foreach ($mail as $messageNum => $message) {
    // 何かの処理 ...
}
]]>
        </programlisting>

        <para>
            保存されているメッセージ数を数えるには、
            <code>countMessages()</code> メソッドあるいは配列形式のアクセスを使用します。
        </para>

        <programlisting role="php"><![CDATA[
// メソッド
$maxMessage = $mail->countMessages();

// 配列形式のアクセス
$maxMessage = count($mail);
]]>
        </programlisting>

        <para>
            メールを削除するには、<code>removeMessage()</code>
            メソッドあるいは配列形式のアクセスを使用します。
        </para>

        <programlisting role="php"><![CDATA[
// メソッド
$mail->removeMessage($messageNum);

// 配列形式のアクセス
unset($mail[$messageNum]);
]]>
        </programlisting>

    </sect2>
    <sect2 id="zend.mail.read-message">
        <title>メッセージの操作</title>

        <para><code>getMessage()</code> でメッセージを取得したら、
        次にしたくなることは、ヘッダの取得やマルチパートメッセージの各パートの取得などでしょう。
        すべてのヘッダには、プロパティあるいはメソッド
        <code>getHeader()</code> (一般的でないヘッダの場合) でアクセスできます。
        ヘッダ名は、内部では小文字で表されます。
        したがって、メールメッセージ内のでのヘッダ名は関係ありません。
        また、ヘッダ名にダッシュが入っている場合は、
        camel-case で保持されます。どちらの記法でもヘッダが見つからなかった場合は、例外がスローされます。
        そんな場合は、<code>headerExists()</code> メソッドを使用すれば
        ヘッダが存在するかどうかを調べることができます。</para>

        <programlisting role="php"><![CDATA[
// メッセージオブジェクトを取得します
$message = $mail->getMessage(1);

// メッセージの件名を出力します
echo $message->subject . "\n";

// content-type ヘッダを取得します
$type = $message->contentType;

// CC が設定されているかどうかを調べます
if( isset($message->cc) ) { // あるいは $message->headerExists('cc');
    $cc = $message->cc;
}
]]>
        </programlisting>

        <para>同名のヘッダが複数ある場合 (たとえば Received ヘッダなど)、
        それを文字列ではなく配列として扱うこともできます。これは
        <code>getHeader()</code> メソッドを使用して行います。</para>

        <programlisting role="php"><![CDATA[
// ヘッダをプロパティとして取得します - 結果は常に文字列で、
// メッセージ内で複数あらわれる場合は改行文字で区切られます
$received = $message->received;

// getHeader() メソッドを使用しても同様です
$received = $message->getHeader('received', 'string');

// 配列形式の場合、複数あらわれるとそれぞれ別のエントリとなるので便利です
$received = $message->getHeader('received', 'array');
foreach ($received as $line) {
    // なにかをします
}

// 書式を指定しなかった場合は内部表現で取得します
// (ひとつしかない場合は文字列、複数ある場合は配列となります)
$received = $message->getHeader('received');
if (is_string($received)) {
    // メッセージ内にそのヘッダはひとつしかありません
}
]]>
        </programlisting>

        <para><code>getHeaders()</code> メソッドは、すべてのヘッダを配列で返します。
        キーはヘッダ名を小文字にしたもので、値は文字列 (そのヘッダがひとつの場合)
        あるいは文字列の配列 (そのヘッダが複数の場合) となります。</para>

        <programlisting role="php"><![CDATA[
// すべてのヘッダを出力します
foreach ($message->getHeaders() as $name => $value) {
    if (is_string($value)) {
        echo "$name: $value\n";
        continue;
    }
    foreach ($value as $entry) {
        echo "$name: $entry\n";
    }
}
]]>
        </programlisting>

        <para>マルチパートメッセージがないのなら、その内容は
        <code>getContent()</code> で簡単に取得できます。ヘッダの場合とは異なり、
        内容は必要になった時点で初めて取得します
        (いわゆる遅延取得っていうやつです)。</para>

        <programlisting role="php"><![CDATA[
// メッセージの内容を HTML で出力します
echo '<pre>';
echo $message->getContent();
echo '</pre>';
]]>
        </programlisting>

        <para>マルチパートメッセージであるかどうかを調べるには <code>isMultipart()</code>
        メソッドを使用します。マルチパートメッセージがある場合は、
        <code>getPart()</code> メソッドで <classname>Zend_Mail_Part</classname> のインスタンスを取得します。
        <classname>Zend_Mail_Part</classname> は <classname>Zend_Mail_Message</classname> の基底クラスなので、
        <code>getHeader()</code> や <code>getHeaders()</code>、<code>getContent()</code>、
        <code>getPart()</code>、<code>isMultipart</code> といったメソッドを同様に使えます。
        また、ヘッダもプロパティとして使用できます。</para>

        <programlisting role="php"><![CDATA[
// マルチパートの最初の部分を取得します
$part = $message;
while ($part->isMultipart()) {
    $part = $message->getPart(1);
}
echo 'Type of this part is ' . strtok($part->contentType, ';') . "\n";
echo "Content:\n";
echo $part->getContent();
]]>
        </programlisting>

        <para><classname>Zend_Mail_Part</classname> は <code>RecursiveIterator</code> も実装しています。
        つまり、すべてのパートを順にスキャンすることも簡単にできます。また、
        結果を簡単に出力できるよう、マジックメソッド <code>__toString()</code>
        を実装しています。このメソッドは、パートの中身を返します。</para>

        <programlisting role="php"><![CDATA[
// 最初の text/plain パートを出力します
$foundPart = null;
foreach (new RecursiveIteratorIterator($mail->getMessage(1)) as $part) {
    try {
        if (strtok($part->contentType, ';') == 'text/plain') {
            $foundPart = $part;
            break;
        }
    } catch (Zend_Mail_Exception $e) {
        // 無視します
    }
}
if (!$foundPart) {
    echo 'プレーンテキストのパートがありません';
} else {
    echo "プレーンテキストパート: \n" . $foundPart;
}
]]>
        </programlisting>

    </sect2>
    <sect2 id="zend.mail.read-flags">
        <title>フラグのチェック</title>

        <para>Maildir および IMAP はフラグの保存をサポートしています。
        <classname>Zend_Mail_Storage</classname> クラスには、maildir や IMAP
        で使用するすべてのフラグに対応する定数が定義されています。これは
        <classname>Zend_Mail_Storage::FLAG_&lt;flagname&gt;</classname> という名前です。
        フラグをチェックするには、<classname>Zend_Mail_Message</classname>
        の <code>hasFlag()</code> メソッドを使用します。
        <code>getFlags()</code> で、設定されているすべてのフラグを取得することができます。</para>

        <programlisting role="php"><![CDATA[
// 未読メッセージを探します
echo "未読メール\n";
foreach ($mail as $message) {
    if ($message->hasFlag(Zend_Mail_Storage::FLAG_SEEN)) {
        continue;
    }
    // 新着メールのマークをつけます
    if ($message->hasFlag(Zend_Mail_Storage::FLAG_RECENT)) {
        echo '! ';
    } else {
        echo '  ';
    }
    echo $message->subject . "\n";
}


// フラグをチェックします
$flags = $message->getFlags();
echo "Message is flagged as: ";
foreach ($flags as $flag) {
    switch ($flag) {
        case Zend_Mail_Storage::FLAG_ANSWERED:
            echo '返信済み ';
            break;
        case Zend_Mail_Storage::FLAG_FLAGGED:
            echo 'フラグ設定済み ';
            break;

        // ...
        // その他のフラグのチェック
        // ...

        default:
            echo $flag . '(未知のフラグ) ';
    }
}
]]>
        </programlisting>

        <para>IMAP ではユーザやクライアントが独自にフラグを設定することができます。
        <classname>Zend_Mail_Storage</classname> で定数が定義されていない、
        このようなフラグを取得することも可能です。これらは文字列として返され、
        <code>hasFlag()</code> で同じようにチェックできます。</para>

        <programlisting role="php"><![CDATA[
// クライアントで定義したフラグ $IsSpam, $SpamTested を調べます
if (!$message->hasFlag('$SpamTested')) {
    echo 'まだスパムチェックがすんでいません';
} else if ($message->hasFlag('$IsSpam')) {
    echo 'このメッセージはスパムです';
} else {
    echo 'このメッセージはハムです (スパムではありません)';
}
]]>
        </programlisting>

    </sect2>
    <sect2 id="zend.mail.read-folders">
        <title>フォルダの使用法</title>

        <para>
            Pop3 以外のすべての保存形式は、フォルダをサポートしています。
            これはメールボックスとも言います。各保存形式で、
            フォルダをサポートするために実装しているインターフェイスが
            <classname>Zend_Mail_Storage_Folder_Interface</classname> です。
            これらすべてのクラスでは、コンストラクタで追加のオプションパラメータ
            <code>folder</code> を指定できます。これは、ログイン後に使用するフォルダを指定するものです。
        </para>
        <para>
            ローカルの保存形式では、<classname>Zend_Mail_Storage_Folder_Mbox</classname>
            あるいは <classname>Zend_Mail_Storage_Folder_Maildir</classname>
            のいずれかのクラスを使用します。どちらもパラメータ <code>dirname</code>
            が必須で、これは基底ディレクトリの名前となります。
            maildir のフォーマットは maildir++ で定義されているもの
            (デフォルトの区切り文字はドットです)、一方 Mbox
            は Mbox ファイルのディレクトリ階層を使用します。Mbox
            の基底ディレクトリに INBOX という名前の Mbox ファイルがない場合は、
            コンストラクタで別のフォルダを設定する必要があります。
        </para>
        <para>
            <classname>Zend_Mail_Storage_Imap</classname> は、デフォルトでフォルダをサポートしています。
            これらの保存形式をオープンする例を以下に示します。
        </para>

        <programlisting role="php"><![CDATA[
// mbox でフォルダを使用します
$mail = new Zend_Mail_Storage_Folder_Mbox(array('dirname' =>
                                                    '/home/test/mail/'));

// mbox で INBOX 以外のデフォルトフォルダを使用します。
// Zend_Mail_Storage_Folder_Maildir および Zend_Mail_Storage_Imap でも動作します
$mail = new Zend_Mail_Storage_Folder_Mbox(array('dirname' =>
                                                    '/home/test/mail/',
                                                'folder'  =>
                                                    'Archive'));

// maildir でフォルダを使用します
$mail = new Zend_Mail_Storage_Folder_Maildir(array('dirname' =>
                                                       '/home/test/mail/'));

// maildir で区切り文字にコロンを使用します。Maildir++ の推奨する形式です
$mail = new Zend_Mail_Storage_Folder_Maildir(array('dirname' =>
                                                       '/home/test/mail/',
                                                   'delim'   => ':'));

// imap の場合は、フォルダを使用するしないにかかわらず同じ形式です
$mail = new Zend_Mail_Storage_Imap(array('host'     => 'example.com',
                                         'user'     => 'test',
                                         'password' => 'test'));
]]>
        </programlisting>

        <para>
            getFolders($root = null) メソッドを使用すると、
            ルートフォルダあるいは指定したフォルダから始まるフォルダ階層を取得できます。
            返り値は <classname>Zend_Mail_Storage_Folder</classname> のインスタンスとなります。これは
            <code>RecursiveIterator</code> を実装しており、子要素もすべて
            <classname>Zend_Mail_Storage_Folder</classname> のインスタンスとなります。
            これらの各インスタンスはローカル名およびグローバル名を持っており、
            それぞれ <code>getLocalName()</code> メソッドおよび
            <code>getGlobalName()</code> メソッドで取得できます。
            グローバル名とはルートフォルダからの絶対名称 (区切り文字を含む) で、
            ローカル名とは親フォルダから見た名前のことです。
        </para>

        <table id="zend.mail.read-folders.table-1">
            <title>Mail フォルダの名前</title>
            <tgroup cols="2">
                <thead>
                    <row>
                        <entry>グローバル名</entry>
                        <entry>ローカル名</entry>
                    </row>
                </thead>
                <tbody>
                    <row>
                        <entry>/INBOX</entry>
                        <entry>INBOX</entry>
                    </row>
                    <row>
                        <entry>/Archive/2005</entry>
                        <entry>2005</entry>
                    </row>
                    <row>
                        <entry>List.ZF.General</entry>
                        <entry>General</entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <para>
            イテレータを使用する際は、要素のキーはローカル名となります。
            グローバル名を取得するには、マジックメソッド
            <code>__toString()</code> を使用します。
            フォルダによっては、選択できないものもあるかもしれません。
            これは、そのフォルダにメッセージを保存できず、
            メッセージを選ぼうとしてエラーになっていることを意味します。
            これを確認するためのメソッドが
            <code>isSelectable()</code> です。
            ツリー全体をビューに出力するのは、このように非常に簡単です。
        </para>

        <programlisting role="php"><![CDATA[
$folders = new RecursiveIteratorIterator($this->mail->getFolders(),
                                         RecursiveIteratorIterator::SELF_FIRST);
echo '<select name="folder">';
foreach ($folders as $localName => $folder) {
    $localName = str_pad('', $folders->getDepth(), '-', STR_PAD_LEFT) .
                 $localName;
    echo '<option';
    if (!$folder->isSelectable()) {
        echo ' disabled="disabled"';
    }
    echo ' value="' . htmlspecialchars($folder) . '">'
        . htmlspecialchars($localName) . '</option>';
}
echo '</select>';
]]>
        </programlisting>

        <para>
            現在選択されているフォルダを返すメソッドは <code>getSelectedFolder()</code>
            です。フォルダを変更するには <code>selectFolder()</code> メソッドを使用します。
            このメソッドのパラメータには、グローバル名を指定しなければなりません。
            区切り文字を書き込んでしまうことを防ぎたければ、
            <classname>Zend_Mail_Storage_Folder</classname> インスタンスのプロパティを使用します。
        </para>

        <programlisting role="php"><![CDATA[
// 選択した保存形式とその設定により、$rootFolder->Archive->2005
// は以下の内容と同等になります
//   /Archive/2005
//  Archive:2005
//  INBOX.Archive.2005
//  ...
$folder = $mail->getFolders()->Archive->2005;
echo 'Last folder was '
   . $mail->getSelectedFolder()
   . "new folder is $folder\n";
$mail->selectFolder($folder);
]]>
        </programlisting>

    </sect2>
    <sect2 id="zend.mail.read-advanced">
        <title>高度な使用法</title>

        <sect3 id="zend.mail.read-advanced.noop">
            <title>NOOP の使用</title>

            <para>
                リモートの保存形式を使用しており、何らかの事情で接続をずっと保持し続けたい場合は
                noop を使用します。
            </para>

            <programlisting role="php"><![CDATA[
foreach ($mail as $message) {

    // 何かの計算 ...

    $mail->noop(); // 接続をキープします

    // また別の処理 ...

    $mail->noop(); // 接続をキープします
}
]]>
            </programlisting>

        </sect3>
        <sect3 id="zend.mail.read-advanced.caching">
            <title>インスタンスのキャッシュ</title>

            <para>
            <classname>Zend_Mail_Storage_Mbox</classname>、<classname>Zend_Mail_Storage_Folder_Mbox</classname>、<classname>Zend_Mail_Storage_Maildir</classname>
            および <classname>Zend_Mail_Storage_Folder_Maildir</classname> は、マジックメソッド
            <code>__sleep()</code> と <code>__wakeup()</code> を実装しています。
            つまり、シリアライズが可能であるということです。
            これで、ファイルやディレクトリツリーを何度もパースする必要がなくなります。
            難点があるとすれば、Mbox や Maildir を変更することができなくなるということです。
            簡単な解決策としては、最終更新時刻が変更されたときに Mbox ファイルをパースしなおしたり、
            フォルダがなくなった場合にフォルダ構造を再パースしたり
            (これはエラーとなりますが、その後別のフォルダを検索することができます)
            といったことが考えられます。よりよい方法は、シグナルファイル的なものを用意して
            変更情報をそこに記録し、まずそれをチェックしてからキャッシュを利用するようにすることです。
            </para>

            <programlisting role="php"><![CDATA[
// ここでは、特定のキャッシュハンドラ/クラスは使用しません。
// 使用するキャッシュハンドラにあわせてコードを変更してください
$signal_file = '/home/test/.mail.last_change';
$mbox_basedir = '/home/test/mail/';
$cache_id = 'example mail cache ' . $mbox_basedir . $signal_file;

$cache = new Your_Cache_Class();
if (!$cache->isCached($cache_id) ||
    filemtime($signal_file) > $cache->getMTime($cache_id)) {
    $mail = new Zend_Mail_Storage_Folder_Pop3(array('dirname' =>
                                                        $mbox_basedir));
} else {
    $mail = $cache->get($cache_id);
}

// 何らかの処理 ...

$cache->set($cache_id, $mail);
]]>
            </programlisting>

        </sect3>
        <sect3 id="zend.mail.read-advanced.extending">
            <title>プロトコルクラスの拡張</title>

            <para>
                リモートの保存形式では、ふたつのクラス <classname>Zend_Mail_Storage_&lt;Name&gt;</classname> および
                <classname>Zend_Mail_Protocol_&lt;Name&gt;</classname> を使用しています。
                プロトコルクラスは、プロトコルのコマンドを処理して、レスポンスを
                PHP に受け渡しします。コマンドに対応したメソッド、
                さまざまなデータ構造に対応した変数を保持します。
                もう一方のメインクラスでは、共通インターフェイスを実装します。
            </para>

            <para>
                プロトコルを追加したい場合は、プロトコルクラスを継承したものを作成し、
                それをメインクラスのコンストラクタで使用します。
                例として、PHP3 接続の前に別のポートをノックしなければならないという場面を考えてみましょう。
            </para>

            <programlisting role="php"><![CDATA[
class Example_Mail_Exception extends Zend_Mail_Exception
{
}

class Example_Mail_Protocol_Exception extends Zend_Mail_Protocol_Exception
{
}

class Example_Mail_Protocol_Pop3_Knock extends Zend_Mail_Protocol_Pop3
{
    private $host, $port;

    public function __construct($host, $port = null)
    {
        // このクラスでは自動接続は行いません
        $this->host = $host;
        $this->port = $port;
    }

    public function knock($port)
    {
        $sock = @fsockopen($this->host, $port);
        if ($sock) {
            fclose($sock);
        }
    }

    public function connect($host = null, $port = null, $ssl = false)
    {
        if ($host === null) {
            $host = $this->host;
        }
        if ($port === null) {
            $port = $this->port;
        }
        parent::connect($host, $port);
    }
}

class Example_Mail_Pop3_Knock extends Zend_Mail_Storage_Pop3
{
    public function __construct(array $params)
    {
        // ... $params をここでチェックします! ...
        $protocol = new Example_Mail_Protocol_Pop3_Knock($params['host']);

        // 「特別な」処理をここでします
        foreach ((array)$params['knock_ports'] as $port) {
            $protocol->knock($port);
        }

        // 正しい状態に修正します
        $protocol->connect($params['host'], $params['port']);
        $protocol->login($params['user'], $params['password']);

        // 親を初期化します
        parent::__construct($protocol);
    }
}

$mail = new Example_Mail_Pop3_Knock(array('host'        => 'localhost',
                                          'user'        => 'test',
                                          'password'    => 'test',
                                          'knock_ports' =>
                                              array(1101, 1105, 1111)));
]]>
            </programlisting>

            <para>
                ご覧の通り、メインクラスのコンストラクタでは
                接続、ログイン、(サポートされるなら) フォルダの選択
                までを済ませているものと期待しています。
                したがって、独自のプロトコルクラスを使用する場合は、
                これらを確実に処理しておく必要があります。そうしないと、
                その後のメソッドが失敗してしまいます。
            </para>

        </sect3>
        <sect3 id="zend.mail.read-advanced.quota">
            <title>容量制限の使用 (1.5 以降)</title>

            <para>
            <classname>Zend_Mail_Storage_Writable_Maildir</classname> は Maildir++
            の容量制限をサポートしています。デフォルトではこの機能は無効になっていますが、
            手動で使用することもできます。これは、自動チェックをしたくないとき (つまり
            <code>appendMessage()</code>、<code>removeMessage()</code> および
            <code>copyMessage()</code> でチェックを行わず
            maildirsize ファイルにもエントリを追加しないとき) に使えます。
            この機能を有効にすると、容量制限に達した maildir
            に書き込もうとしたときに例外がスローされます。
            </para>

            <para>
            容量制限関連のメソッドは <code>getQuota()</code>、<code>setQuota()</code> および
            <code>checkQuota()</code> の 3 つです。
            </para>

            <programlisting role="php"><![CDATA[
$mail = new Zend_Mail_Storage_Writable_Maildir(array('dirname' =>
                                                   '/home/test/mail/'));
$mail->setQuota(true); // true で有効に、そして false で無効にします
echo 'Quota check is now ', $mail->getQuota() ? 'enabled' : 'disabled', "\n";
// チェックを無効にしている場合でも手動でのチェックは使用できます
echo 'You are ', $mail->checkQuota() ? 'over quota' : 'not over quota', "\n";
]]>
            </programlisting>

            <para>
            <code>checkQuota()</code> は、より詳細な情報も返します。
            </para>

            <programlisting role="php"><![CDATA[
$quota = $mail->checkQuota(true);
echo 'You are ', $quota['over_quota'] ? 'over quota' : 'not over quota', "\n";
echo 'You have ',
     $quota['count'],
     ' of ',
     $quota['quota']['count'],
     ' messages and use ';
echo $quota['size'], ' of ', $quota['quota']['size'], ' octets';
]]>
            </programlisting>

            <para>
            maildirsize ファイルで指定したものではなく独自の容量制限を使用したい場合は、
            <code>setQuota()</code> を使用します。
            </para>

            <programlisting role="php"><![CDATA[
// メッセージ数とオクテットサイズに対応しています。順序が重要です
$quota = $mail->setQuota(array('size' => 10000, 'count' => 100));
]]>
            </programlisting>

            <para>
            独自の容量チェックを追加するには、単一の文字をキーとして使用します。
            キーが保存されます (が、チェックはされません)。
            <classname>Zend_Mail_Storage_Writable_Maildir</classname>
            を継承して独自の容量制限 を定義することもできます。
            maildirsize ファイルが存在しないときにのみ使用します
            (Maildir++ ではこれが起こりえます)。
            </para>

            <programlisting role="php"><![CDATA[
class Example_Mail_Storage_Maildir extends Zend_Mail_Storage_Writable_Maildir {
    // getQuota は、容量チェックの際に $fromStorage = true でコールされます
    public function getQuota($fromStorage = false) {
        try {
            return parent::getQuota($fromStorage);
        } catch (Zend_Mail_Storage_Exception $e) {
            if (!$fromStorage) {
                // 未知のエラー
                throw $e;
            }
            // maildirsize ファイルが見つからないようです

            list($count, $size) = get_quota_from_somewhere_else();
            return array('count' => $count, 'size' => $size);
        }
    }
}
]]>
            </programlisting>


        </sect3>
    </sect2>
</sect1>

<!--
vim:se ts=4 sw=4 et:
-->
