Let's create a Gmail email client using PHP's native IMAP library.
For this, you need the php-imap
installed.
Despite the name, the IMAP library works with POP3 too.
// gmail-client.php
/** Connect to GMail. */
$username = '[email protected]';
$password = '34LWD12';
$str_conn = '{imap.gmail.com:993/ssl/novalidate-cert}INBOX';
$conn = imap_open($str_conn, $username, $password);
/** Retrieve and display all messages. */
$msgs = imap_fetch_overview($conn, '1:*');
print_r($msgs);
Array
(
[0] => stdClass Object
(
[subject] => Test
[from] => John Snow <john@snow.com>
[to] => Daniel Loureiro <daniel@learnwithdaniel.com>
[date] => Fri, 12 Jul 2019 13:04:12 -0400
[message_id] => <CABgjj39-BMb_NPaCPmh+ds7zsOzTCrJfRotaB4CZ1Yuv=mLxkA@mail.gmail.com>
[size] => 4691
[uid] => 3
[msgno] => 1
[recent] => 0
[flagged] => 0
[answered] => 1
[deleted] => 0
[seen] => 1
[draft] => 0
[udate] => 1562951089
)
)
That's it!
The previous example connects through IMAP. If you prefer POP3, use this:
$str_conn = '{pop.gmail.com:995/pop3/ssl/novalidate-cert}INBOX';
msgno
Each message has a msgno
, which is a sequential number. The very first message is 1
. This number is unique for your mailbox. Even if you delete a message, its msgno
won't be reused.
The msgno
is used in many commands.
If you get an "invalid credentials" error, you may have to enable the "Allow Less Secure Apps" option: https://myaccount.google.com/lesssecureapps (opens new window)
If you get this email, you need to click on it and authorize access:
Then, enable "Less Secure Apps" (or just use an "app password"):
As we saw previously, that's how we get the messages:
// gmail-client.php
$msgs = imap_fetch_overview($conn, $range);
$range
format is: {msgno_from}:{msgno_to}
. The msgno_from
and msgno_to
are included in the response.
Range examples:
Use the range argument to create savepoints, i.e. if you want to download only the new messages and not the whole inbox each time your app syncs with Gmail. To create savepoints, download all messages (1:*
) on the first time and save the last message number ("msgno"), ex. 5
. Next time, you check from that message and beyond (6:*
).
The "From" and "To" fields support a display name in addition to the email address:
[email protected]
John Snow <[email protected]>
You should close the email connection after using it, which is usually at the end of your script.
If you forget to close, PHP will automatically close it for you when the script ends, but it is a good idea to explicitly close it with:
imap_close($conn);
To get an array with past errors, use:
$errors = imap_errors();
Return example:
[0 => '[TRYCREATE] No folder {imap.gmail.com} (Failure)']
To get only the last error, use imap_last_error()
.
WARNING
If you don't retrieve errors by calling these functions, then all errors and warnings will be thrown when the connection gets closed.
Each message has a message_id
, which is a universal unique identifier. Do not confuse with msgno
, which is a local id, valid only for your inbox.
To reply to messages, you need to set 2 things:
message_id
of the message you are replying to in two header items: In-Reply-To
and References
;{$subject}
or Re: {$subject}
or Reply to {$subject}
. It actually doesn't matter if you prefix with something (like "Re:") or not, the only rule is that you need to end the subject with the original copy;If you are curious of how a replied message looks like when retrieving it:
[1] => stdClass Object
(
[subject] => Re: Test
[from] => john@snow.com
[to] => Daniel Loureiro <daniel@learnwithdaniel.com>
[date] => Fri, 12 Jul 2019 13:04:12 -0400
[message_id] => <A0C26432-DD82-411A-AD61-2FB12E65C4D1@learnwithdaniel.com>
[references] => <CABgjj39-BMb_NPaCPmh+ds7zsOzTCrJfRotaB4CZ1Yuv=mLxkA@mail.gmail.com>
[in_reply_to] => <CABgjj39-BMb_NPaCPmh+ds7zsOzTCrJfRotaB4CZ1Yuv=mLxkA@mail.gmail.com>
[size] => 4691
[uid] => 3
[msgno] => 2
[recent] => 0
[flagged] => 0
[answered] => 1
[deleted] => 0
[seen] => 1
[draft] => 0
[udate] => 1562951089
)
Notice subject
, references
and the in_reply_to
fields.
You can reply to the email address specified in the "From" field, but if the sender has set a "reply-to", your message will go the wrong place.
To reply properly, you should get the headers with imap_headerinfo($hnd, $msgno)
and use the reply_toaddress
property (a string). Alternatively, you can use the reply_to
property, which returns the email parsed as domain, account and display_name.
It is safe to always use the "reply_toaddress" property (or the "reply_to") even if the sender hasn't set a reply-to. In these cases, "reply_toaddress" will return the same value as "from".
$headers = imap_headerinfo($hnd, $msgno);
$reply_addr = $headers->reply_toaddress;