Hudzilla.org - the homepage of Paul Hudson
Contents > Networks > Reading mail Wish List | Report Bug | About Me ]

15.6.3     Reading message contents: imap_num_msg() and imap_body()

This is NOT the latest copy of this book; click here for the latest version.

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:

<?php
    $imap
= imap_open("{mail.yourserver.com:143}INBOX", "username", "password");
    
$message_count = imap_num_msg($imap);
    print
$message_count;
    
imap_close($imap);
?>

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 <foo@bar.com> 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" <foo@bar.com>, the "Foo Bar" will be $header->From[0]->personal. However, many people just send their address as foo@bar.com with no personal field, which means we need to be careful when reading in the information.

Here is how it looks in PHP:

<?php
    $imap
= imap_open("{mail.yourserver.com:143}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";
    }

    
imap_close($imap);
?>

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 "foo@bar.com".

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.





<< 15.6.2 Reading message information: imap_headers() and imap_header()   15.7 Dealing with MIME-encoded messages: imap_fetchbody() >>
Table of Contents
Want to see this stuff in print? PHP in a Nutshell takes the core topics covered here, adds in thousands of edits from the editorial team and myself, and combines them to make an unbeatable reference for PHP programmers at all levels.



My latest book has hundreds more tips on how to use PHP, Apache, and MySQL, plus Perl, Python, shell scripts, performance tuning, and more!



Top-right shadow
 
Bottom-left shadow Bottom shadow

Comments from other readers
A PHP User - 13 Oct 2008

after imap_body() messages are not marking as read. do you know what is the reason?

A PHP User - 13 Oct 2008

The &lt; shouldn't be neccessary but is a very good idea.
The $header->from[0]->mailbox is encompassed in curly brackets ensuring PHP parses this and should ignore the greater-than sign treating it as a standard character.

If replacing < with &lt;, you may as well go all the way and use...
$email = "$personal &lt;{$header->from[0]->mailbox}@{$header->from[0]->host}&gt;";
or, I would personally use..
$email = $personal.' &lt;'.$header->from[0]->mailbox.'@'.$header->from[0]->host.'&gt;';

J.S.

nick@mr-nick.co.uk - 13 Oct 2008

Is the following line...

$email = "$personal <{$header->from[0]->mailbox}@{$header->from[0]->host}>";

....supposed to show..

Personal Name <email@address.com> ?


The left angle bracket should be represented by &lt;


Hopefully I have not misunderstood and this helps someone.



Add comment
Please note that by posting a comment here you are committing it to the public domain. This is important so that others can make use of your code themselves, and also so that I can incorporate helpful notes directly into the main text. Comments are limited to 2000 characters in length.

If you are reporting an error in the content, please tell me directly.

Your name/email address:
Your comment:
 
Now, in order to verify that you're a real person, please answer this simple question: what is seven plus nine?
The answer is:
(please write in
numbers, eg 19)


Top-right shadow
 
Bottom-left shadow Bottom shadow