Costruire funzionalità avanzate di posta elettronica con IMAP e PHP

Cosa starai creando

In questo tutorial, ti guiderò attraverso alcuni esempi reali di come puoi utilizzare PHP e IMAP per creare nuove funzionalità per la gestione delle tue e-mail che i grandi provider di posta elettronica non hanno creato per noi.

Il mio interesse per questo è iniziato nel 2010, quando ho scritto Twelve Gmail Ideas per Rivoluzionare Email (Again), ma soprattutto le idee che desideravo erano rimaste fuori dalla mia portata. Per quanto importante sia la posta elettronica, l'innovazione della posta elettronica come applicazione è stata piuttosto lenta.

Stiamo affogando nelle e-mail e la gestione delle nostre caselle di posta rimane un pesante fardello. I servizi di posta e i clienti hanno fatto ben poco per aiutarci in questo. La maggior parte delle e-mail che riceviamo viene inviata dalle macchine, non dalle persone, eppure siamo noi a doverle elaborare singolarmente. 

L'analisi della mia e-mail ha mostrato che stavo ricevendo e-mail da più di 230 mittenti automatici, molte meno persone reali. Ero stanco di costruire filtri in Gmail e compilare una miriade di moduli di disiscrizione. Volevo avere più controllo sulla gestione della mia email e semplificare la mia vita.

Infine, l'anno scorso, ho deciso di costruire le funzionalità di cui avevo bisogno. Il risultato è Simplify Email (SE), una piccola app web che puoi ospitare tu stesso che offre una varietà di nuove e interessanti funzionalità di posta elettronica che puoi consultare sul sito Web del progetto..

La cosa più bella di SE è che è una piattaforma per leggere, analizzare, instradare e gestire la tua posta elettronica: le possibilità abbondano. Semplificare l'email è essenzialmente un campo di gioco programmabile per "hackerare" la tua email.

Ti guiderò attraverso il codice di tre esempi di SE che utilizzano PHP, IMAP e MySQL per lavorare con la posta elettronica:

  1. Controllo della posta in arrivo e filtraggio dei messaggi
  2. Implementazione di una whitelist challenge per mittenti sconosciuti
  3. Segnalazione di e-mail senza risposta

Questo tutorial ti darà sicuramente un vantaggio nella scrittura del codice IMAP in PHP. Ma puoi anche lavorare direttamente con la base di codici di e-mail semplificata. È possibile acquistare il codice per un minimo di $ 10, e c'è una versione open source precedente (che manca alcune delle funzionalità che stiamo descrivendo di seguito). Le guide di installazione sono fornite per le configurazioni tipiche di Linux. Offro anche immagini preinstallate a Digital Ocean per $ 25 e un'installazione manuale di valet. SE è scritto in PHP, in Yii Framework. 

Si noti che non sarà possibile accedere alla maggior parte dei server di posta elettronica tramite la macchina di sviluppo locale a meno che non si compili una libreria IMAP sicura per PHP. Questo è uno dei motivi per cui incoraggio le persone a utilizzare Simplify Email in droplets su Digital Ocean. Ci sono anche alcuni trucchi per ottenere la sicurezza dell'account Google che ti consente di accedere tramite IMAP.

Lavorare con IMAP

Come funziona la posta elettronica semplificata

Con SE, puoi continuare a utilizzare il tuo client di posta elettronica preferito sia sul Web che sui tuoi dispositivi mobili. Non è necessario modificare app o abitudini personali. SE accede ai tuoi account e-mail dietro le quinte tramite IMAP; agendo come un assistente personale intelligente, SE pre-elabora la tua e-mail, spostando i messaggi nei luoghi appropriati in base a tutto ciò che hai detto.

Quando arriva un messaggio da un mittente familiare, SE lo sposta nella cartella che hai specificato. Quando si arriva da un mittente sconosciuto per la prima volta, lo sposta nella cartella di revisione. 

Ogni due ore (o alla frequenza scelta), SE ti invierà un riepilogo di dove ha spostato i tuoi messaggi e quali messaggi sono in revisione. Nota: i link per i mittenti di addestramento sono inclusi nella cartella di revisione, rendendo abbastanza facile addestrare SE nel tempo.

In qualsiasi momento è possibile sfogliare la cartella delle recensioni: non è necessario attendere l'arrivo del digest. Ma il vantaggio di SE è che non devi più sfogliare le tue cartelle; puoi solo leggere il tuo sommario per ottenere una vista dell'e-mail che hai ricevuto e addestrare nuovi mittenti.

1. Controllo della posta in arrivo e dei messaggi di filtro

SE usa diversi compiti di cron per operare in background sul tuo server. Ognuno è chiamato da DaemonController.php.

Il primo, processInbox, viene chiamato frequentemente e ha bisogno di funzionare rapidamente: il suo compito è di schermare la posta elettronica e spostarla fuori dalla Posta in arrivo il più rapidamente possibile e nella cartella di triage, chiamata cartella di filtraggio. 

Il secondo, processFiltering, è più intensivo del processo ed esegue operazioni più approfondite su e-mail, spostando in ultima analisi i messaggi verso la loro destinazione finale.

Il metodo ProcessInbox

Le chiamate di compiti cron processInbox regolarmente:

funzione pubblica actionInbox () // sposta i messaggi di posta in arrivo su @filtering // esegue frequentemente $ r = new Remote (); $ R-> processInbox (); 

Per ogni account, decifriamo le tue credenziali di posta elettronica e quindi utilizziamo imap_open per creare uno stream IMAP nella tua cartella di posta in arrivo:

funzione pubblica aperta ($ account_id, $ mailbox = ", $ options = NULL) // apre la cartella in un account IMAP $ account = Account :: model () -> findByPk ($ account_id); $ this-> hostname = $ account-> indirizzo; if (! stristr ($ this-> hostname, '')) $ this-> hostname = ''. $ this-> hostname. ''; $ cred = Account :: model () -> getCredentials ($ account-> cred); if ($ account-> provider == Account :: PROVIDER_ICLOUD) // icloud accetta solo la parte nome della casella di posta, ad esempio stevejobs vs. [email protected] $ temp = explode (' @ ', $ cred [0]); $ cred [0] = $ temp [0]; $ this-> stream = imap_open ($ this-> hostname. $ mailbox, $ cred [0], $ cred [1 ], $ options, 1) o die ('Impossibile connettersi al server di posta - account_id:'. $ account_id. ". print_r (imap_errors ()));  

Entro processInbox, utilizziamo le funzioni della libreria PHP imap_search e imap_fetch_overview per recuperare una serie di messaggi:

// cerca folder_id dell'INBOX di questo account $ folder_id = Folder :: model () -> lookup ($ account_id, $ this-> path_inbox); $ This-> open ($ account_id, $ this-> path_inbox); $ Cnt = 0; $ message_limit = 50; // interrompe i messaggi per impedire il timeout echo 'Ordina da:' .date ("j F Y", $ tstamp); // imap_search date format 30 novembre 2013 $ recent_messages = @imap_search ($ this-> stream, 'SINCE' '.date ("j F Y", $ tstamp). ""', SE_UID); se ($ recent_messaggi === false) continua; // da fare - continua nel prossimo account $ result = imap_fetch_overview ($ this-> stream, implode (',', array_slice ($ recent_messages, 0, $ message_limit)), FT_UID); 

Quindi elaboriamo la matrice di messaggi nella Posta in arrivo:

foreach ($ result as $ item) if (! $ this-> checkExecutionTime ($ time_start)) interruzione; // get msg header e stream uid $ msg = $ this-> parseHeader ($ item); 

Ecco una versione adattata del codice di analisi dell'intestazione IMAP disponibile pubblicamente che raccoglie le informazioni aggiuntive di cui ha bisogno SE per una serie di attività. Fondamentalmente, utilizza imap_rfc822_parse_adrlist per determinare le informazioni sul destinatario, id-messaggio, oggetto e data / ora (o informazioni sul mittente durante la scansione della cartella inviata):

 parseHeader public function ($ header) // analizza l'oggetto header restituito da imap_fetch_overview se (! isset ($ header-> from)) return false;  else $ from_arr = imap_rfc822_parse_adrlist ($ header-> from, 'gmail.com'); $ fi = $ from_arr [0]; $ msg = array ("uid" => (isset ($ header-> uid))? $ header-> uid: 0, "personal" => (isset ($ fi-> personal))? @ imap_utf8 ($ fi -> personale): "", "email" => (isset ($ fi-> mailbox) && isset ($ fi-> host))? $ fi-> mailbox. "@". $ fi-> host: " "," mailbox "=> (isset ($ fi-> mailbox))? $ fi-> mailbox:" "," host "=> (isset ($ fi-> host))? $ fi-> host:" "," oggetto "=> (isset ($ header-> subject)) @ imap_utf8 ($ header-> subject):" "," message_id "=> (isset ($ header-> message_id))? $ header- > message_id: "", "in_reply_to" => (isset ($ header-> in_reply_to))? $ header-> in_reply_to: "", "udate" => (isset ($ header-> udate))? $ header- > udate: 0, "date_str" => (isset ($ header-> date))? $ header-> date: ""); // gestisce fetch con l'intestazione di uid e rfc if ($ msg ['udate'] == 0 && isset ($ header-> date)) $ msg ['udate'] = strtotime ($ header-> date);  $ msg ['rx_email'] = "; $ msg ['rx_personal'] ="; $ msg ['rx_mailbox'] = "; $ msg ['rx_host'] ="; if (isset ($ header-> to)) $ to_arr = imap_rfc822_parse_adrlist ($ header-> to, 'gmail.com'); $ to_info = $ to_arr [0]; if (isset ($ to_info-> mailbox) && isset ($ to_info-> host)) $ msg ['rx_email'] = $ to_info-> mailbox. '@'. $ to_info-> host;  if (isset ($ to_info-> personal)) $ msg ['rx_personal'] = $ to_info-> personale; if (isset ($ to_info-> mailbox)) $ msg ['rx_mailbox'] = $ to_info-> mailbox; if (isset ($ to_info-> host)) $ msg ['rx_host'] = $ to_info-> host;  restituisce $ msg; 

Creiamo record per il mittente e la busta del messaggio all'interno del nostro database:

 // salta tutti i messaggi di sistema se ($ msg ['email'] == $ system_email) continua; // se udate è troppo vecchio, salta msg if (time () - $ msg ['udate']> $ this-> scan_seconds) continua; // skip msg // azione predefinita $ action = self :: ACTION_MOVE_FILTERED; $ isNew = $ s-> isNew ($ account_id, $ msg ["email"]); // cerca il mittente, se nuovo, creali $ sender_id = $ s-> aggiungi ($ user_id, $ account_id, $ msg ["personale"], $ msg ["mailbox"], $ msg ["host"], 0); $ sender = Sender :: model () -> findByPk ($ sender_id); // crea un messaggio in db se necessario $ message_id = $ m-> add ($ user_id, $ account_id, 0, $ sender_id, $ msg ['message_id'], $ msg ['subject'], $ msg ['udate '], $ msg [' in_reply_to ']); $ message = Message :: model () -> findByPk ($ message_id); 

Se il mittente è nuovo per noi (sconosciuto), invieremo un'email di richiesta di whitelist (parleremo di più sulle sfide nella white list nella prossima sezione di seguito):

if ($ isNew) $ this-> challengeSender ($ user_id, $ account_id, $ mittente, $ messaggio); 

Successivamente, determiniamo se l'utente potrebbe aver trascinato un messaggio da un'altra cartella nella posta in arrivo, intendendo addestrarlo tramite trascinamento della selezione. In tal caso, impostiamo la formazione per questo mittente nella Posta in arrivo. In altre parole, la prossima volta vorremmo semplicemente indirizzare i messaggi da questo mittente alla Posta in arrivo:

 if ($ message ['status'] == Messaggio :: STATUS_FILTERED || $ messaggio ['stato'] == Messaggio :: STATUS_REVIEW || ($ message ['status'] == Messaggio :: STATUS_TRAINED && $ messaggio [ 'folder_id'] <> $ folder_id) || ($ message ['status'] == Messaggio :: STATUS_ROUTED && $ message ['folder_id'] <> $ folder_id)) // allora è un allenamento $ action = self :: ACTION_TRAIN_INBOX;  else if (($ message ['status'] == Messaggio :: STATUS_TRAINED || $ messaggio ['stato'] == Messaggio :: STATUS_ROUTED) && $ messaggio ['folder_id'] == $ folder_id) // se già addestrato o indirizzato alla casella di posta in arrivo, saltalo $ action = self :: ACTION_SKIP; echo 'Addestrato in precedenza, salta'; lb (); Continua;  

In caso contrario, ci prepareremo a spostare il messaggio nella cartella Filtro per un'ulteriore elaborazione. In primo luogo, potremmo inviare notifiche al telefono dell'utente se c'è una corrispondenza del mittente o una corrispondenza di parole chiave per le notifiche (e non sono ore tranquille):

 if ($ action == self :: ACTION_MOVE_FILTERED) $ cnt + = 1; if ($ sender-> exclude_quiet_hours == Sender :: EQH_YES o! $ this-> isQuietHours ($ user_id)) // invia notifiche per smartphone in base al mittente if ($ sender-> alert == Sender :: ALERT_YES) $ this-> notificare ($ mittente, $ messaggio, Monitor :: NOTIFY_SENDER);  // invia notifiche basate su parole chiave if (AlertKeyword :: model () -> scan ($ msg)) $ this-> notify ($ mittente, $ message, Monitor :: NOTIFY_KEYWORD);  // sposta imap msg su + Filtering echo 'Spostarsi su + Filtering; lb (); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ this-> path_filtering, CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ this-> path_filtering, false); if ($ result) echo 'spostato
'; $ M-> setStatus ($ message_id, Messaggio :: STATUS_FILTERED);

Se il messaggio è stato trascinato nella Posta in arrivo, aggiorneremo le nostre impostazioni di allenamento:

altrimenti if ($ action == self :: ACTION_TRAIN_INBOX) // imposta il mittente id_cartella su inbox echo 'Train to Inbox'; lb (); $ M-> setStatus ($ message_id, Messaggio :: STATUS_TRAINED); // solo treno mittente quando il messaggio è più recente dell'ultima impostazione se ($ msg ['udate']> = $ sender ['last_trained']) $ s-> setFolder ($ sender_id, $ folder_id); 

Il metodo ProcessFiltering

Viene chiamato il metodo di elaborazione secondario processFiltering, anche in DaemonController.php. Fa gli aspetti più dispendiosi in termini di tempo di spostare i messaggi nelle cartelle appropriate:

funzione pubblica actionIndex () // elabora i messaggi in @Filtering nelle cartelle appropriate $ r = new Remote (); $ R-> processFiltering (); // Registra il timestamp del cronjob per il monitoraggio $ file = file_put_contents ('./ protected / runtime / cronstamp.txt', time (), FILE_USE_INCLUDE_PATH);  

Questo metodo apre il tuo account e-mail per cercare messaggi recenti e raccogliere dati su di loro. Usa anche imap_search, imap_fetch_overview e parseHeader:

$ tstamp = time () - (7 * 24 * 60 * 60); // 7 giorni fa $ recent_messages = @imap_search ($ this-> stream, 'SINCE "' .date (" j F Y ", $ tstamp)." "', SE_UID); se ($ recent_messaggi === false) continua; // da fare - continua nel prossimo account $ result = imap_fetch_overview ($ this-> stream, implode (',', array_slice ($ recent_messages, 0, $ message_limit)), FT_UID); foreach ($ result as $ item) $ cnt + = 1; se (! $ this-> checkExecutionTime ($ time_start)) interrompe; // get msg header e stream uid $ msg = $ this-> parseHeader ($ item); 

Il ciclo di elaborazione principale per ogni messaggio nella cartella di filtraggio è abbastanza dettagliato. Per prima cosa guardiamo all'indirizzo del destinatario, poiché SE consente alle persone di addestrare le cartelle in base all'indirizzo del destinatario, ad es. messaggi al dominio happyvegetarian.com vai alla cartella veggie:

 // Imposta l'azione predefinita per spostarsi nella cartella di revisione $ action = self :: ACTION_MOVE_REVIEW; $ destination_folder = 0; // cerca e crea il destinatario $ recipient_id = $ r-> aggiungi ($ user_id, $ account_id, $ msg ['rx_email'], 0); $ routeByRx = $ this-> routeByRecipient ($ recipient_id); se ($ routeByRx! == false) $ action = $ routeByRx-> azione; $ destination_folder = $ routeByRx-> destination_folder;  

Quindi cerchiamo il mittente e creiamo un nuovo record nel database (se necessario). Se esiste una formazione per il mittente, possiamo impostare la cartella di destinazione:

 // cerca il mittente, se nuovo, creali $ sender_id = $ s-> aggiungi ($ user_id, $ account_id, $ msg ["personale"], $ msg ["mailbox"], $ msg ["host"], 0); $ sender = Sender :: model () -> findByPk ($ sender_id); // se la destinazione del mittente è nota, percorso alla cartella if ($ destination_folder == 0 && $ sender ['folder_id']> 0) $ action = self :: ACTION_ROUTE_FOLDER; $ destination_folder = $ sender ['folder_id'];  

Se un mittente inesperto (nuovo) si è verificato tramite una sfida nella white list (di cui parleremo nella prossima sezione di seguito), inoltreremo questo messaggio alla posta in arrivo:

// i mittenti verificati nella whitelist passano alla posta in arrivo se ($ sender-> is_verified == 1 && $ sender ['folder_id'] == 0 && UserSetting :: model () -> useWhitelisting ($ user_id)) // inserisci un messaggio in inbox $ action = self :: ACTION_ROUTE_FOLDER; $ destination_folder = Folder :: model () -> lookup ($ account_id, $ this-> path_inbox); 

Quindi, creiamo una voce di messaggio nel database con le informazioni sulla busta su questo messaggio:

 // crea un messaggio in db $ message = Message :: model () -> findByAttributes (array ('message_id' => $ msg ['message_id'])); se (! empty ($ message)) // messaggio esiste già, $ message_id = $ message-> id;  else $ message_id = $ m-> aggiungi ($ user_id, $ account_id, 0, $ sender_id, $ msg ['message_id'], $ msg ['subject'], $ msg ['udate'], $ msg [ 'in risposta a']);  

Se proviene da un mittente sconosciuto non verificato, possiamo spostare il messaggio nella cartella di revisione. La cartella di revisione contiene tutti i messaggi dei mittenti che non riconosciamo.

Se il messaggio proviene da un mittente conosciuto e abbiamo una destinazione in mente, possiamo spostarlo a condizione che non siano ore tranquille (e che il disturbo non sia disattivato):

 if ($ recipient_id! == false) $ m-> setRecipient ($ message_id, $ recipient_id); if ($ action == self :: ACTION_MOVE_REVIEW) echo 'Spostamento su + Filtering / Review'; lb (); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ this-> path_review, CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ this-> path_review, false); if ($ result) echo 'spostato
'; $ M-> setStatus ($ message_id, Messaggio :: STATUS_REVIEW); else if ($ action == self :: ACTION_ROUTE_FOLDER || $ action == self :: ACTION_ROUTE_FOLDER_BY_RX) // nome della cartella di ricerca per folder_id $ folder = Folder :: model () -> findByPk ($ destination_folder); // se la posta in arrivo e le ore tranquille, non instradare adesso se (strtolower ($ cartella ['nome']) == 'inbox' e $ sender-> exclude_quiet_hours == Sender :: EQH_NO e $ this-> isQuietHours ( $ user_id)) continua; echo 'Spostamento a'. $ folder ['name']; lb (); $ mark_read = Folder :: model () -> isMarkRead ($ folder ['mark_read']) || Mittente :: modello () -> isMarkRead ($ mittente [ 'mark_read']); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ cartella ['nome'], CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ cartella ['nome'], $ mark_read); if ($ result) echo 'spostato
'; $ M-> setStatus ($ message_id, Messaggio :: STATUS_ROUTED); $ M-> setFolder ($ message_id, $ destination_folder);

Durante le ore di silenzio, i messaggi vengono conservati principalmente nella cartella dei filtri.

Ogni due ore, un processo diverso costruirà il digest del messaggio utilizzando i record della tabella dei messaggi per determinare quali e-mail sono state ricevute e filtrate di recente e come sono state instradate.

2. Implementazione di una whitelist Challenge a mittenti sconosciuti

L'obiettivo della sfida della whitelist è di mantenere qualsiasi messaggio da un mittente sconosciuto, ad es. possibilmente un bot di marketing o spammer, fuori dalla tua casella di posta. SE inserisce posta da mittenti sconosciuti nella cartella di revisione. Tuttavia, se attivi la whitelist, inviamo un'email di prova che consente al mittente di verificare che siano umani. Se rispondono, sposteremo il messaggio nella tua casella di posta. Se l'email risulta indesiderata, puoi scaricare il messaggio dal sommario o trascinarlo in qualsiasi cartella in cui desideri formarlo.

L'utente può attivare e disattivare la whitelist nelle impostazioni:

Per implementare la whitelist, inviamo sfide e-mail ogni volta che la posta arriva da un nuovo mittente:

if ($ isNew) $ this-> challengeSender ($ user_id, $ account_id, $ mittente, $ messaggio); 

ChallengeSender invia un link codificato all'utente affinché faccia clic. Abbiamo anche alcune protezioni per assicurarci che non ci intrappoliamo in un ciclo di email con un messaggio fuori sede:

 public function challengeSender ($ user_id, $ account_id, $ sender, $ message) // whitelist email challenge $ yg = new Yiigun (); $ ac = Account :: model () -> findByPk ($ account_id); if (! empty ($ ac ['challenge_name'])) $ from = $ ac ['challenge_name']. ' mg_domain '>.'; altrimenti $ da = 'Filtro mg_domain '>.'; $ cred = Account :: model () -> getCredentials ($ ac-> cred); $ account_email = $ cred [0]; unset ($ cred); // sicurezza: non verifica l'e-mail recente se ($ mittente-> last_emailed> (time () - (48 * 60 * 60))) restituisce false; if ($ sender-> isBot ($ sender ['email'])) // to do - può anche impostare questa persona alla rinfusa per default return false;  $ link = Yii :: app () -> getBaseUrl (true). "/ sender / verify / s /".$ sender-> id." / m /".$ message-> id. '/ u /' . $ message-> udate; $ subject = 'Verifica il messaggio che hai inviato a'. $ account_email; $ Corpo ="

Ciao,

Sto cercando di ridurre le email non richieste. Potresti verificare il tuo indirizzo email facendo clic sul link sottostante:
'$ Link.'

La verifica del tuo indirizzo email contribuirà a velocizzare il tuo messaggio nella mia casella di posta. Grazie per l'assistenza!

'; $ yg-> send_html_message ($ from, $ sender ['email'], $ subject, $ body); // aggiorna last_emailed $ sender-> touchLastEmailed ($ sender-> id);

Quindi, se il destinatario fa clic sul link codificato, li verifichiamo nel database. Il controller mittente elabora queste richieste e verifica la loro validità:

 funzione pubblica actionVerify ($ s = 0, $ m = 0, $ u = 0) // verifica che url msg sicuro da digest sia valido, accedi utente, mostra msg $ sender_id = $ s; $ message_id = $ m; $ udate = $ u; $ msg = Message :: model () -> findByPk ($ message_id); if (! empty ($ msg) && $ msg-> sender_id == $ sender_id && $ msg-> udate == $ udate) $ result = 'Grazie per l'assistenza. Risponderò alla tua email il prima possibile. '; $ a = new Advanced (); $ A-> verifySender ($ msg> account_id, $ sender_id);  else $ result = 'Spiacenti, non è stato possibile verificare il tuo indirizzo email.';  $ this-> render ('verify', array ('result' => $ result,)); 

Questo dice ai nostri loop di elaborazione di spostare questo e i futuri messaggi da questo mittente alla posta in arrivo.

3. Segnalazione di e-mail senza risposta

A volte può essere utile vedere un riepilogo dei messaggi inviati ma non ricevere una risposta. Per identificarli, Simplify Email monitora i messaggi che sono stati inviati ma non hanno ricevuto risposta.

Ogni messaggio che riceviamo contiene un ID univoco, chiamato message_id (parte della specifica IMAP). Spesso appare così:

Message-Id: 

Inoltre, quando i messaggi vengono inviati in risposta ad altri messaggi, hanno un in risposta a campo che rimanda all'originale message_id.

Quindi, usiamo una query SQL per trovare tutti i messaggi ricevuti che non hanno un messaggio di risposta corrispondente che fa riferimento al loro message_id. Per questo, usiamo LEFT OUTER JOIN dove non c'è in risposta a id:

funzione pubblica getUnanswered ($ account_id, $ mode = 0, $ range_days = 7) if ($ mode == 0) $ subject_compare = 'not'; else $ subject_compare = "; $ query = Yii :: app () -> db-> createCommand (" SELECT fi_sent_message.id, fi_sent_message.recipient_id as sender_id, fi_sent_message.subject, fi_sent_message.udate, fi_message.in_reply_to, fi_sent_message.message_id FROM fi_sent_message LEFT OUTER JOIN fi_message ON fi_message.in_reply_to = fi_sent_message.message_id WHERE fi_sent_message.account_id = ". $ account_id." AND fi_message.in_reply_to è null e fi_sent_message.udate> ". (time () - (3600 * 24 * $ range_days) ). "e fi_sent_message.subject". $ subject_compare. "come 'Re:%' ORDER BY fi_sent_message.udate DESC") -> queryAll (); restituisce $ query;

Noi usiamo il $ subject_compare modalità per distinguere tra i nostri messaggi inviati che non hanno ricevuto risposta e le nostre risposte inviate a un thread a cui non è stata data risposta. Ecco il rapporto sui messaggi senza risposta nel tuo account:

SE offre anche queste informazioni come un digest opzionale, chiamato digest email senza risposta. Puoi riceverlo ogni giorno, ogni pochi giorni o ogni settimana.

Utilizziamo inoltre tabulazione SQL simile con Google Charts per fornire rapporti su quanto spesso alcune persone ti inviano email:

 report di funzione pubblicaInbound ($ account_id, $ intervallo = 30, $ limite = 100) $ result = Yii :: app () -> db-> createCommand ('SELECT fi_sender.personal, fi_sender.email, count (sender_id) as cnt FROM fi_message LEFT JOIN fi_sender ON fi_sender.id = fi_message.sender_id WHERE fi_sender.account_id =: account_id AND fi_message.created_at> DATE_SUB (NOW (), INTERVAL: intervallo DAY) GROUP BY sender_id ORDER BY cnt desc LIMIT: limit ') -> bindValue ( 'range', $ range) -> bindValue ( 'ACCOUNT_ID', $ account_id) -> bindValue ( 'limite', $ limite) -> queryAll (); ritorno $ risultato;  

Presto scriverò di più su Google Charts for Tuts +. 

Prossimi passi

Spero che abbiate trovato Simplify Email abbastanza intrigante da provare la vostra programmazione PHP IMAP. Ci sono così tante fantastiche funzioni che puoi creare senza che i grandi provider di posta elettronica debbano fare nulla di nuovo.

In caso di domande o correzioni, inseriscile nei commenti. Se desideri tenere il passo con i miei tutorial Tuts + e altre serie, segui @reifman o visita la mia pagina dell'autore. Puoi anche contattarmi qui.

Link correlati

Ecco alcuni link aggiuntivi che potresti trovare utili:

  • Semplifica l'email
  • Introduzione a Simplify Email (video)
  • Dodici idee Gmail per rivoluzionare l'email (ancora) 
  • Copertura di Simplify Email in BoingBoing qui e qui
  • Riferimento IMAP PHP
  • Introduzione al framework Yii (Tuts +)