Heim >Backend-Entwicklung >PHP-Tutorial >Erstellen Sie erweiterte E-Mail-Funktionen mit IMAP und PHP
In diesem Tutorial zeige ich Ihnen einige Beispiele aus der Praxis, wie Sie mit PHP und IMAP neue Funktionen für die E-Mail-Verwaltung erstellen, die die großen E-Mail-Anbieter noch nicht für uns entwickelt haben.
Mein Interesse daran begann im Jahr 2010, als ich „Twelve Gmail Ideas to Revolutionize Email (Again)“ schrieb, aber die meisten Ideen, die ich wollte, waren immer noch unerreichbar. Trotz ihrer Bedeutung kam es bei E-Mail als Anwendung nur relativ langsam zu Innovationen.
Wir werden mit E-Mails überschwemmt und die Verwaltung unseres Posteingangs kann immer noch eine schwere Belastung sein. Postdienste und Kunden haben uns dabei kaum geholfen. Die meisten E-Mails, die wir erhalten, werden von Maschinen und nicht von Menschen gesendet, dennoch müssen wir sie alle einzeln bearbeiten.
Die Analyse meiner eigenen E-Mails ergab, dass ich E-Mails von über 230 automatischen Absendern erhalten habe, während die Anzahl der tatsächlichen Absender deutlich geringer war. Ich habe es satt, in Gmail Filter zu erstellen und unzählige Abmeldeformulare auszufüllen. Ich möchte mehr Kontrolle über mein E-Mail-Management haben und mein Leben vereinfachen.
Im letzten Jahr habe ich schließlich beschlossen, die Funktionalität zu entwickeln, die ich brauchte. Das Ergebnis ist Simplify Email (SE), eine kleine Web-App, die Sie selbst hosten können und die eine Vielzahl cooler neuer E-Mail-Funktionen bietet, die Sie alle auf der Projektwebsite ausprobieren können.
Das Coole an SE ist, dass es eine Plattform zum Lesen, Analysieren, Weiterleiten und Verwalten von E-Mails ist – die Möglichkeiten sind zahlreich. Simplified Email ist im Wesentlichen ein programmierbarer Spielplatz zum „Hacken“ Ihrer eigenen E-Mails.
Ich führe Sie durch den Code für drei Beispiele der E-Mail-Verarbeitung in SE mit PHP, IMAP und MySQL:
Dieses Tutorial wird Ihnen auf jeden Fall einen Vorsprung beim Schreiben von IMAP-Code in PHP verschaffen. Sie können die Codebasis von Simplify Email aber auch direkt nutzen. Sie können den Code für nur 10 US-Dollar erwerben, und es gibt eine ältere Open-Source-Version (der einige der unten beschriebenen Funktionen fehlen). Es werden Installationsanweisungen für typische Linux-Konfigurationen bereitgestellt. Ich biete bei Digital Ocean auch vorinstallierte Bilder für 25 $ an und biete eine Handheld-Valet-Installation an. SE ist in PHP innerhalb des Yii-Frameworks geschrieben.
Bitte beachten Sie, dass Sie von Ihrem lokalen Entwicklungscomputer aus nicht auf die meisten E-Mail-Server zugreifen können, es sei denn, Sie kompilieren eine sichere IMAP-Bibliothek für PHP. Dies ist einer der Gründe, warum ich Menschen dazu ermutige, Simplify Email als Droplet in Digital Ocean auszuführen. Es gibt auch einige Tipps, wie Sie Ihr Google-Konto schützen, wenn Sie über IMAP einsteigen.
Mit SE können Sie den E-Mail-Client Ihrer Wahl weiterhin im Web und auf Mobilgeräten verwenden. Sie müssen keine Anwendungen oder persönlichen Gewohnheiten ändern. SE greift hinter den Kulissen über IMAP auf Ihr E-Mail-Konto zu. Als intelligenter persönlicher Assistent verarbeitet SE Ihre E-Mails vor und verschiebt Nachrichten basierend auf allem, was Sie ihm mitteilen, an den entsprechenden Ort.
Wenn eine Nachricht von einem bekannten Absender eintrifft, verschiebt SE sie in den von Ihnen angegebenen Ordner. Wenn ein unbekannter Absender zum ersten Mal eine Nachricht erhält, wird diese in den Überprüfungsordner verschoben.
Alle paar Stunden (oder so oft Sie möchten) sendet SE Ihnen eine Zusammenfassung darüber, wohin Ihre Nachrichten verschoben wurden und welche Nachrichten überprüft werden. Beachten Sie, dass der Audit-Ordner einen Link zum Trainingssender enthält, was es sehr einfach macht, den SE im Laufe der Zeit zu trainieren.
Sie können Ihren Bewertungsordner jederzeit durchsuchen – Sie müssen nicht auf Zusammenfassungen warten. Der Vorteil von SE besteht jedoch darin, dass Sie Ihre Ordner nicht mehr durchsuchen müssen; Sie können einfach die Zusammenfassung lesen, um empfangene E-Mails anzuzeigen und neue Absender zu schulen.
SE verwendet mehrere Cron-Tasks, die im Hintergrund des Servers ausgeführt werden. Jeder wird von DaemonController.php
aufgerufen.
Der erste, processInbox
, wird häufig aufgerufen und muss schnell erledigt werden – seine Aufgabe besteht darin, E-Mails zu sichten und sie so schnell wie möglich aus dem Posteingang in kategorisierte Ordner, sogenannte Filterordner, zu verschieben.
Die zweite Methode, processFiltering
, ist verarbeitungsintensiver und führt detailliertere Vorgänge an der E-Mail durch, wodurch die Nachricht letztendlich an ihr endgültiges Ziel weitergeleitet wird.
Cron-Task wird regelmäßig aufgerufen processInbox
:
public function actionInbox() { // moves inbox messages to @filtering // runs frequently $r = new Remote(); $r->processInbox(); }
Für jedes Konto entschlüsseln wir Ihre E-Mail-Anmeldeinformationen und verwenden dann imap_open, um einen IMAP-Stream in Ihren Posteingangsordner zu erstellen:
public function open($account_id, $mailbox='',$options=NULL) { // opens folder in an IMAP account $account = Account::model()->findByPk($account_id); $this->hostname = $account->address; if (!stristr($this->hostname,'{')) $this->hostname = '{'.$this->hostname.'}'; $cred = Account::model()->getCredentials($account->cred); if ($account->provider == Account::PROVIDER_ICLOUD) { // icloud accepts only name part of mailbox e.g. stevejobs vs. stevejobs@icloud.com $temp = explode('@',$cred[0]); $cred[0]=$temp[0]; } $this->stream = imap_open($this->hostname.$mailbox,$cred[0],$cred[1],$options,1) or die('Cannot connect to mail server - account_id:'.$account_id .' '.print_r(imap_errors())); }
In processInbox
verwenden wir die PHP-Bibliotheksfunktionen imap_search und imap_fetch_overview, um das Nachrichtenarray abzurufen:
// lookup folder_id of this account's INBOX $folder_id = Folder::model()->lookup($account_id,$this->path_inbox); $this->open($account_id,$this->path_inbox); $cnt=0; $message_limit= 50; // break after n messages to prevent timeout echo 'Sort since: '.date("j F Y",$tstamp); // imap_search date format 30 November 2013 $recent_messages = @imap_search($this->stream, 'SINCE "'.date("j F Y",$tstamp).'"',SE_UID); if ($recent_messages===false) continue; // to do - continue into next account $result = imap_fetch_overview($this->stream, implode(',',array_slice($recent_messages,0,$message_limit)),FT_UID);
Dann verarbeiten wir das Nachrichtenfeld im Posteingang:
foreach ($result as $item) { if (!$this->checkExecutionTime($time_start)) break; // get msg header and stream uid $msg = $this->parseHeader($item);
Dies ist eine angepasste Version des öffentlich verfügbaren IMAP-Header-Parsing-Codes, der zusätzliche Informationen sammelt, die die SE zur Erledigung verschiedener Aufgaben benötigt. Grundsätzlich verwendet es imap_rfc822_parse_adrlist, um die Empfängerinformationen, die Nachrichten-ID, den Betreff und den Zeitstempel (oder Absenderinformationen beim Scannen des Ordners „Gesendet“) zu ermitteln:
public function parseHeader($header) { // parses header object returned from imap_fetch_overview if (!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->personal) : "", "email" => (isset($fi->mailbox) && isset($fi->host)) ? $fi->mailbox . "@" . $fi->host : "", "mailbox" => (isset($fi->mailbox)) ? $fi->mailbox : "", "host" => (isset($fi->host)) ? $fi->host : "", "subject" => (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 : "" ); // handles fetch with uid and rfc header parsing 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->personal; if (isset($to_info->mailbox)) $msg['rx_mailbox']=$to_info->mailbox; if (isset($to_info->host)) $msg['rx_host']=$to_info->host; } return $msg; } }
Wir erstellen in der Datenbank Datensätze für den Absender und den Briefumschlag:
// skip any system messages if ($msg['email']==$system_email) continue; // if udate is too old, skip msg if (time()-$msg['udate']>$this->scan_seconds) continue; // skip msg // default action $action = self::ACTION_MOVE_FILTERED; $isNew = $s->isNew($account_id,$msg["email"]); // look up sender, if new, create them $sender_id = $s->add($user_id,$account_id,$msg["personal"], $msg["mailbox"], $msg["host"],0); $sender = Sender::model()->findByPk($sender_id); // create a message in db if needed $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);
Wenn der Absender für uns neu (unbekannt) ist, senden wir eine Whitelist-Challenge-E-Mail (wir gehen ausführlich auf Whitelist-Challenges im nächsten Abschnitt unten ein):
if ($isNew) { $this->challengeSender($user_id,$account_id,$sender,$message); }
Als nächstes ermitteln wir, ob der Benutzer möglicherweise eine Nachricht aus einem anderen Ordner zurück in den Posteingang gezogen hat – mit der Absicht, sie per Drag & Drop zu trainieren. Wenn ja, stellen wir das Training für diesen Absender auf den Posteingang ein. Mit anderen Worten: Das nächste Mal möchten wir E-Mails nur von diesem Absender an den Posteingang weiterleiten:
if ($message['status'] == Message::STATUS_FILTERED || $message['status'] == Message::STATUS_REVIEW || ($message['status'] == Message::STATUS_TRAINED && $message['folder_id'] <> $folder_id) || ($message['status'] == Message::STATUS_ROUTED && $message['folder_id'] <> $folder_id)) { // then it's a training $action = self::ACTION_TRAIN_INBOX; } else if (($message['status'] == Message::STATUS_TRAINED || $message['status'] == Message::STATUS_ROUTED) && $message['folder_id'] == $folder_id) { // if trained already or routed to inbox already, skip it $action = self::ACTION_SKIP; echo 'Trained previously, skip ';lb(); continue; }
Wenn nicht, bereiten wir die Nachricht so vor, dass sie zur weiteren Verarbeitung in den Filterordner verschoben wird. Erstens senden wir möglicherweise eine Benachrichtigung an das Telefon des Benutzers, wenn der Absender der Benachrichtigung oder ein Schlüsselwort übereinstimmt (und es ist keine ruhige Zeit):
if ($action == self::ACTION_MOVE_FILTERED) { $cnt+=1; if ($sender->exclude_quiet_hours == Sender::EQH_YES or !$this->isQuietHours($user_id)) { // send smartphone notifications based on sender if ($sender->alert==Sender::ALERT_YES) { $this->notify($sender,$message,Monitor::NOTIFY_SENDER); } // send notifications based on keywords if (AlertKeyword::model()->scan($msg)) { $this->notify($sender,$message,Monitor::NOTIFY_KEYWORD); } } // move imap msg to +Filtering echo 'Moving to +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 'moved<br />'; $m->setStatus($message_id,Message::STATUS_FILTERED); } }
Wenn eine Nachricht in den Posteingang gezogen wird, aktualisieren wir unsere Trainingseinstellungen:
else if ($action == self::ACTION_TRAIN_INBOX) { // set sender folder_id to inbox echo 'Train to Inbox';lb(); $m->setStatus($message_id,Message::STATUS_TRAINED); // only train sender when message is newer than last setting if ($msg['udate']>=$sender['last_trained']) { $s->setFolder($sender_id,$folder_id); } }
Die sekundäre Verarbeitungsmethode heißt processFiltering
,也在DaemonController.php
中. Es übernimmt die zeitaufwändigere Aufgabe, Nachrichten in die entsprechenden Ordner zu verschieben:
public function actionIndex() { // processes messages in @Filtering to appropriate folders $r = new Remote(); $r->processFiltering(); // Record timestamp of cronjob for monitoring $file = file_put_contents('./protected/runtime/cronstamp.txt',time(),FILE_USE_INCLUDE_PATH); }
Diese Methode öffnet Ihr E-Mail-Konto, um nach aktuellen Nachrichten zu suchen und Daten darüber zu sammeln. Es verwendet auch imap_search
、imap_fetch_overview
和 parseHeader
:
$tstamp = time()-(7*24*60*60); // 7 days ago $recent_messages = @imap_search($this->stream, 'SINCE "'.date("j F Y",$tstamp).'"',SE_UID); if ($recent_messages===false) continue; // to do - continue into next account $result = imap_fetch_overview($this->stream, implode(',',array_slice($recent_messages,0,$message_limit)),FT_UID); foreach ($result as $item) { $cnt+=1; if (!$this->checkExecutionTime($time_start)) break; // get msg header and stream uid $msg = $this->parseHeader($item);
Die Hauptverarbeitungsschleife für jede Nachricht im Filterordner ist sehr detailliert. Zuerst schauen wir uns die Empfängeradresse an, da SE es ermöglicht, Ordner anhand der Empfängeradresse zu trainieren. Beispielsweise wird eine an die Domain happyvegetarian.com gesendete Nachricht in den Veggie-Ordner verschoben:
// Set the default action to move to the review folder $action = self::ACTION_MOVE_REVIEW; $destination_folder =0; // look up & create recipient $recipient_id = $r->add($user_id,$account_id,$msg['rx_email'],0); $routeByRx = $this->routeByRecipient($recipient_id); if ($routeByRx!==false) { $action = $routeByRx->action; $destination_folder = $routeByRx->destination_folder; }
Wir suchen dann nach dem Absender und erstellen einen neuen Datensatz in der Datenbank (falls erforderlich). Wenn für den Absender eine Schulung vorhanden ist, können wir den Zielordner festlegen:
// look up sender, if new, create them $sender_id = $s->add($user_id,$account_id,$msg["personal"], $msg["mailbox"], $msg["host"],0); $sender = Sender::model()->findByPk($sender_id); // if sender destination known, route to folder if ($destination_folder ==0 && $sender['folder_id'] > 0) { $action = self::ACTION_ROUTE_FOLDER; $destination_folder = $sender['folder_id']; }
Wenn sich der ungeschulte (neue) Absender über eine Whitelist-Challenge authentifiziert hat (auf die wir im nächsten Abschnitt weiter unten eingehen), leiten wir diese Nachricht an den Posteingang weiter:
// whitelist verified senders go to inbox if ($sender->is_verified==1 && $sender['folder_id'] ==0 && UserSetting::model()->useWhitelisting($user_id)) { // place message in inbox $action = self::ACTION_ROUTE_FOLDER; $destination_folder = Folder::model()->lookup($account_id,$this->path_inbox); }
Wir erstellen dann einen Nachrichteneintrag in der Datenbank, der die Umschlaginformationen zu dieser Nachricht enthält:
// create a message in db $message = Message::model()->findByAttributes(array('message_id'=>$msg['message_id'])); if (!empty($message)) { // message exists already, $message_id = $message->id; } else { $message_id = $m->add($user_id,$account_id,0,$sender_id,$msg['message_id'],$msg['subject'],$msg['udate'],$msg['in_reply_to']); }
Wenn die Nachricht von einem unbekannten, nicht bestätigten Absender stammt, können wir die Nachricht in einen Überprüfungsordner verschieben. Der Bewertungsordner enthält alle Nachrichten von Absendern, die wir nicht kennen.
Wenn die Nachricht von einem bekannten Absender stammt und wir das Ziel identifiziert haben, können wir sie verschieben, solange keine Ruhezeit ist (und „Bitte nicht stören“ deaktiviert ist):
if ($recipient_id!==false) $m->setRecipient($message_id,$recipient_id); if ($action == self::ACTION_MOVE_REVIEW) { echo 'Moving to +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 'moved<br />'; $m->setStatus($message_id,Message::STATUS_REVIEW); } } else if ($action == self::ACTION_ROUTE_FOLDER || $action == self::ACTION_ROUTE_FOLDER_BY_RX) { // lookup folder name by folder_id $folder = Folder::model()->findByPk($destination_folder); // if inbox & quiet hours, don't route right now if (strtolower($folder['name'])=='inbox' and $sender->exclude_quiet_hours == Sender::EQH_NO and $this->isQuietHours($user_id)) continue; echo 'Moving to '.$folder['name'];lb(); $mark_read = Folder::model()->isMarkRead($folder['mark_read']) || Sender::model()->isMarkRead($sender['mark_read']); //$result = @imap_mail_move($this->stream,$msg['uid'],$folder['name'],CP_UID); $result = $this->messageMoveHandler($msg['uid'],$folder['name'],$mark_read); if ($result) { echo 'moved<br />'; $m->setStatus($message_id,Message::STATUS_ROUTED); $m->setFolder($message_id,$destination_folder); } }
In ruhigen Zeiten werden Nachrichten hauptsächlich in gefilterten Ordnern gespeichert.
Alle paar Stunden erstellt ein anderer Prozess mithilfe von Nachrichtentabellendatensätzen einen Nachrichtenauszug, um kürzlich empfangene und gefilterte E-Mails und deren Weiterleitung zu ermitteln.
Das Ziel der Whitelist-Challenge besteht darin, alle Nachrichten von unbekannten Absendern, wie z. B. Marketing-Bots oder Spammer, die sich möglicherweise in Ihrem Posteingang befinden, zurückzuhalten. SE legt Nachrichten von unbekannten Absendern in einem Überprüfungsordner ab. Wenn Sie jedoch die Whitelist aktivieren, senden wir eine Herausforderungs-E-Mail, um dem Absender die Möglichkeit zu geben, zu überprüfen, ob es sich um einen Menschen handelt. Wenn sie antworten, verschieben wir die Nachricht in Ihren Posteingang. Wenn sich herausstellt, dass die E-Mail unerwünscht ist, können Sie die Nachricht aus dem Digest löschen oder sie in einen beliebigen Ordner ziehen, in den Sie sie trainieren möchten.
Benutzer können Whitelisting in den Einstellungen aktivieren und deaktivieren:
Um Whitelisting zu implementieren, senden wir jedes Mal eine E-Mail-Aufforderung, wenn ein neuer Absender eine Nachricht erhält:
if ($isNew) { $this->challengeSender($user_id,$account_id,$sender,$message); }
ChallengeSender
Senden Sie Benutzern einen verschlüsselten Link, auf den sie klicken können. Wir haben außerdem einige Sicherheitsvorkehrungen getroffen, um sicherzustellen, dass wir nicht in einer E-Mail-Schleife mit Abwesenheitsnachrichten stecken bleiben:
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'].' <no-reply@'.$yg->mg_domain.'>'; else $from = 'Filter <no-reply@'.$yg->mg_domain.'>'; $cred = Account::model()->getCredentials($ac->cred); $account_email = $cred[0]; unset($cred); // safety: checks no recent email if ($sender->last_emailed>(time()-(48*60*60))) return false; if ($sender->isBot($sender['email'])) { // to do - can also set this person to bulk by default return false; } $link=Yii::app()->getBaseUrl(true)."/sender/verify/s/".$sender->id."/m/".$message->id.'/u/'.$message->udate; $subject = 'Please verify the message you sent to '.$account_email; $body="<p>Hi,<br /><br /> I'm trying to reduce unsolicited email. Could you please verify your email address by clicking the link below:<br /><a href=\"".$link.'">'.$link.'</a><br /><br />Verifying your email address will help speed your message into my inbox. Thanks for your assistance!</p>'; $yg->send_html_message($from, $sender['email'], $subject,$body); // update last_emailed $sender->touchLastEmailed($sender->id); }
Wenn der Empfänger dann auf den verschlüsselten Link klickt, verifizieren wir ihn in der Datenbank. Der Sender-Controller bearbeitet diese Anfragen und prüft ihre Gültigkeit:
public function actionVerify($s = 0, $m=0,$u=0) { // verify that secure msg url from digest is valid, log in user, show 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 = 'Thank you for your assistance. I\'ll respond to your email as soon as possible.'; $a = new Advanced(); $a->verifySender($msg->account_id,$sender_id); } else { $result = 'Sorry, we could not verify your email address.'; } $this->render('verify',array( 'result'=>$result, )); }
Dadurch wird unsere Verarbeitungsschleife angewiesen, diese Nachricht und zukünftige Nachrichten von diesem Absender in den Posteingang zu verschieben.
有时,查看您已发送但未收到回复的消息摘要会有所帮助。为了识别这些邮件,Simplify Email 会监视已发送但尚未收到回复的邮件。
我们收到的每条消息都包含一个唯一的 ID,称为 message_id(IMAP 规范的一部分)。它通常看起来像这样:
Message-Id: <CALe0OAaF3fb3d=gCq2Fs=Ex61Qp6FdbiA4Mvs6kTQ@mail.gmail.com>
此外,当发送消息以回复其他消息时,它们有一个 in_reply_to
字段,该字段链接回原始 message_id
。
因此,我们使用 SQL 查询来查找所有收到的消息,这些消息没有引用其 message_id
的相应回复消息。为此,我们在没有 in_reply_to
id 的情况下使用 LEFT OUTER JOIN:
public function 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 is null and fi_sent_message.udate > ".(time()-(3600*24*$range_days))." and fi_sent_message.subject ".$subject_compare." like 'Re: %' ORDER BY fi_sent_message.udate DESC")->queryAll(); return $query; }
我们使用 $subject_compare
模式来区分我们发送的尚未回复的消息和我们发送给尚未回复的线程的回复。以下是您帐户中的未回复消息报告:
SE 还将此信息作为可选摘要提供,称为未回复电子邮件摘要。您可以每天、每隔几天或每周收到它。
我们还使用类似的 SQL 表格和 Google Charts 来提供有关某些人向您发送电子邮件的频率的报告:
public function reportInbound($account_id,$range=30,$limit = 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 :range DAY ) GROUP BY sender_id ORDER BY cnt desc LIMIT :limit ')->bindValue('range',$range)->bindValue('account_id',$account_id)->bindValue('limit',$limit)->queryAll(); return $result; }
我很快就会撰写更多有关 Tuts+ 的 Google Charts 的文章。
我希望您已经发现 Simplify Email 足够有趣,可以尝试 PHP IMAP 编程。您可以构建许多很酷的功能,而不需要大型电子邮件提供商做任何新的事情。
如果您有任何疑问或更正,请在评论中提出。如果您想继续关注我未来的 Tuts+ 教程和其他系列,请关注 @reifman 或访问我的作者页面。您也可以在这里联系我。
以下是一些您可能会觉得有用的附加链接:
Das obige ist der detaillierte Inhalt vonErstellen Sie erweiterte E-Mail-Funktionen mit IMAP und PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!