Table des matières
Utilisez IMAP
Comment simplifier le fonctionnement du courrier électronique
1. Vérifiez votre boîte de réception et filtrez les messages
Méthode ProcessInbox
Méthode ProcessFiltering
2. Mettre en œuvre un défi de liste blanche pour les expéditeurs inconnus
后续步骤
相关链接
Maison développement back-end tutoriel php Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Aug 31, 2023 am 08:33 AM
php imap 邮件功能

Ce que vous allez créer

Dans ce didacticiel, je vais vous montrer quelques exemples concrets d'utilisation de PHP et IMAP pour créer de nouvelles fonctionnalités de gestion du courrier électronique que les grands fournisseurs de courrier électronique n'ont pas encore créées pour nous.

Mon intérêt pour cela a commencé en 2010 lorsque j'ai écrit Douze idées Gmail pour révolutionner (encore) le courrier électronique, mais la plupart des idées que je souhaitais étaient encore hors de portée. Malgré son importance, l’e-mail en tant qu’application a été assez lent à innover.

Nous sommes inondés d'e-mails et la gestion de notre boîte de réception peut encore être une lourde charge. Les services de courrier et les clients n’ont pas fait grand-chose pour nous aider à cet égard. La plupart des e-mails que nous recevons sont envoyés par des machines plutôt que par des personnes, mais nous devons tous les traiter individuellement.

L'analyse de mes propres e-mails a montré que j'avais reçu des e-mails de plus de 230 expéditeurs automatisés, alors que le nombre d'expéditeurs réels était bien inférieur. J'en ai marre de créer des filtres et de remplir d'innombrables formulaires de désabonnement dans Gmail. Je souhaite avoir plus de contrôle sur la gestion de mes emails et me simplifier la vie.

Enfin, au cours de la dernière année, j'ai décidé de créer les fonctionnalités dont j'avais besoin. Le résultat est Simplify Email (SE), une petite application Web que vous pouvez héberger vous-même et qui offre une variété de nouvelles fonctionnalités de messagerie intéressantes, que vous pouvez toutes consulter sur le site Web du projet.

Ce qui est intéressant avec SE, c'est qu'il s'agit d'une plate-forme de lecture, d'analyse, d'acheminement et de gestion des e-mails - les possibilités sont nombreuses. Le courrier électronique simplifié est essentiellement un terrain de jeu programmable pour « pirater » votre propre courrier électronique.

Je vais vous expliquer le code de trois exemples de traitement d'e-mails dans SE à l'aide de PHP, IMAP et MySQL :

  1. Vérifiez votre boîte de réception et filtrez vos messages
  2. Mise en œuvre d'une requête de liste blanche pour les expéditeurs inconnus
  3. Signaler un e-mail sans réponse

Ce tutoriel vous donnera certainement une longueur d'avance sur l'écriture de code IMAP en PHP. Mais vous pouvez également utiliser directement la base de code Simplify Email. Vous pouvez acheter le code pour seulement 10 $, et il existe une ancienne version open source (à laquelle manque certaines des fonctionnalités décrites ci-dessous). Des instructions d'installation pour les configurations Linux typiques sont fournies. Je propose également des images préinstallées chez Digital Ocean pour 25 $ et propose une installation avec voiturier portable. SE est écrit en PHP dans le framework Yii.

Veuillez noter qu'à moins de compiler une bibliothèque IMAP sécurisée pour PHP, vous ne pourrez pas accéder à la plupart des serveurs de messagerie depuis votre machine de développement locale. C'est l'une des raisons pour lesquelles j'encourage les gens à exécuter Simplify Email sous forme de droplet dans Digital Ocean. Il existe également quelques conseils pour sécuriser votre compte Google lorsque vous vous connectez via IMAP.

Utilisez IMAP

Comment simplifier le fonctionnement du courrier électronique

Avec SE, vous pouvez continuer à utiliser le client de messagerie de votre choix sur le Web et les appareils mobiles. Vous n'avez pas besoin de modifier vos applications ou vos habitudes personnelles. SE accède à votre compte de messagerie en coulisse via IMAP ; en tant qu'assistant personnel intelligent, SE prétraite votre courrier électronique, déplaçant les messages vers l'emplacement approprié en fonction de tout ce que vous lui dites.

Lorsqu'un message arrive d'un expéditeur familier, SE le déplace vers le dossier que vous spécifiez. Lorsqu'un expéditeur inconnu reçoit un message pour la première fois, celui-ci est déplacé vers un dossier de révision.

Toutes les quelques heures (ou aussi souvent que vous le souhaitez), SE vous enverra un résumé de l'endroit où il a déplacé vos messages et des messages en cours d'examen. Notez que le dossier d'audit contient un lien vers l'expéditeur de la formation, ce qui facilite grandement la formation du SE au fil du temps.

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Vous pouvez parcourir votre dossier de révision à tout moment - pas besoin d'attendre l'arrivée des résumés. Mais l’avantage de SE est que vous n’avez plus besoin de parcourir vos dossiers ; vous pouvez simplement lire le résumé pour visualiser les emails reçus et former les nouveaux expéditeurs.

1. Vérifiez votre boîte de réception et filtrez les messages

SE utilise plusieurs tâches cron à exécuter en arrière-plan du serveur. Chacun est appelé depuis DaemonController.php.

Le premier, processInbox, est appelé fréquemment et doit être effectué rapidement : son travail consiste à passer au crible les e-mails et à les sortir de la boîte de réception et dans des dossiers catégorisés, appelés dossiers de filtrage, le plus rapidement possible.

Le second, processFiltering, est plus gourmand en traitement et effectue des opérations plus approfondies sur l'e-mail, déplaçant finalement le message vers sa destination finale.

Méthode ProcessInbox

La tâche Cron est appelée régulièrement processInbox :

public function actionInbox() {
    
    // moves inbox messages to @filtering
    // runs frequently 
    $r = new Remote();
    $r->processInbox();	  
    
}
Copier après la connexion

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Pour chaque compte, nous décryptons vos identifiants de messagerie, puis utilisons imap_open pour créer un flux IMAP dans votre dossier de boîte de réception :

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()));
}
Copier après la connexion

Dans processInbox nous utilisons les fonctions de la bibliothèque PHP imap_search et imap_fetch_overview pour récupérer le tableau des messages :

// 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);
Copier après la connexion

Ensuite, nous traitons l'ensemble des messages dans la boîte de réception :

foreach ($result as $item) {         
   if (!$this->checkExecutionTime($time_start)) break;
   // get msg header and stream uid
   $msg = $this->parseHeader($item); 
Copier après la connexion

Il s'agit d'une version adaptée du code d'analyse d'en-tête IMAP accessible au public qui collecte les informations supplémentaires nécessaires au SE pour accomplir diverses tâches. Fondamentalement, il utilise imap_rfc822_parse_adrlist pour déterminer les informations sur le destinataire, l'ID du message, l'objet et l'horodatage (ou les informations sur l'expéditeur lors de l'analyse du dossier Envoyé) :

  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;
    }
  }
Copier après la connexion

Nous créons des enregistrements dans la base de données pour l'expéditeur et l'enveloppe du courrier :

   // 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);
Copier après la connexion

Si l'expéditeur est nouveau (inconnu) pour nous, nous enverrons un e-mail de défi de liste blanche (nous discutons en détail des défis de liste blanche dans la section suivante ci-dessous) :

if ($isNew) {
        $this->challengeSender($user_id,$account_id,$sender,$message);
      }
Copier après la connexion
Copier après la connexion

Ensuite, nous déterminons si l'utilisateur a peut-être fait glisser un message d'un autre dossier vers la boîte de réception - dans l'intention de l'entraîner par glisser-déposer. Si tel est le cas, nous définirons la formation de cet expéditeur dans la boîte de réception. En d'autres termes, la prochaine fois, nous souhaitons acheminer uniquement le courrier de cet expéditeur vers la boîte de réception :

         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;  
   	  }
Copier après la connexion

Sinon, nous préparerons le message à déplacer vers le dossier Filtre pour un traitement ultérieur. Tout d'abord, nous pouvons envoyer une notification sur le téléphone de l'utilisateur si l'expéditeur de la notification correspond ou si un mot-clé correspond (et ce n'est pas une période calme) :

   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);
     }
   } 
Copier après la connexion

Si un message est glissé dans la boîte de réception, nous mettrons à jour nos paramètres d'entraînement :

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);
     } 
   }
Copier après la connexion

Méthode ProcessFiltering

La méthode de traitement secondaire est appelée processFiltering,也在DaemonController.php中. Il s'agit du travail le plus fastidieux consistant à déplacer les messages vers les dossiers appropriés :

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);	  
}
Copier après la connexion

Cette méthode ouvre votre compte de messagerie pour rechercher des messages récents et collecter des données à leur sujet. Il utilise également imap_searchimap_fetch_overviewparseHeader:

$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);            
Copier après la connexion

La boucle de traitement principale pour chaque message du dossier filtre est très détaillée. Nous regardons d'abord l'adresse du destinataire, puisque SE permet de former des dossiers par adresse de destinataire, par exemple un message envoyé au domaine happyvegetarian.com ira dans le dossier veggie :

 // 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;
 }            
Copier après la connexion

Nous recherchons ensuite l'expéditeur et créons un nouvel enregistrement dans la base de données (si nécessaire). Si une formation existe pour l'expéditeur, nous pouvons définir le dossier cible :

 // 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'];      
 }
 
Copier après la connexion

Si le (nouvel) expéditeur non formé s'est authentifié via un défi de liste blanche (dont nous discutons dans la section suivante ci-dessous), alors nous acheminerons ce message vers la boîte de réception :

// 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);             
 }
Copier après la connexion

Nous créons ensuite une entrée de message dans la base de données qui contient les informations d'enveloppe concernant ce message :

  // 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']);         
  }
Copier après la connexion

Si le message provient d'un expéditeur inconnu et non vérifié, nous pouvons déplacer le message vers un dossier de révision. Le dossier de révision contient tous les messages provenant d'expéditeurs que nous ne reconnaissons pas.

Si le message provient d'un expéditeur connu et que nous avons identifié la destination, nous pouvons le déplacer tant que ce n'est pas une heure calme (et que la fonction Ne pas déranger est désactivée) :

  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);
  }
}
Copier après la connexion

Pendant les périodes calmes, les messages sont principalement enregistrés dans des dossiers filtrés.

Toutes les quelques heures, un processus différent crée un résumé des messages à l'aide des enregistrements de la table des messages pour déterminer les e-mails récemment reçus et filtrés ainsi que la manière dont ils ont été acheminés.

2. Mettre en œuvre un défi de liste blanche pour les expéditeurs inconnus

L'objectif du défi de la liste blanche est de conserver tous les messages provenant d'expéditeurs inconnus, tels que des robots marketing ou des spammeurs, qui pourraient se trouver dans votre boîte de réception. SE place les messages provenant d'expéditeurs inconnus dans un dossier de révision. Cependant, si vous activez la liste blanche, nous enverrons un e-mail de défi pour donner à l'expéditeur la possibilité de vérifier qu'il est humain. S'ils répondent, nous déplacerons le message vers votre boîte de réception. Si l'e-mail s'avère indésirable, vous pouvez supprimer le message du résumé ou le faire glisser vers n'importe quel dossier dans lequel vous souhaitez l'entraîner.

Les utilisateurs peuvent activer et désactiver la liste blanche dans les paramètres :

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

Pour mettre en œuvre la liste blanche, nous enverrons un défi par e-mail chaque fois qu'un nouvel expéditeur recevra un message :

if ($isNew) {
        $this->challengeSender($user_id,$account_id,$sender,$message);
      }
Copier après la connexion
Copier après la connexion

ChallengeSender Envoyez aux utilisateurs un lien codé sur lequel ils pourront cliquer. Nous avons également mis en place certaines mesures de protection pour garantir que nous ne restons pas coincés dans une boucle de courrier électronique avec des messages d'absence du bureau :

 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);
 }
Copier après la connexion

Ensuite, si le destinataire clique sur le lien encodé, nous le vérifions dans la base de données. Le contrôleur émetteur traite ces demandes et vérifie leur validité :

  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,
		));
  }
Copier après la connexion

Cela indique à notre boucle de traitement de déplacer ce message et les futurs messages de cet expéditeur vers la boîte de réception.

3。报告未回复的电子邮件

有时,查看您已发送但未收到回复的消息摘要会有所帮助。为了识别这些邮件,Simplify Email 会监视已发送但尚未收到回复的邮件。

我们收到的每条消息都包含一个唯一的 ID,称为 message_id(IMAP 规范的一部分)。它通常看起来像这样:

Message-Id: <CALe0OAaF3fb3d=gCq2Fs=Ex61Qp6FdbiA4Mvs6kTQ@mail.gmail.com>
Copier après la connexion

此外,当发送消息以回复其他消息时,它们有一个 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;
  }
Copier après la connexion

我们使用 $subject_compare 模式来区分我们发送的尚未回复的消息和我们发送给尚未回复的线程的回复。以下是您帐户中的未回复消息报告:

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

SE 还将此信息作为可选摘要提供,称为未回复电子邮件摘要。您可以每天、每隔几天或每周收到它。

我们还使用类似的 SQL 表格和 Google Charts 来提供有关某些人向您发送电子邮件的频率的报告:

Développez des fonctionnalités de messagerie avancées avec IMAP et PHP

  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;
  }
Copier après la connexion

我很快就会撰写更多有关 Tuts+ 的 Google Charts 的文章。

后续步骤

我希望您已经发现 Simplify Email 足够有趣,可以尝试 PHP IMAP 编程。您可以构建许多很酷的功能,而不需要大型电子邮件提供商做任何新的事情。

如果您有任何疑问或更正,请在评论中提出。如果您想继续关注我未来的 Tuts+ 教程和其他系列,请关注 @reifman 或访问我的作者页面。您也可以在这里联系我。

相关链接

以下是一些您可能会觉得有用的附加链接:

  • 简化电子邮件
  • 简化电子邮件简介(视频)
  • (再次)彻底改变电子邮件的 12 个 Gmail 创意

  • BoingBoing 中简化电子邮件的报道(此处和此处)
  • PHP IMAP 参考
  • Yii 框架简介 (Tuts+)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 apporte plusieurs nouvelles fonctionnalités, améliorations de sécurité et de performances avec une bonne quantité de dépréciations et de suppressions de fonctionnalités. Ce guide explique comment installer PHP 8.4 ou mettre à niveau vers PHP 8.4 sur Ubuntu, Debian ou leurs dérivés. Bien qu'il soit possible de compiler PHP à partir des sources, son installation à partir d'un référentiel APT comme expliqué ci-dessous est souvent plus rapide et plus sécurisée car ces référentiels fourniront les dernières corrections de bogues et mises à jour de sécurité à l'avenir.

Comment configurer Visual Studio Code (VS Code) pour le développement PHP Comment configurer Visual Studio Code (VS Code) pour le développement PHP Dec 20, 2024 am 11:31 AM

Visual Studio Code, également connu sous le nom de VS Code, est un éditeur de code source gratuit – ou environnement de développement intégré (IDE) – disponible pour tous les principaux systèmes d'exploitation. Avec une large collection d'extensions pour de nombreux langages de programmation, VS Code peut être c

Comment analysez-vous et traitez-vous HTML / XML dans PHP? Comment analysez-vous et traitez-vous HTML / XML dans PHP? Feb 07, 2025 am 11:57 AM

Ce tutoriel montre comment traiter efficacement les documents XML à l'aide de PHP. XML (Language de balisage extensible) est un langage de balisage basé sur le texte polyvalent conçu à la fois pour la lisibilité humaine et l'analyse de la machine. Il est couramment utilisé pour le stockage de données et

7 fonctions PHP que je regrette de ne pas connaître auparavant 7 fonctions PHP que je regrette de ne pas connaître auparavant Nov 13, 2024 am 09:42 AM

Si vous êtes un développeur PHP expérimenté, vous aurez peut-être le sentiment d'y être déjà allé et de l'avoir déjà fait. Vous avez développé un nombre important d'applications, débogué des millions de lignes de code et peaufiné de nombreux scripts pour réaliser des opérations.

Expliquez les jetons Web JSON (JWT) et leur cas d'utilisation dans les API PHP. Expliquez les jetons Web JSON (JWT) et leur cas d'utilisation dans les API PHP. Apr 05, 2025 am 12:04 AM

JWT est une norme ouverte basée sur JSON, utilisée pour transmettre en toute sécurité des informations entre les parties, principalement pour l'authentification de l'identité et l'échange d'informations. 1. JWT se compose de trois parties: en-tête, charge utile et signature. 2. Le principe de travail de JWT comprend trois étapes: la génération de JWT, la vérification de la charge utile JWT et l'analyse. 3. Lorsque vous utilisez JWT pour l'authentification en PHP, JWT peut être généré et vérifié, et les informations sur le rôle et l'autorisation des utilisateurs peuvent être incluses dans l'utilisation avancée. 4. Les erreurs courantes incluent une défaillance de vérification de signature, l'expiration des jetons et la charge utile surdimensionnée. Les compétences de débogage incluent l'utilisation des outils de débogage et de l'exploitation forestière. 5. L'optimisation des performances et les meilleures pratiques incluent l'utilisation des algorithmes de signature appropriés, la définition des périodes de validité raisonnablement,

Programme PHP pour compter les voyelles dans une chaîne Programme PHP pour compter les voyelles dans une chaîne Feb 07, 2025 pm 12:12 PM

Une chaîne est une séquence de caractères, y compris des lettres, des nombres et des symboles. Ce tutoriel apprendra à calculer le nombre de voyelles dans une chaîne donnée en PHP en utilisant différentes méthodes. Les voyelles en anglais sont a, e, i, o, u, et elles peuvent être en majuscules ou en minuscules. Qu'est-ce qu'une voyelle? Les voyelles sont des caractères alphabétiques qui représentent une prononciation spécifique. Il y a cinq voyelles en anglais, y compris les majuscules et les minuscules: a, e, i, o, u Exemple 1 Entrée: String = "TutorialSpoint" Sortie: 6 expliquer Les voyelles dans la chaîne "TutorialSpoint" sont u, o, i, a, o, i. Il y a 6 yuans au total

Expliquez la liaison statique tardive en PHP (statique: :). Expliquez la liaison statique tardive en PHP (statique: :). Apr 03, 2025 am 12:04 AM

Liaison statique (statique: :) ​​implémente la liaison statique tardive (LSB) dans PHP, permettant à des classes d'appel d'être référencées dans des contextes statiques plutôt que de définir des classes. 1) Le processus d'analyse est effectué au moment de l'exécution, 2) Recherchez la classe d'appel dans la relation de succession, 3) il peut apporter des frais généraux de performance.

Quelles sont les méthodes PHP Magic (__construct, __ destruct, __ call, __get, __set, etc.) et fournir des cas d'utilisation? Quelles sont les méthodes PHP Magic (__construct, __ destruct, __ call, __get, __set, etc.) et fournir des cas d'utilisation? Apr 03, 2025 am 12:03 AM

Quelles sont les méthodes magiques de PHP? Les méthodes magiques de PHP incluent: 1. \ _ \ _ Construct, utilisé pour initialiser les objets; 2. \ _ \ _ Destruct, utilisé pour nettoyer les ressources; 3. \ _ \ _ Appel, gérer les appels de méthode inexistants; 4. \ _ \ _ GET, Implémentez l'accès à l'attribut dynamique; 5. \ _ \ _ SET, Implémentez les paramètres d'attribut dynamique. Ces méthodes sont automatiquement appelées dans certaines situations, améliorant la flexibilité et l'efficacité du code.

See all articles