用PHP實現(xiàn)POP3郵件的收取(二)
用PHP實現(xiàn)POP3收取郵件的類
現(xiàn)在讓我們來用PHP實現(xiàn)一個通過POP3協(xié)議收取信件的類吧,這個類中所用到的一些sock操作的函數(shù),不另做特殊說明,請參考php的有關(guān)資料。通過這個實例,相信你也會和我一樣,感覺到PHP中對于sock操作的靈活、方便和功能的強大。
首先,我們來說明一下這個類中需要用到的一些內(nèi)部成員變量:(這些變量應(yīng)該都是對外封閉的,可是由于php對類的成員變量沒有private與publice之類的分別,只好就這么直接定義了。這是PHP的一個令人遺憾的地方。)
1.成員變量說明
class pop3{
var $hostname=''; // POP主機名
var $port=110; // 主機的POP3端口,一般是110號端口
var $timeout=5;// 連接主機的最大超時時間
var $connection=0; // 保存與主機的連接
var $state='DISCONNECTED'; // 保存當(dāng)前的狀態(tài)
var $debug=0;// 做為標(biāo)識,是否在調(diào)試狀態(tài),是的話,輸出調(diào)試信息
var $err_str='';// 如果出錯,這里保存錯誤信息
var $err_no; //如果出錯,這里保存錯誤號碼
var $resp; // 臨時保存服務(wù)器的響應(yīng)信息
var $apop; // 指示需要使用加密方式進(jìn)行密碼驗證,一般服務(wù)器不需要
var $messages; // 郵件數(shù)
var $size; //各郵件的總大小
var $mail_list; // 一個數(shù)組,保存各個郵件的大小及其在郵件服務(wù)器上序號
var $head=array(); // 郵件頭的內(nèi)容,數(shù)組
var $body=array(); // 郵件體的內(nèi)容,數(shù)組;
2.當(dāng)然,這其中的有些變量,僅通過這樣一個簡單的說明并不能完全了解如何使用,下面我就逐個來說明這個類實現(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ū)ο缶幊痰呐笥岩豢淳蜁溃@是這個類的構(gòu)造函數(shù),在初始化這個類時,可以給出這幾個最基本的參數(shù):pop3服務(wù)器的地址,端口號,及連接服務(wù)器時的最大超時時間。一般來說,只需要給出POP3服務(wù)器的地址就行了。
Function open(){if($this->hostname=='')
{$this->err_str='無效的主機名!!';
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ù)器失敗,錯誤信息:'.$err_str.'錯誤號:'.$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.'請檢查POP服務(wù)器是否正確';
return false; }
$this->state='AUTHORIZATION';
return true;
}
}
該方法不需要任何參數(shù)就可建立與POP3服務(wù)器的sock連接。該方法又用到了另一個類中的方法$this->getresp();下面是這個方法的聲明:
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)=='rn')
{
$this->resp=strtok($this->resp,'rn');
return true;
}}}
這個方法取得服務(wù)器端的返回信息并進(jìn)行簡單的處理:去掉最后的回車換行符,將返回信息保存在resp這個內(nèi)部變量中。這個方法在后面的多個操作中都將用到。另外,還有個小方法也在后面的多個操作中用到:
Function outdebug($message){ echo htmlspecialchars($message).'<br>n';}
它的作用就是把調(diào)試信息$message顯示出來,并把一些特殊字符進(jìn)行轉(zhuǎn)換以及在行尾加上<br>標(biāo)簽,這樣是為了使其輸出的調(diào)試信息便于閱讀和分析。
建立起與服務(wù)器的sock連接之后,就要給服務(wù)器發(fā)送相關(guān)的命令了(請參見上面的與服務(wù)器對話的過程)從上面對 POP對話的分析可以看到,每次都是發(fā)送一條命令,然后服務(wù)器給予一定的回應(yīng),如果命令的執(zhí)行是對的,回應(yīng)一般是以+OK開頭,后面是一些描述信息,所以,我們可以做一個通過發(fā)送命令的方法:
Function command($command,$return_lenth=1,$return_code='+'){ if ($this->connection==0) { $this->err_str='沒有連接到任何服務(wù)器,請檢查網(wǎng)絡(luò)連接';
return false; }
if ($this->debug) $this->outdebug('>>> $command');
if (!fputs($this->connection,'$commandrn'))
{
$this->err_str='無法發(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;
}}
這個方法可以接受三個參數(shù): $command--> 發(fā)送給服務(wù)器的命令; $return_lenth,$return_code ,指定從服務(wù)器的返回中取多長的值做為命令返回的標(biāo)識以及這個標(biāo)識的正確值是什么。對于一般的pop操作來說,如果服務(wù)器的返回第一個字符為'+',則可以認(rèn)為命令是正確執(zhí)行了。也可以用前面提到過的三個字符'+OK'做為判斷的標(biāo)識。
下面介紹的幾個方法則可以按照前述收取信件的對話去理解,因為有關(guān)的內(nèi)容已經(jīng)在前面做了說明,因此下面的方法不做詳細(xì)的說明,請參考其中的注釋:
Function Login($user,$password) //發(fā)送用戶名及密碼,登錄到服務(wù)器{
if($this->state!='AUTHORIZATION') {
$this->err_str='還沒有連接到服務(wù)器或狀態(tài)不對';
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,'rn');
if (!$this->command('APOP $user '.md5($this->greeting.$password),3,'+OK')) return false;
}
$this->state='TRANSACTION'; // 用戶認(rèn)證通過,進(jìn)入傳送模式
return true;}
Function stat() // 對應(yīng)著stat命令,取得總的郵件數(shù)與總的大小{if($this->state!='TRANSACTION') {
$this->err_str='還沒有連接到服務(wù)器或沒有成功登錄';
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) //對應(yīng)的是LIST命令,取得每個郵件的大小及序號。一般來說用到的是List命令,如果指定了$uni_id ,則使用UIDL命令,返回的是每個郵件的標(biāo)識符,事實上,這個標(biāo)識符一般是沒有什么用的。取得的各個郵件的大小返回到類的內(nèi)部變量mail_list這個二維數(shù)組里。
{if($this->state!='TRANSACTION') {
$this->err_str='還沒有連接到服務(wù)器或沒有成功登錄';
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是郵件的序號,$line是指定共取得正文的多少行。有些時候,如郵件比較大而我們只想先查看郵件的主題時是必須指定行數(shù)的。默認(rèn)值$line=-1,即取回所有的郵件內(nèi)容,取得的內(nèi)容存放到內(nèi)部變量$head,$body兩個數(shù)組里,數(shù)組里的每一個元素對應(yīng)的是郵件源代碼的一行。
{
if($this->state!='TRANSACTION') { $this->err_str='不能收取信件,還沒有連接到服務(wù)器或沒有成功登錄';
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!='.') // . 號是郵件結(jié)束的標(biāo)識 {
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)=='') // 郵件頭與正文部分的是一個空行
$is_head=false;
if ($is_head)
$this->head[]=$this->resp;
else
$this->body[]=$this->resp;
$this->getresp();
}
return true; } } // end function
function dele($num) // 刪除指定序號的郵件,$num 是服務(wù)器上的郵件序號
{
if($this->state!='TRANSACTION') {
$this->err_str='不能刪除遠(yuǎn)程信件,還沒有連接到服務(wù)器或沒有成功登錄';
return false; }
if (!$num) {
$this->err_str='刪除的參數(shù)不對';
return false; }
if ($this->command('DELE $num ',3,'+OK')) return true;
else
return false;}
通過以上幾個方法,我們已經(jīng)可以實現(xiàn)郵件的查看、收取、刪除的操作,不過別忘了最后要退出,并關(guān)閉與服務(wù)器的連接,調(diào)用下面的這個方法:
Function Close(){
if($this->connection!=0) {
if($this->state=='TRANSACTION')
$this->command('QUIT',3,'+OK');
fclose($this->connection);
$this->connection=0;
$this->state='DISCONNECTED'; }
}
