Exploring the PHP IMAP Library, Part 2

Share this article

In the first part of this series I discussed how to connect to IMAP servers using the PHP IMAP extension. In this part we’ll complete the series by discussing working with folders and reading email content. Let’s get started!

Working with Email Flags

A list of flags are usually associated with each message: unread, replied, flagged, draft, etc. We can check the message’s Unseen property to identify whether it has been read or not. If the message has been viewed then we’ll get the value “U”. So going back to the code from part one, let’s modify it to show the read/unread status.
<?php
$numMessages = imap_num_msg($imap);
for ($i = $numMessages; $i > ($numMessages - 20); $i--) {
    $header = imap_header($imap, $i);

    ...

    $uid = imap_uid($imap, $i);
    $class = ($header->Unseen == "U") ? "unreadMsg" : "readMsg";

    echo "<ul class="' . $class . '">';
    echo "<li><strong>From:</strong>" . $details["fromName"];
    echo " " . $details["fromAddr"] . "</li>";
    echo "<li><strong>Subject:</strong> " . $details["subject"] . "</li>";
    echo '<li><a href="mail.php?folder=' . $folder . '&uid=' . $uid . '&func=read">Read</a>';
    echo " | ";
    echo '<a href="mail.php?folder=' . $folder . '&uid=' . $uid . '&func=delete">Delete</a></li>';
    echo "</ul>";
}
We can check the status of the Unseen property assign different CSS classes to the markup which can show the read/unread email information differently.
.unreadMsg {
    color: #000;
    font-weight: bold;
}

.readMsg {
    color: #999;
}
We can also create special flags on emails. For example, let’s say we wanted to mark a message as starred. For this we use the Flagged property which will have the value “F” if the message is flagged.
<?php
if ($header->Flagged == "F") {
    $class .= " flaggedMsg";
}
To set flags on a message, we use the impa_setflag_full() function.
<?php
$status = imap_setflag_full($imap, $uid, "\Seen \Flagged", ST_UID);
The above code marks the message as “Read(\Seen)” and sets the flagged status to “F”. I always prefer using a UID instead of email sequence number as the second parameter, so I must set the optional fourth parameter with the constant ST_UID. You can provide other flags, such as Draft, Deleted, and Answered with this function too. Although I have set the flags on just a single message, you can provide a range like “1,10” as the second parameter for setting flags on multiple messages if you’d like.

Deleting Email Messages

The imap_delete() function is used to delete messages. It marks them for deletion, but doesn’t actually remove them from your account. The imap_expunge() function is responsible for actually deleting the marked messages.
<?php
imap_delete($imap, $uid, FT_UID);
imap_expunge($imap);
I’ve called the delete function with a UID instead of sequence number. Otherwise, I risk losing important messages because of changes in sequence numbers (remember from part one that sequence numbers are not unique).

Viewing Email Attachments

Next to reading and sending emails, working with email attachments is probably the next most important feature of an email client. We’ll focus on checking for email attachments, displaying them, and downloading them. There are various methods for reading the structure of a message and identifying attachments. The library mentioned in the first part, the Receive Mail class developed by Mitul Koradia, has features for downloading them as well. But here I’ll use functions included in the comments section for the imap_fetchstructure() function which I think is the easiest way. Before taking a look at some code, I’d like to show you the structure of an email with attachments as returned by imap_fetchstructure().
stdClass Object
(
  [type] => 1
  [encoding] => 0
  [ifsubtype] => 1
  [subtype] => MIXED
  [ifdescription] => 0
  [ifid] => 0
  [ifdisposition] => 0
  [ifdparameters] => 0
  [ifparameters] => 1
  [parameters] => Array
    (
      [0] => stdClass Object
        (
            [attribute] => BOUNDARY
            [value] => bcaec54b516462cef304c7e9d5c3
        )
    )
  [parts] => Array
    (
      [0] => stdClass Object
        (
          [type] => 1
          [encoding] => 0
          [ifsubtype] => 1
          [subtype] => ALTERNATIVE
          [ifdescription] => 0
          [ifid] => 0
          [ifdisposition] => 0
          [ifdparameters] => 0
          [ifparameters] => 1
          [parameters] => Array
            (
              [0] => stdClass Object
                (
                  [attribute] => BOUNDARY
                  [value] => bcaec54b516462ceeb04c7e9d5c1
                )
            )
          [parts] => Array
            (
              [0] => stdClass Object
                (
                  [type] => 0
                  [encoding] => 0
                  [ifsubtype] => 1
                  [subtype] => PLAIN
                  [ifdescription] => 0
                  [ifid] => 0
                  [lines] => 1
                  [bytes] => 2
                  [ifdisposition] => 0
                  [ifdparameters] => 0
                  [ifparameters] => 1
                  [parameters] => Array
                    (
                      [0] => stdClass Object
                        (
                          [attribute] => CHARSET
                          [value] => ISO-8859-1
                        )
                    )
                )
              [1] => stdClass Object
                (
                  [type] => 0
                  [encoding] => 0
                  [ifsubtype] => 1
                  [subtype] => HTML
                  [ifdescription] => 0
                  [ifid] => 0
                  [lines] => 1
                  [bytes] => 6
                  [ifdisposition] => 0
                  [ifdparameters] => 0
                  [ifparameters] => 1
                  [parameters] => Array
                    (
                      [0] => stdClass Object
                        (
                          [attribute] => CHARSET
                          [value] => ISO-8859-1
                        )
                    )
                )
            )
        )
      [1] => stdClass Object
        (
          [type] => 3
          [encoding] => 3
          [ifsubtype] => 1
          [subtype] => ZIP
          [ifdescription] => 0
          [ifid] => 0
          [bytes] => 115464
          [ifdisposition] => 1
          [disposition] => ATTACHMENT
          [ifdparameters] => 1
          [dparameters] => Array
            (
              [0] => stdClass Object
                (
                  [attribute] => FILENAME
                  [value] => weekly-reports.zip
                )
            )
          [ifparameters] => 1
          [parameters] => Array
            (
              [0] => stdClass Object
                (
                  [attribute] => NAME
                  [value] => weekly-reports.zip
                )
            )
        )
    )
)
If you look carefully at the structure, you’ll see the attachment as the part with disposition equal to “ATTACHMENT”. This mail has 1 attachment, but it is entirely possible to have multiple attachments and thus multiple parts with “ATTACHMENT”. We can easily identify attachments by checking this parameter.
<?php
$mailStruct = imap_fetchstructure($imap, $i);
$attachments = getAttachments($imap, $i, $mailStruct, "");
Inside the viewMailbox() function I have included the above lines. First we get the structure of each mail using imap_fetchstructure() function. It will return an object like the one shown previously. Then we call the getAttachments() function, which will provide the attachment details.
<?php
function getAttachments($imap, $mailNum, $part, $partNum) {
    $attachments = array();

    if (isset($part->parts)) {
        foreach ($part->parts as $key => $subpart) {
            if($partNum != "") {
                $newPartNum = $partNum . "." . ($key + 1);
            }
            else {
                $newPartNum = ($key+1);
            }
            $result = getAttachments($imap, $mailNum, $subpart,
                $newPartNum);
            if (count($result) != 0) {
                 array_push($attachments, $result);
             }
        }
    }
    else if (isset($part->disposition)) {
        if ($part->disposition == "ATTACHMENT") {
            $partStruct = imap_bodystruct($imap, $mailNum,
                $partNum);
            $attachmentDetails = array(
                "name"    => $part->dparameters[0]->value,
                "partNum" => $partNum,
                "enc"     => $partStruct->encoding
            );
            return $attachmentDetails;
        }
    }

    return $attachments;
}
First we check whether parts are set for the current email and then we have to traverse through each part recursively. We have to modify the part number and pass it to the recursive call. As you can see, subpart numbers are broken up into dotted segments. If you have 3 levels of a part number, it would be something like 1.0.1. When further parts are not available, we check whether the disposition parameter is available and whether its value is “ATTACHMENT”. In such situations we get the structure of the given part using imap_bodystruct(). Both imap_bodystruct() and imap_fetchstructure()
provides the same output. The only difference between the two is that we can use imap_bodystruct() to get specific part information instead of the entire structure. Now we have the list of attachment details for the given email and we loop through all the attachments and display download links for them:
<?php
echo "Attachments: ";
foreach ($attachments as $attachment) {

echo '<a href="mail.php?func=' . $func . '&folder=' . $folder . '&uid=' . $uid .
    '&part=' . $attachment["partNum"] . '&enc=' . $attachment["enc"] . '">' .
    $attachment["name"] . "</a>";
}

Downloading Attachments

To download an attachment, we need the email’s UID, the part number, and the encoding type of the attachment. I’ve included those parameters in the download link created above. Once the link is clicked, the following function can be invoked:
<?php
function downloadAttachment($imap, $uid, $partNum, $encoding, $path) {
    $partStruct = imap_bodystruct($imap, imap_msgno($imap, $uid), $partNum);

    $filename = $partStruct->dparameters[0]->value;
    $message = imap_fetchbody($imap, $uid, $partNum, FT_UID);

    switch ($encoding) {
        case 0:
        case 1:
            $message = imap_8bit($message);
            break;
        case 2:
            $message = imap_binary($message);
            break;
        case 3:
            $message = imap_base64($message);
            break;
        case 4:
            $message = quoted_printable_decode($message);
            break;
    }

    header("Content-Description: File Transfer");
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename=" . $filename);
    header("Content-Transfer-Encoding: binary");
    header("Expires: 0");
    header("Cache-Control: must-revalidate");
    header("Pragma: public");
    echo $message;
}
First we need to get the structure of the given part to identify the attachment name, which is done with imap_bodystruct(). You can see that I used imap_msgno() to get the sequence number from the UID; this is because imap_bodystruct() doesn’t accept a UID so we have to convert the UID into the sequence number. Next we get the attachment content using imap_fetchbody(). It will only receive the contents of the given part number. Then we use the appropriate content decoding function according to the encoding type of the given attachment to decode it. Finally, we output the attachment content with appropriate headers so the browser will download the file.

Summary

We’ve completed our look at PHP’s IMAP functions and you should now have an understanding sufficient enough to put together a simple working email reader. Be sure to learn about the other functions that are available and explore them as well to broaden your understanding. Image via Fotolia

Frequently Asked Questions (FAQs) about PHP’s IMAP Library

How can I install the IMAP extension in PHP on Windows?

To install the IMAP extension in PHP on Windows, you need to follow these steps. First, locate your PHP installation folder. Then, find the php.ini file and open it in a text editor. Look for the line that says “;extension=imap”. Remove the semicolon at the beginning of the line to uncomment it, which will enable the extension. Save the changes and restart your server. The IMAP extension should now be installed and ready to use.

How can I download attachments using IMAP in PHP?

To download attachments using IMAP in PHP, you need to use the imap_fetchstructure function to get the structure of the message, and then use the imap_fetchbody function to fetch the body of the message. You can then save the body of the message to a file, which will be the downloaded attachment.

What is the purpose of the imap_fetchbody function in PHP?

The imap_fetchbody function in PHP is used to fetch a particular section of the body of the message. This function is useful when you want to retrieve a specific part of the message, such as an attachment.

What is the purpose of the imap_fetchstructure function in PHP?

The imap_fetchstructure function in PHP is used to retrieve the structure of a message. This function returns an object that describes the structure of the message, including the type of the message, the encoding of the message, and any parts the message may have.

How can I use the IMAP functions in PHP to interact with an email server?

To use the IMAP functions in PHP to interact with an email server, you first need to open a connection to the server using the imap_open function. Once the connection is open, you can use the other IMAP functions to interact with the server, such as fetching messages, downloading attachments, and sending messages.

How can I handle errors when using the IMAP functions in PHP?

When using the IMAP functions in PHP, errors can be handled using the imap_errors function. This function returns an array of all the errors that have occurred during the current page request. You can then loop through this array to handle each error individually.

How can I search for specific messages using the IMAP functions in PHP?

To search for specific messages using the IMAP functions in PHP, you can use the imap_search function. This function takes a search criteria as a string and returns an array of message numbers that match the criteria.

How can I delete messages using the IMAP functions in PHP?

To delete messages using the IMAP functions in PHP, you can use the imap_delete function. This function marks a message for deletion. To permanently delete the message, you need to use the imap_expunge function, which deletes all messages marked for deletion.

How can I move messages to a different mailbox using the IMAP functions in PHP?

To move messages to a different mailbox using the IMAP functions in PHP, you can use the imap_mail_move function. This function takes a message number and a mailbox name as parameters and moves the message to the specified mailbox.

How can I fetch the headers of a message using the IMAP functions in PHP?

To fetch the headers of a message using the IMAP functions in PHP, you can use the imap_headerinfo function. This function returns an object that contains information about the headers of the message, such as the subject, the sender, and the recipient.

Rakhitha NimeshRakhitha Nimesh
View Author

Rakhitha Nimesh is a software engineer and writer from Sri Lanka. He likes to develop applications and write on latest technologies. He is available for freelance writing and WordPress development. You can read his latest book on Building Impressive Presentations with Impress.js. He is a regular contributor to 1stWebDesigner, Tuts+ network and SitePoint network. Make sure to follow him on Google+.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week