Reading message contents

int imap_num_msg ( resource imap_stream)

string imap_body ( resource imap_stream, int msg_number, int options)

Now you have an idea what imap_header() returns, it is time to get down to business and use it. The script we are going to create loops through each message in the inbox, printing out the first 100 characters of body text for each message as it goes. To do this we are going to enlist the help of two new functions: imap_num_msg() and imap_body(), which return the number of messages in the mail box and the contents of a specific message respectively.

To get the number of messages in a mail box, pass your IMAP stream into imap_num_msg() and store the return value, like this:

    $imap = imap_open("{}INBOX", "username", "password");
    $message_count = imap_num_msg($imap);
    print $message_count;

As it is now possible to check the number of messages in a given inbox, we can loop through them all using imap_header() on each one. For the sake of this test we will print each message out like this:

On 22nd April 2004, FooBar <> said, "first 100 characters of message here".\n

To accomplish this, we need to write code along these lines:

  • Open mail box, get number of messages

  • For 0 to number of messages, get header and get body

  • Trim body to a maximum of 100 characters

  • Format message date to the format "jS F Y"

  • Print out the data

In code, there is one minor drawback to this implementation, and that is that many people don't have a "personal" from address. In the email "Foo Bar" <>, the "Foo Bar" will be $header->From[0]->personal. However, many people just send their address as with no personal field, which means we need to be careful when reading in the information.

Here is how it looks in PHP:

    $imap = imap_open("{}INBOX", "username", "password");
    $message_count = imap_num_msg($imap);

    for ($i = 1; $i <= $message_count; ++$i) {
        $header = imap_header($imap, $i);
        $body = trim(substr(imap_body($imap, $i), 0, 100));
        $prettydate = date("jS F Y", $header->udate);

        if (isset($header->from[0]->personal)) {
            $personal = $header->from[0]->personal;
        } else {
            $personal = $header->from[0]->mailbox;

        $email = "$personal <{$header->from[0]->mailbox}@{$header->from[0]->host}>";
        echo "On $prettydate, $email said \"$body\".\n";


Note that I strongly suggest you change the starting value of $i to somewhere near your current message count, as running that script would take an exceptionally long time to execute if you have a large inbox - I will explain why shortly.

First, our IMAP stream is opened as usual, then we grab the number of messages currently in there and store it in $message_count. This is then used as our maximum loop value - note that we count from 1 to $message_count inclusive because the IMAP sequence numbering is 1-based, which means it counts from 1 up rather than 0 up.

Next, the header information for the current message is grabbed and stored in $header, and the body text is also grabbed and placed in $body. Note that the body text is cut to 100 characters then trimmed.

The udate field in the header is our Unix date, so that is formatted to the required layout. Then comes the from[0]->personal issue - if you recall I mentioned that not everyone sends a personal name in their From header, so this field might not be set for everyone. To get around this, isset() is used to check whether the variable exists, and, if it does, it is placed into the $personal variable. If not, the sender's mail box name is used instead, which would be the "foo" in "".

Finally, all the information is printed out, the loop continues until it hits the end of the mail box, then the IMAP stream is closed and the script is complete.

Now, if only 100 characters from the body is used, why is the script slow to execute? The problem is that imap_body() returns the entire body of each message, of which we use just 100 characters. So, if someone sent you a 7MB file attached to an email, imap_body() would download all of it then throw away 99% percent of it. Worse, what we do get in the meagre 1% we read is likely to be all sorts of MIME gibberish such as "----=_NextPart_000_0022_01C3EC8E.3DBDDCB0". Dealing with this involves really getting into the guts of MIME messages, so, if you think you have had enough - the script we just produced works fine for plain text messages - I suggest you stop here and skip on ahead.

Author's Note: You can pass FT_PEEK as the third parameter to imap_body() to have it not set the read flag to true for the selected message. Without this, any message retrieved using imap_body() is marked as read.


Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Dealing with MIME-encoded messages >>

Previous chapter: Reading message information

Jump to:


Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.