23

I have donwloaded new Gmail API source code from Google PHP client library.

I inititalized the service using:

set_include_path("./google-api-php-client-master/src/".PATH_SEPARATOR.get_include_path());
    
require_once 'Google/Client.php';   
require_once 'Google/Service/Gmail.php';

$client = new Google_Client();
$client->setClientId($this->config->item('gmailapi_clientid'));
$client->setClientSecret($this->config->item('gmailapi_clientsecret'));
$client->setRedirectUri(base_url('auth'));
$client->addScope('email');
//$client->addScope('profile');     
$client->addScope('https://mail.google.com');           
$client->setAccessType('offline');

$gmailService = new Google_Service_Gmail($client);

What should I do next? How to read Gmail messages using Gmail API PHP library?

Blaztix
  • 1,223
  • 1
  • 19
  • 28
Anandhan
  • 422
  • 5
  • 12
  • 22

5 Answers5

25

For the sake of demonstration, you can do something like this:

$optParams = [];
$optParams['maxResults'] = 5; // Return Only 5 Messages
$optParams['labelIds'] = 'INBOX'; // Only show messages in Inbox
$messages = $service->users_messages->listUsersMessages('me',$optParams);
$list = $messages->getMessages();
$messageId = $list[0]->getId(); // Grab first Message


$optParamsGet = [];
$optParamsGet['format'] = 'full'; // Display message in payload
$message = $service->users_messages->get('me',$messageId,$optParamsGet);
$messagePayload = $message->getPayload();
$headers = $message->getPayload()->getHeaders();
$parts = $message->getPayload()->getParts();

$body = $parts[0]['body'];
$rawData = $body->data;
$sanitizedData = strtr($rawData,'-_', '+/');
$decodedMessage = base64_decode($sanitizedData);

var_dump($decodedMessage);
Blaztix
  • 1,223
  • 1
  • 19
  • 28
Muffy
  • 411
  • 3
  • 6
  • Thanks lot. is possible to get message details using listUersMessages() function. Because its very slow when loading first twenty messages with message details – Anandhan Jul 07 '14 at 03:54
  • 1
    Thanks a lot, it miss me sanitizedData can you explain why we must do that ? – Jean-Luc Barat Feb 22 '17 at 15:49
  • 1
    @Jean-LucBarat, I found a better explanation than what I would have come up with. Credit goes to joesmo There are additional base64 specs. But essentially you need 65 chars to encode: 26 lowercase + 26 uppercase + 10 digits = 62. You need two more ['+', '/'] and a padding char '='. But none of them are url friendly, so just use different chars for them and you're set. The standard ones from the chart above are ['-', '_'], but you could use other chars as long as you decoded them the same, and didn't need to share with others. – Muffy Mar 01 '17 at 10:35
  • Thanks nice explanation. – Jean-Luc Barat Mar 02 '17 at 13:06
  • Oooh!!! Thank you! But i don't understand. Why `$sanitizedData = strtr($rawData,'-_', '+/');` is not included in php client module? – LINKeRxUA Mar 15 '17 at 12:31
  • It is not included in the PHP Client Module. I may be wrong though but will verify. – Muffy Mar 25 '17 at 18:30
  • $body = $parts[0]['body']; this line is throwing error. What does it mean? Notice: Undefined offset: 0 in D:\xampp\htdocs\gmailAPI\quickstart.php on line 89 – Sunil Kumar Oct 29 '18 at 05:27
12

This is the full function, You can use it because It worked fine.

session_start();
$this->load->library('google');
$client = new Google_Client();
$client->setApplicationName('API Project');
$client->setScopes(implode(' ', array(Google_Service_Gmail::GMAIL_READONLY)));
//Web Applicaion (json)
$client->setAuthConfigFile('key/client_secret_105219sfdf2456244-bi3lasgl0qbgu5hgedg9adsdfvqmds5c0rkll.apps.googleusercontent.com.json');

$client->setAccessType('offline');       

// Redirect the URL after OAuth
if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']);
    $_SESSION['access_token'] = $client->getAccessToken();
    $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}

// If Access Toket is not set, show the OAuth URL
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
    $client->setAccessToken($_SESSION['access_token']);
} else {
    $authUrl = $client->createAuthUrl();
}

if ($client->getAccessToken()) {

    $_SESSION['access_token'] = $client->getAccessToken();
 
    // Prepare the message in message/rfc822
    try {
 
        // The message needs to be encoded in Base64URL

        $service = new Google_Service_Gmail($client);

        $optParams = [];
        $optParams['maxResults'] = 5; // Return Only 5 Messages
        $optParams['labelIds'] = 'INBOX'; // Only show messages in Inbox
        $messages = $service->users_messages->listUsersMessages('me',$optParams);
        $list = $messages->getMessages();
        $messageId = $list[0]->getId(); // Grab first Message


        $optParamsGet = [];
        $optParamsGet['format'] = 'full'; // Display message in payload
        $message = $service->users_messages->get('me',$messageId,$optParamsGet);
        $messagePayload = $message->getPayload();
        $headers = $message->getPayload()->getHeaders();
        $parts = $message->getPayload()->getParts();

        $body = $parts[0]['body'];
        $rawData = $body->data;
        $sanitizedData = strtr($rawData,'-_', '+/');
        $decodedMessage = base64_decode($sanitizedData);

        var_dump($decodedMessage);
 
    } catch (Exception $e) {
        print($e->getMessage());
        unset($_SESSION['access_token']);
    }
 
}

// If there is no access token, there will show url
if ( isset ( $authUrl ) ) { 
    echo $authUrl;
}
Blaztix
  • 1,223
  • 1
  • 19
  • 28
Samphors
  • 530
  • 9
  • 24
3

This is the sample code I used to develop an email ticketing system. It shows how to retrieve labels, messages, and headers.

<?php

require_once __DIR__ . '/vendor/autoload.php';

define('APPLICATION_NAME', 'Gmail API PHP Quickstart');
define('CREDENTIALS_PATH', '~/.credentials/gmail-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/client_secret.json');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-php-quickstart.json
define('SCOPES', implode(' ', array(
  Google_Service_Gmail::GMAIL_READONLY)
));

if (php_sapi_name() != 'cli') {
  throw new Exception('This application must be run on the command line.');
}

/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient() {
  $client = new Google_Client();
  $client->setApplicationName(APPLICATION_NAME);
  $client->setScopes(SCOPES);
  $client->setAuthConfig(CLIENT_SECRET_PATH);
  $client->setAccessType('offline');

  // Load previously authorized credentials from a file.
  $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
  if (file_exists($credentialsPath)) {
    $accessToken = json_decode(file_get_contents($credentialsPath), true);
  } else {
    // Request authorization from the user.
    $authUrl = $client->createAuthUrl();
    printf("Open the following link in your browser:\n%s\n", $authUrl);
    print 'Enter verification code: ';
    $authCode = trim(fgets(STDIN));

    // Exchange authorization code for an access token.
    $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) {
      mkdir(dirname($credentialsPath), 0700, true);
    }
    file_put_contents($credentialsPath, json_encode($accessToken));
    printf("Credentials saved to %s\n", $credentialsPath);
  }
  $client->setAccessToken($accessToken);

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
  }
  return $client;
}

/**
 * Expands the home directory alias '~' to the full path.
 * @param string $path the path to expand.
 * @return string the expanded path.
 */
function expandHomeDirectory($path) {
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) {
    $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
  }
  return str_replace('~', realpath($homeDirectory), $path);
}

/**
 * Get list of Messages in user's mailbox.
 *
 * @param  Google_Service_Gmail $service Authorized Gmail API instance.
 * @param  string $userId User's email address. The special value 'me'
 * can be used to indicate the authenticated user.
 * @return array Array of Messages.
 */
function listMessages($service, $userId, $optArr = []) {
  $pageToken = NULL;
  $messages = array();
  do {
    try {
      if ($pageToken) {
        $optArr['pageToken'] = $pageToken;
      }
      $messagesResponse = $service->users_messages->listUsersMessages($userId, $optArr);
      if ($messagesResponse->getMessages()) {
        $messages = array_merge($messages, $messagesResponse->getMessages());
        $pageToken = $messagesResponse->getNextPageToken();
      }
    } catch (Exception $e) {
      print 'An error occurred: ' . $e->getMessage();
    }
  } while ($pageToken);

  return $messages;
}

function getHeaderArr($dataArr) {
    $outArr = [];
    foreach ($dataArr as $key => $val) {
        $outArr[$val->name] = $val->value;
    }
    return $outArr;
}

function getBody($dataArr) {
    $outArr = [];
    foreach ($dataArr as $key => $val) {
        $outArr[] = base64url_decode($val->getBody()->getData());
        break; // we are only interested in $dataArr[0]. Because $dataArr[1] is in HTML.
    }
    return $outArr;
}

function base64url_decode($data) {
  return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}

function getMessage($service, $userId, $messageId) {
  try {
    $message = $service->users_messages->get($userId, $messageId);
    print 'Message with ID: ' . $message->getId() . ' retrieved.' . "\n";

    return $message;
  } catch (Exception $e) {
    print 'An error occurred: ' . $e->getMessage();
  }
}

function listLabels($service, $userId, $optArr = []) {
    $results = $service->users_labels->listUsersLabels($userId);

    if (count($results->getLabels()) == 0) {
      print "No labels found.\n";
    } else {
      print "Labels:\n";
      foreach ($results->getLabels() as $label) {
        printf("- %s\n", $label->getName());
      }
    }
}

// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
$user = 'me';

// Print the labels in the user's account.
listLabels($service, $user);

// Get the messages in the user's account.
$messages = listMessages($service, $user, [
    #'maxResults' => 20, // Return 20 messages.
    'labelIds' => 'INBOX', // Return messages in inbox.
]);

foreach ($messages as $message) {
    print 'Message with ID: ' . $message->getId() . "\n";

    $msgObj = getMessage($service, $user, $message->getId());

    $headerArr = getHeaderArr($msgObj->getPayload()->getHeaders());

    echo 'Message-ID: ' . $headerArr['Message-ID'];
    echo "\n";
    echo 'In-Reply-To: ' . (empty($headerArr['In-Reply-To']) ? '' : $headerArr['In-Reply-To']);
    echo "\n";
    echo 'References: ' . (empty($headerArr['References']) ? '': $headerArr['References']);
    echo "\n";

    #print_r($headerArr);

    $bodyArr = getBody($msgObj->getPayload()->getParts());
    echo 'Body: ' . (empty($bodyArr[0]) ? '' : $bodyArr[0]);
}

Reference:

https://developers.google.com/gmail/api/quickstart/php https://developers.google.com/gmail/api/v1/reference/users/messages/modify#php

Blaztix
  • 1,223
  • 1
  • 19
  • 28
Jun Hsieh
  • 1,574
  • 13
  • 9
  • very close to what I had been looking for, but for some reason I don't get the content of body (the `getParts` is no longer available) if I add filter to the `optArr` param of `listMessages` function, ```$messages = listMessages($service, $user, [ #'maxResults' => 20, // Return 20 messages. 'labelIds' => 'INBOX', // Return messages in inbox. 'q' => 'Marketplace Purchase Complete: New Order' ]);``` – CodeGenius Sep 09 '21 at 09:14
2

I'd start here: https://developers.google.com/gmail/api/v1/reference/users/messages/list and https://developers.google.com/gmail/api/v1/reference/users/messages/get

note that when you get a list of messages only the IDs of those messages are returned then you use the ID with the get method to actually get the message content:

Arthur Thompson
  • 9,087
  • 4
  • 29
  • 33
  • 1
    Thanks. Do you know how to get message body with php ? $msg->getPayload()->getParts()->getBody(); not able to get content – Anandhan Jul 03 '14 at 05:52
  • php library for gmail api is completed one ? – Anandhan Jul 03 '14 at 07:30
  • That's true , we get only list of messages with its ID. The problem is how to use every ID to get its messageId i.e i am trying to go through the google documentation ,but unable to find any clue. – Nilesh Kumar Jul 14 '15 at 11:54
0

Building on the excellent answers from @Samphors and @Muffy, here is how you would do this in the MVC world of Symfony

My controller...

class GMailController extends AbstractController
{
    /**
     * @Route("/g/mail", name="app_g_mail")
     */
    public function index(Request $request): Response
    {
        $code = $request->query->get('code');
        return $this->render('g_mail/index.html.twig', [
            'controller_name' => 'GMailController',
            'code' => $code
        ]);
    }
    /**
     * @Route("/g/mail/test", name="app_g_mail_test", methods={"POST"})
     */
    public function test(Request $request) : Response
    {
        $filepath = '../client_secret.json';
        $contents = file_get_contents($filepath);
        $client = new Client();
        $client->setApplicationName('Gmail_test');
        $client->setAuthConfigFile($filepath);
        $client->addScope('email');
        $client->addScope('https://mail.google.com');
        $client->setRedirectUri('https://localhost:8005/g/mail');
        $client->setAccessType('offline');
        // Redirect the URL after OAuth
        $code = $request->request->get('code');
        if ($code) {
            error_log('code is set = '.$code);
            $client->authenticate($code);
            $_SESSION['access_token'] = $client->getAccessToken();
        }
        
        // If Access Toket is not set, show the OAuth URL
        if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
            error_log('setting access token');
            $client->setAccessToken($_SESSION['access_token']);
        } else {
            error_log('creating authorization url');
            $authUrl = $client->createAuthUrl();
            error_log($authUrl);
            return new JsonResponse(['success' => true, 'authUrl' => $authUrl]);
        }
        if ($client->getAccessToken()) {

            $_SESSION['access_token'] = $client->getAccessToken();
         
            // Prepare the message in message/rfc822
            try {
                $service = new Gmail($client);
                $optParams = [];
                $optParams['maxResults'] = 5; // Return Only 5 Messages
                $optParams['labelIds'] = 'INBOX'; // Only show messages in Inbox
                $messages = $service->users_messages->listUsersMessages('me',$optParams);
                $list = $messages->getMessages();
                $messageId = $list[0]->getId(); // Grab first Message
                
                
                $optParamsGet = [];
                $optParamsGet['format'] = 'full'; // Display message in payload
                $message = $service->users_messages->get('me',$messageId,$optParamsGet);
                $messagePayload = $message->getPayload();
                $headers = $message->getPayload()->getHeaders();
                $parts = $message->getPayload()->getParts();
                
                $body = $parts[0]['body'];
                $rawData = $body->data;
                $sanitizedData = strtr($rawData,'-_', '+/');
                $decodedMessage = base64_decode($sanitizedData);
                return new JsonResponse(['success' => true, 'message' => $decodedMessage]);
            } catch(Exception $e) {
                error_log('in exception handler');
                error_log($e);                
                return new JsonResponse(['success' => false, 'error' => $e->__toString()]);
            }
        }
        return new JsonResponse(['success' => false]);
    }
}

My twig template...

{% extends 'base.html.twig' %}

{% block title %}GMail API Bench{% endblock %}

{% block body %}
<div class="container m-5">
    <div class="row my-2">
        <div class="col-12">
            <button type="button" class="btn btn-primary btn-lg" id="test-gmail-button">Test GMail API</button>
        </div>
    </div>
    <div class="row my-2">
        <h4><b>Authorization:</b></h4>
        <div id="test-authorization">
            <a id="authorization-link" href="#"></a>
        </div>
        <input type="hidden" id="gmail-authorization-code" value="{{ code }}">
        {% if code is not null %}
            <span class="text-success"><h4>Authorized</h4></span>
        {% endif %}
    </div>
    <div class="row my-2">
        <h4><b>Results:</b></h4>
        <div id="test-results">
        </div>
    </div>
</div>

{% endblock %}

{% block javascripts %}
    <script src="/static/site/js/gmail_bench.js?v=1.20230126"></script>
{% endblock %}

My client-side JavaScript...

$(document).ready(() => {
    console.log('gmail_bench.js: in document ready')
})

$('#test-gmail-button').on('click', function(ev) {
    console.log('test gmail');
    let code = $('#gmail-authorization-code').val();
    $.ajax({
        url: '/g/mail/test',
        type: 'POST',
        data: {
            code: code
        },
        success: function(data, textStatus) {
            if(data.success) {
                if(data.authUrl) {
                    $('#authorization-link').attr('href',data.authUrl);
                    $('#authorization-link').html('Authorize');
                }
                if(data.message) {
                    $('#test-results').empty().append(data.message);
                }
            } else {
                $('#test-results').empty().append(data.error);
            }
        },
        error: function(xhr, textStatus) {
            console.log('in test gmail error');
            console.log(xhr);
            console.log(textStatus);
        }
    })  
})

So the way this works is the first post to the test endpoint returns the authorization url. This is added as a link on the page...

After initial test click...authorize link appears

Then after authorization, the OAuth2 code is added to the page...

After authorization

Finally the second click on test butter sends the code and the email contents come back...

After 2nd click of test button

AQuirky
  • 4,691
  • 2
  • 32
  • 51