明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

用PHP完成POP3郵件的收。ǘ

[摘要]用PHP實(shí)現(xiàn)POP3收取郵件的類(作者:陳俊清 2000年10月18日 11:56)  現(xiàn)在讓我們來(lái)用PHP實(shí)現(xiàn)一個(gè)通過(guò)POP3協(xié)議收取信件的類吧,這個(gè)類中所用到的一些sock操作的函數(shù),不另做特殊...
用PHP實(shí)現(xiàn)POP3收取郵件的類
(作者:陳俊清 2000年10月18日 11:56)

  現(xiàn)在讓我們來(lái)用PHP實(shí)現(xiàn)一個(gè)通過(guò)POP3協(xié)議收取信件的類吧,這個(gè)類中所用到的一些sock操作的函數(shù),不另做特殊說(shuō)明,請(qǐng)參考php的有關(guān)資料。通過(guò)這個(gè)實(shí)例,相信你也會(huì)和我一樣,感覺(jué)到PHP中對(duì)于sock操作的靈活、方便和功能的強(qiáng)大。

  首先,我們來(lái)說(shuō)明一下這個(gè)類中需要用到的一些內(nèi)部成員變量:(這些變量應(yīng)該都是對(duì)外封閉的,可是由于php對(duì)類的成員變量沒(méi)有private與publice之類的分別,只好就這么直接定義了。這是PHP的一個(gè)令人遺憾的地方。)

  

 。.成員變量說(shuō)明

  class pop3

  {

      var $hostname=""; // POP主機(jī)名

      var $port=110; // 主機(jī)的POP3端口,一般是110號(hào)端口

   var $timeout=5;  // 連接主機(jī)的最大超時(shí)時(shí)間 

      var $connection=0; // 保存與主機(jī)的連接

      var $state="DISCONNECTED"; // 保存當(dāng)前的狀態(tài) 

      var $debug=0;  // 做為標(biāo)識(shí),是否在調(diào)試狀態(tài),是的話,輸出調(diào)試信息

   var $err_str='';  // 如果出錯(cuò),這里保存錯(cuò)誤信息

   var $err_no;   //如果出錯(cuò),這里保存錯(cuò)誤號(hào)碼

   var $resp; // 臨時(shí)保存服務(wù)器的響應(yīng)信息

   var $apop; // 指示需要使用加密方式進(jìn)行密碼驗(yàn)證,一般服務(wù)器不需要

   var $messages; // 郵件數(shù)

   var $size; //各郵件的總大小

   var $mail_list; // 一個(gè)數(shù)組,保存各個(gè)郵件的大小及其在郵件服務(wù)器上序號(hào)

  

   var $head=array(); // 郵件頭的內(nèi)容,數(shù)組

   var $body=array(); // 郵件體的內(nèi)容,數(shù)組;

  

  2.當(dāng)然,這其中的有些變量,僅通過(guò)這樣一個(gè)簡(jiǎn)單的說(shuō)明并不能完全了解如何使用,下面我就逐個(gè)來(lái)說(shuō)明這個(gè)類實(shí)現(xiàn)中的一些主要方法:

  Function pop3($server="192.100.100.1",$port=110,$time_out=5)

  {$this->hostname=$server;

   $this->port=$port;

   $this->timeout=$time_out;

   return true;

  }

  熟悉面向?qū)ο缶幊痰呐笥岩豢淳蜁?huì)知道,這是這個(gè)類的構(gòu)造函數(shù),在初始化這個(gè)類時(shí),可以給出這幾個(gè)最基本的參數(shù):pop3服務(wù)器的地址,端口號(hào),及連接服務(wù)器時(shí)的最大超時(shí)時(shí)間。一般來(lái)說(shuō),只需要給出POP3服務(wù)器的地址就行了。

  Function open()

  {

  if($this->hostname=="")

    {$this->err_str="無(wú)效的主機(jī)名!!";

   return false;

   }

  

  if ($this->debug) echo "正在打開 $this->hostname,$this->port,&$err_no, &$err_str, $this->timeout<BR>";

  if (!$this->connection=fsockopen($this->hostname,$this->port,&$err_no, &$err_str, $this->timeout))

   {

   $this->err_str="連接到POP服務(wù)器失敗,錯(cuò)誤信息:".$err_str."錯(cuò)誤號(hào):".$err_no;

   return false;

   }

   else

   {

   $this->getresp();

   if($this->debug)        

      $this->outdebug($this->resp);

   if (substr($this->resp,0,3)!="+OK")

   {$this->err_str="服務(wù)器返回?zé)o效的信息:".$this->resp."請(qǐng)檢查POP服務(wù)器是否正確";

   return false;

   }

   $this->state="AUTHORIZATION";

   return true;

   }

  }

  該方法不需要任何參數(shù)就可建立與POP3服務(wù)器的sock連接。該方法又用到了另一個(gè)類中的方法$this->getresp();下面是這個(gè)方法的聲明:

  Function getresp()

   {

   for($this->resp="";;)

     {

         if(feof($this->connection))

               return false;

           $this->resp.=fgets($this->connection,100);

           $length=strlen($this->resp);

           if($length>=2 && substr($this->resp,$length-2,2)=="\r\n")

           {

            $this->resp=strtok($this->resp,"\r\n");

   return true;

           }

          }

      }

  這個(gè)方法取得服務(wù)器端的返回信息并進(jìn)行簡(jiǎn)單的處理:去掉最后的回車換行符,將返回信息保存在resp這個(gè)內(nèi)部變量中。這個(gè)方法在后面的多個(gè)操作中都將用到。另外,還有個(gè)小方法也在后面的多個(gè)操作中用到:

  Function outdebug($message)

  {

   echo htmlspecialchars($message)."<br>\n";

  }

  它的作用就是把調(diào)試信息$message顯示出來(lái),并把一些特殊字符進(jìn)行轉(zhuǎn)換以及在行尾加上<br>標(biāo)簽,這樣是為了使其輸出的調(diào)試信息便于閱讀和分析。

  建立起與服務(wù)器的sock連接之后,就要給服務(wù)器發(fā)送相關(guān)的命令了(請(qǐng)參見(jiàn)上面的與服務(wù)器對(duì)話的過(guò)程)從上面對(duì) POP對(duì)話的分析可以看到,每次都是發(fā)送一條命令,然后服務(wù)器給予一定的回應(yīng),如果命令的執(zhí)行是對(duì)的,回應(yīng)一般是以+OK開頭,后面是一些描述信息,所以,我們可以做一個(gè)通過(guò)發(fā)送命令的方法:

  Function command($command,$return_lenth=1,$return_code='+')

  {

   if ($this->connection==0)

   {

   $this->err_str="沒(méi)有連接到任何服務(wù)器,請(qǐng)檢查網(wǎng)絡(luò)連接";

   return false;

   }

   if ($this->debug)

   $this->outdebug(">>> $command");

   if (!fputs($this->connection,"$command\r\n"))

   {

   $this->err_str="無(wú)法發(fā)送命令".$command;

   return false;

   }

   else

   {

   $this->getresp();

   if($this->debug)

      $this->outdebug($this->resp);

   if (substr($this->resp,0,$return_lenth)!=$return_code)

   {

   $this->err_str=$command." 命令服務(wù)器返回?zé)o效:".$this->resp;

   return false;

   }

    else

   return true;

    }

  }

  這個(gè)方法可以接受三個(gè)參數(shù): $command--> 發(fā)送給服務(wù)器的命令; $return_lenth,$return_code ,指定從服務(wù)器的返回中取多長(zhǎng)的值做為命令返回的標(biāo)識(shí)以及這個(gè)標(biāo)識(shí)的正確值是什么。對(duì)于一般的pop操作來(lái)說(shuō),如果服務(wù)器的返回第一個(gè)字符為"+",則可以認(rèn)為命令是正確執(zhí)行了。也可以用前面提到過(guò)的三個(gè)字符"+OK"做為判斷的標(biāo)識(shí)。

  下面介紹的幾個(gè)方法則可以按照前述收取信件的對(duì)話去理解,因?yàn)橛嘘P(guān)的內(nèi)容已經(jīng)在前面做了說(shuō)明,因此下面的方法不做詳細(xì)的說(shuō)明,請(qǐng)參考其中的注釋:

  Function Login($user,$password) //發(fā)送用戶名及密碼,登錄到服務(wù)器

  {

  if($this->state!="AUTHORIZATION")

   {

   $this->err_str="還沒(méi)有連接到服務(wù)器或狀態(tài)不對(duì)";

   return false;

   }

  if (!$this->apop) //服務(wù)器是否采用APOP用戶認(rèn)證

   {

   if (!$this->command("USER $user",3,"+OK")) return false;

   if (!$this->command("PASS $password",3,"+OK")) return false;

   }

  else

   {

   //echo $this->resp=strtok($this->resp,"\r\n");

   if (!$this->command("APOP $user ".md5($this->greeting.$password),3,"+OK")) return false;

   }

  $this->state="TRANSACTION"; // 用戶認(rèn)證通過(guò),進(jìn)入傳送模式

  return true;

  }

  

  Function stat() // 對(duì)應(yīng)著stat命令,取得總的郵件數(shù)與總的大小

  {

  if($this->state!="TRANSACTION")

   {

   $this->err_str="還沒(méi)有連接到服務(wù)器或沒(méi)有成功登錄";

   return false;

   }

  if (!$this->command("STAT",3,"+OK"))

   return false;

   else

   {

   $this->resp=strtok($this->resp," ");

   $this->messages=strtok(" "); // 取得郵件總數(shù)

   $this->size=strtok(" "); //取得總的字節(jié)大小

   return true;

   }

  }

  Function listmail($mess=null,$uni_id=null) //對(duì)應(yīng)的是LIST命令,取得每個(gè)郵件的大小及序號(hào)。一般來(lái)說(shuō)用到的是List命令,如果指定了$uni_id ,則使用UIDL命令,返回的是每個(gè)郵件的標(biāo)識(shí)符,事實(shí)上,這個(gè)標(biāo)識(shí)符一般是沒(méi)有什么用的。取得的各個(gè)郵件的大小返回到類的內(nèi)部變量mail_list這個(gè)二維數(shù)組里。

  {

  if($this->state!="TRANSACTION")

   {

   $this->err_str="還沒(méi)有連接到服務(wù)器或沒(méi)有成功登錄";

   return false;

   }

  if ($uni_id)

   $command="UIDL ";

   else

   $command="LIST ";

  if ($mess)

   $command.=$mess;

  

  if (!$this->command($command,3,"+OK"))

   {

   //echo $this->err_str;

   return false;

   }

   else

   {

   $i=0;

   $this->mail_list=array();

   $this->getresp();

   while ($this->resp!=".")

   { $i++;

   if ($this->debug)

   {

   $this->outdebug($this->resp);

   }

   if ($uni_id)

   {

   $this->mail_list[$i][num]=strtok($this->resp," ");

   $this->mail_list[$i][size]=strtok(" ");

   }

   else

   {

   $this->mail_list[$i]["num"]=intval(strtok($this->resp," "));

   $this->mail_list[$i]["size"]=intval(strtok(" "));

   }

   $this->getresp();

   }

   return true;

   }

  }

  function getmail($num=1,$line=-1) // 取得郵件的內(nèi)容,$num是郵件的序號(hào),$line是指定共取得正文的多少行。有些時(shí)候,如郵件比較大而我們只想先查看郵件的主題時(shí)是必須指定行數(shù)的。默認(rèn)值$line=-1,即取回所有的郵件內(nèi)容,取得的內(nèi)容存放到內(nèi)部變量$head,$body兩個(gè)數(shù)組里,數(shù)組里的每一個(gè)元素對(duì)應(yīng)的是郵件源代碼的一行。

  {

 。if($this->state!="TRANSACTION")

   {

   $this->err_str="不能收取信件,還沒(méi)有連接到服務(wù)器或沒(méi)有成功登錄";

   return false;

   }

  if ($line<0)

   $command="RETR $num";

  else

   $command="TOP $num $line";

  

  if (!$this->command("$command",3,"+OK"))

   return false;

   else

   {

   $this->getresp();

   $is_head=true;

   while ($this->resp!=".") // . 號(hào)是郵件結(jié)束的標(biāo)識(shí)

   {

   if ($this->debug)

   $this->outdebug($this->resp);

   if (substr($this->resp,0,1)==".")

   $this->resp=substr($this->resp,1,strlen($this->resp)-1);

   if (trim($this->resp)=="") // 郵件頭與正文部分的是一個(gè)空行

   $is_head=false;

   if ($is_head)

   $this->head[]=$this->resp;

   else

   $this->body[]=$this->resp;

   $this->getresp();

   }

   return true;

   }

  

  } // end function

  

  function dele($num) // 刪除指定序號(hào)的郵件,$num 是服務(wù)器上的郵件序號(hào)

  {

   if($this->state!="TRANSACTION")

   {

   $this->err_str="不能刪除遠(yuǎn)程信件,還沒(méi)有連接到服務(wù)器或沒(méi)有成功登錄";

   return false;

   }

   if (!$num)

   {

   $this->err_str="刪除的參數(shù)不對(duì)";

   return false;

   }

   if ($this->command("DELE $num ",3,"+OK"))

   return true;

   else

   return false;

  }

  通過(guò)以上幾個(gè)方法,我們已經(jīng)可以實(shí)現(xiàn)郵件的查看、收取、刪除的操作,不過(guò)別忘了最后要退出,并關(guān)閉與服務(wù)器的連接,調(diào)用下面的這個(gè)方法:

  Function Close()

  {

  if($this->connection!=0)

   {

   if($this->state=="TRANSACTION")

   $this->command("QUIT",3,"+OK");

   fclose($this->connection);

     $this->connection=0;

   $this->state="DISCONNECTED";

   }

  }