Ler e-mails do GMail pelo PHP

Artigo que explica como é possível fazer um script PHP ler os e-mails recebidos em uma conta do GMail ou de uma conta com suporte a IMAP.

Este artigo mostra como obter os e-mails recebidos por uma conta do GMail a partir de código PHP.

Primeiramente, é preciso configurar o GMail para aceitar conexão IMAP. Para isso, é preciso entrar na sua conta pelo web mail (mail.google.com), e acessar o link "Configurações", no canto superior direito da página. Isso deve abrir uma tela com várias abas de configurações, então acesse a aba "Encaminhamento e POP/IMAP". Nesta aba, ative o IMAP no campo mostrado pela imagem:
Configuração de IMAP do GMail

E não se esqueça de clicar em "Salvar alterações".

Também é necessário permitir o acesso por aplicações menos seguras nesse link Aplicativos menos seguros.

Agora é só montar o script. Para isso, será preciso usar o módulo imap do PHP. Ele pode ser instalado via PECL.

Abrindo a conexão:

// Configure com seu login/senha
$login = 'teste';
$senha = '12345';

$str_conexao = '{imap.gmail.com:993/imap/ssl}';
if (!extension_loaded('imap')) {
    die('Modulo PHP/IMAP nao foi carregado');
}

// Abrindo conexao
$mailbox = imap_open($str_conexao, $login, $senha);
if (!$mailbox) {
    die('Erro ao conectar: '.imap_last_error());
}

Obtendo dados básicos sobre a conta:

$check = imap_check($mailbox);

// Ultima mensagem
echo $check->Date;

// Tipo de conexao
echo $check->Driver;

// Mailbox
echo $check->Mailbox;

// Numero de mensagens total
echo $check->Nmsgs;

// Numero de mensagens novas
echo $check->Recent;

Obter os marcadores (ou diretórios) da conta:

$marcadores = imap_getmailboxes($mailbox, $str_conexao, '*');

if (is_array($marcadores)) {
    foreach ($marcadores as $marcador) {
        $nome = str_replace($str_conexao, '', $marcador->name);
        $pos = strpos($nome, $marcador->delimiter);
        if ($pos !== false) {
            $nome = substr($nome, $pos + 1);
        }
        echo $nome."\n";
    }
} else {
    echo imap_last_error();
}

Obtém um array com informações sobre a mensagem "1" (primeira mensagem da caixa de entrada):

$overview = imap_fetch_overview($mailbox, 1);

$email = current($overview);

// Assunto
echo $email->subject;

// Remetente
echo $email->from;

// Destinatarios
echo $email->to;

// Data
echo $email->date;

// Identificador da mensagem
echo $email->message_id;

// Identificador da mensagem de referencia
echo $email->references;

// Identificador da mensagem que gerou a resposta
echo $email->in_reply_to;

// Tamanho em bytes
echo $email->size;

// Identificador da mensagem na caixa de entrada
echo $email->uid;

// Numero sequencial da mensagem na caixa de entrada
echo $email->msgno;

// Possui marca de "recente"
echo $email->recent;

// Possui marca de "marcada"
echo $email->flagged;

// Possui marca de "respondida"
echo $email->answered;

// Possui marca de "pronta para ser apagada"
echo $email->deleted;

// Possui marca de "lida"
echo $email->seen;

// Possui marca de "rascunho"
echo $email->draft;

Ler o cabeçalho da mensagem "1" (primeira mensagem da caixa de entrada):

$header = imap_header($mailbox, 1);

// Data
echo $header->Date;

// Endereco do destinatario
echo $header->toaddress;

// Endereco do remetente
echo $header->fromaddress;

// Enderecos de copia
echo $header->cc;

// Endereco de resposta
echo $header->reply_toaddress;

// Tamanho da mensagem
echo $header->Size;

// Assunto da mensagem
echo $header->Subject;

Obter o conteúdo da mensagem 1 (primeira mensagem):

$conteudo = imap_body($mailbox, 1);

Obtendo o conteúdo de texto (PLAIN e/ou HTML) das mensagens não lidas:

/** Funcao para decodificar o conteudo de uma mensagem */
function parse_encoding($body, $encoding) {
    switch ($encoding) {
    case ENCBASE64:
        return base64_decode($body);
    case ENCQUOTEDPRINTABLE:
        return imap_qprint($body);
    case ENC7BIT:
        return imap_mutf7_to_utf8($body);
    case ENC8BIT:
    case ENCBINARY:
    case ENCOTHER:
        return $body;
    }
}

// Obter os IDs dos e-mails nao lidos
$unseen = imap_search($mailbox, 'UNSEEN');      

foreach ($unseen as $id) {
    // Obter o conteudo cru da mensagem
    $body = imap_body($mailbox, $id);
    $structure = imap_fetchstructure($mailbox, $id);

    // Decodificando o conteudo das partes da mensagem
    $contents = array();
    if ($structure->type == TYPEMULTIPART) {
        foreach ($structure->parts as $j => $part) {
            $body_part = imap_fetchbody($mailbox, $id, $j + 1);
            if ($part->type == TYPETEXT) {
                $contents[$part->subtype] = parse_encoding($body_part, $part->encoding);
            }
        }
    } else {
        $contents[$structure->subtype] = parse_encoding($body, $structure->encoding);
    }

    // Exibindo o conteudo de texto do e-mail
    var_dump($contents);
}

Fechar a conexão IMAP:

imap_close($mailbox);

30 comentários

klawdyo disse...

Não esqueça de incluir o @gmail.com ao final do nome de usuário.

Pancinha de Ouro disse...

Olá, para obter o conteúdo da mensagem exatamente (de e-mail com anexo), você disse que é necessário utilizar um parser. como faço isso, teria com me ajudar?

hotel disse...

Olá rubS, como posso obter o tamanho total de espaço em disco utilizado por essa conta de e-mail?
pode ajudar?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Hotel,

Creio que não exista algo para determinar o tamanho em disco, mas você é capaz de calcular o tamanho total das mensagens de e-mail. Para isso, basta somar o valor de $overview['size'] de cada mensagem. Veja no exemplo de como obter um vetor com informações sobre uma mensagem.

Anônimo disse...

Rubens, usamos o servidor do google mas com nosso proprio domínio, tem problema? Preciso usar o gmail.com?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Ednaldo

Não tenho certeza sobre qual é o comportamento do servidor imap do gmail respondendo por um domínio próprio.

Acredito que seja possível consultá-lo normalmente, mas precisaria informar o e-mail completo no lugar do "login" do exemplo do artigo (e-mail com o seu domínio próprio). É testar pra ver.

Se conseguir, por favor, comente aqui para ajudar outras pessoas.

Rubens Takiguti Ribeiro (autor do blog) disse...

Luiz Filipe, para pegar as mensagens de um diretório, basta mudar a string de conexão colocando o nome do diretório (no caso do GMail, é o "marcador"). Por exemplo:

$str_conexao = '{imap.gmail.com:993/imap/ssl}[Gmail]/Com estrela';

Para obter direitinho o nome da string de conexão, você precisa conectar na raíz e obter os marcadores com a função "imap_getmailboxes", como mostrado no artigo.

CARLOS ROBERTO POLISHOP-VIPBRASIL disse...

Ola estou tentando fazer um script e gostaria da sua ajuda, seguinte eu recebi um e-mail com uma imagem com um link, daí quando clico nesta imagem vai pra um formulario que já preenche o e-mail automaticamente saberia me dizer como pegar o parametro e-mail.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Carlos

Se a URL possui o e-mail como parâmetro, basta usar a função parse_str sobre parte de "query string" da URL. Para mais detalhes, sugiro que leia este outro artigo e, se tiver dúvidas, é só perguntar:
http://rubsphp.blogspot.com.br/2012/04/manipulacao-de-url-uri-e-links.html

Anônimo disse...

Olá Rubens, tudo bem? Possuo uma variável que recebe message_id do e-mail e gostaria de gravar em um banco mysql porem recebo o erro 1064 já tentei de diversas formas mas ainda não rolou, pode me auxiliar ;) ?

Anônimo disse...

Segui sim Rubens, quanto ao que você passou acima tudo esta ok,porem quero gravar a id da mensagem em um banco mysql e neste momento que recebo o erro, a id esta neste formato <175228826.8.1412111715564.JavaMail.root@xxxdnn2594>, sistema@crmtel.com.br)

Rubens Takiguti Ribeiro (autor do blog) disse...

Entendido. Então o erro é na hora de inserir no banco. O erro 1064 do MySQL significa erro de sintaxe da sua query. Sugiro que dê uma revisada na query, para ver se não ficou faltando algum parênteses ou aspas. Recomendo que utilize o PDO para conectar ao MySQL, e capturar a exception se ocorrer algum erro. Seguem alguns artigos úteis:
http://rubsphp.blogspot.com.br/2010/09/pdo.html
http://rubsphp.blogspot.com.br/2010/11/modificando-o-controle-de-erros-do-pdo.html

Jéssica Reis disse...

Boa tarde, eu segui o procedimento e deu td certo até então, gostaria de informações a respeito se é possivel trazer somente as mensagens não lidas da caixa principal.

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Jessica

Não conheço uma forma de obter apenas as mensagens não lidas de forma direta. A forma que eu conheço é percorrendo os e-mails e usando a função imap_fetch_overview para saber se cada um foi lido ou não (conforme mostrado no artigo).

Acredito que os clientes de e-mail façam um cache disso para não precisar consultar todos e-mail todas as vezes que são acessados.

Anônimo disse...

O gmail está bloqueando meu acesso, mesmo colocando para utilizar app menos seguro. Alguma sugestão ?

Unknown disse...

estou utilizando o seu codigo acima mas é retornado este erro:
Erro ao conectar: Can not authenticate to IMAP server: [ALERT] Please log in via your web browser: https://support.google.com/mail/acco.
vc sabe o que pode estar errado??

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Marcio
Se você habilitou o imap nas configurações do gmail e habilitou a autenticação de aplicativos menos seguros, como mostrado no artigo, então não sei.

Unknown disse...

Ola, precisava de um script para percorrer as mensagens da caixa de entrada do gmail e me retornar os endereços de email que devolveram as mensagens. Tenho como conseguir isso via php ou g-scripts?

Anônimo disse...

Estou recebendo esse erro : imap_search() expects parameter 1 to be resource, null given nesta linha do seu código "$unseen = imap_search($mailbox, 'UNSEEN');"

Anônimo disse...

Mensagens de erro :
Undefined property: stdClass::$references Na linha "echo $email->references;"
Undefined property: stdClass::$in_reply_to Na linha "echo $email->in_reply_to;"
Array to string conversion Na linha "echo $header->cc;"