Read latest Email (Gmail) by PHP IMAP Client and detect a verification code from a specified email

fmchanprogrammingLeave a Comment

First we have to allow third parties to access your gmail.

https://myaccount.google.com/

Even I turn on “Less secure app access”, it still doesn’t work.

Workable solution is to turn on 2-step verification for your google account then set an app password, which you can find the config on the section “Signing in to Google”

Secondly, go to https://mail.google.com and click the setting icon then see all settings. press Forwarding and POP/IMAP on the top horizontal menu, click “Enable IMAP”

Gmail is ready for you to access, let’s code it!

composer require php-mail-client/client
composer require ssilence/php-imap-client

Before we start, we download the above libraries using composer then we try to connect.

use SSilence\ImapClient\ImapClient as Imap;
use SSilence\ImapClient\ImapClientException;

try {
    $imap = new Imap("imap.gmail.com", "yourname@gmail.com", "app-password", Imap::ENCRYPT_SSL);
} catch (ImapClientException $error) {
    throw new InstagramAuthException('Login error. Cannot login to imap server: ' . $error->getMessage());
}

Try to get the count of unread messages in INBOX folder.

$imap->selectFolder("INBOX");
try {
    $unreadMessages = $imap->countUnreadMessages();
    var_dump('countUnreadMessages: ' . $unreadMessages);
} catch (ImapClientException $e) {
    var_dump($e);
}

Try to search Criteria: UNSEEN, on today, from a specified email using imap_search and form an array storing the mails and their IDs.

$date = date("j F Y");
var_dump($date);
$result = imap_search($imap->getImap(), 'UNSEEN FROM "xxx@mail.xxx.com" ON "'.$date.'"');
$countId = count($result);
var_dump('countId: ' . $countId);
foreach($result as $key=>$id) {
    //var_dump($id);
    $emails[]= ['email'=>$imap->getMessage($id), 'id'=>$id];
}
var_dump($emails);

Why do we get the ID? because we can set the email to be read.

$imap->setSeenMessage($id);

Here I have a completed source code to keep checking the email and stop once I receive a special email with a verification code inside the email.

<?php
require __DIR__ . '/vendor/autoload.php';

use SSilence\ImapClient\ImapClient as Imap;
use SSilence\ImapClient\ImapClientException;

const MAIL_MAX_WAIT_TIME = 600;
const MAIL_WAIT_STEP_TIME = 10;

try {
    $imap = new Imap("imap.gmail.com", "user@gmail.com", "app-password", Imap::ENCRYPT_SSL);
    echo keepChecking($imap);
} catch (ImapClientException $error) {
    throw new InstagramAuthException('Login error. Cannot login to imap server: ' . $error->getMessage());
}
function keepChecking($imap) {
    $startTime = time();
    while (time() - $startTime < MAIL_MAX_WAIT_TIME) {
        sleep(mt_rand(60,MAIL_WAIT_STEP_TIME*60)/60);
        try {
            $code = getCodeFromEmail($imap);
        } catch (ImapClientException $e) {
            break;
        }
        if ($code) return $code;
        sleep(MAIL_WAIT_STEP_TIME);
    }
    throw new InstagramAuthException('Login error. Verification email read timeout.');
}

function searchEmail($imap) {
    $date = date("j F Y");
    var_dump($date);
    $result = imap_search($imap->getImap(), 'UNSEEN FROM "from@mail.com" ON "'.$date.'"');
    $countId = count($result);
    var_dump('countId: ' . $countId);
    foreach($result as $key=>$id) {
        //var_dump($id);
        $emails[]= ['email'=>$imap->getMessage($id), 'id'=>$id];
    }
    return array_reverse($emails); //desc
}

function getCodeFromEmail($imap) {
    $imap->selectFolder("INBOX");
    try {
        $unreadMessages = $imap->countUnreadMessages();
        var_dump('countUnreadMessages: ' . $unreadMessages);
        $code = false;
        if ($unreadMessages) {
            $emails = searchEmail($imap);
            foreach ($emails as $mailWrap) {
                $mail = $mailWrap['email'];
                echo "{$mailWrap['id']} [{$mail->header->date}] ({$mail->header->from}) {$mail->header->subject}\n";
                if ($mail->header->from !== 'Demo <from@mail.com>') {
                    continue;
                }
                $message = json_encode($mail->message);
                if (preg_match('/>(\d{6})</', $message, $matches)) {
                    $code = $matches[1];
                    var_dump($code);
                    $imap->setSeenMessage($mailWrap['id']);
                    return $code;
                }

            }
        }
    } catch (ImapClientException $e) {
        var_dump($e);
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *