PHP で Socket を使用して電子メールを送信する

WBOY
リリース: 2016-06-21 09:06:05
オリジナル
958 人が閲覧しました

筆者が申請したいくつかの PHP ホームページ スペースの中で、メール機能を提供できるものは多くありません。 mail() 関数を呼び出した後は、常に何もする必要がありません。しかし、オンライン生活において電子メールの役割はますます高まっています。考えてみてください。電子メールを受信せずにインターネットをサーフィンする場合、インターネットのバグは本当のインターネットのバグと言えるでしょうか?電子メールの役割についてはこれ以上話したくないのですが、ホームページ スペースが mail() の送信をサポートしていない場合はどうすればよいでしょうか?ソケット経由でメールを送信することも考えましたが、残念ながら私は PHP を使用したソケット プログラミングに慣れていません。また、メールを送信するには SMTP プロトコルが必要で、大量の英語を読む必要があるため、勉強したことがありません。ついにある日、ソケット プログラミングを使用して電子メールを送信することに関する記事を見つけました。私は宝物を見つけたかのようにそれをコピーし、誰でも使用できる PHP で利用できるクラスに変換しました。元の記事は単なるサンプルであり、いくつかのエラーがありましたが、多くの実験と変更を経て、最終的には MIME を組み合わせて送信するという前の手順に従って、ソケットを直接使用して指定されたメールボックスにメールを送信するクラスに変更しました。この記事では、mail() 関数をサポートしていない Web サイトでも電子メールを送信できます。メール送信の処理には時間がかかるため、mail() の処理機構と全く同じではないため、速度は遅くなりますが、同時にメールを送信するという緊急のニーズを解決することもできます。ソケット プログラミングに PHP を使用する方法を学びます。次に、このクラスの実装原理を紹介し、同時に SMTP に関する基礎知識を説明します。

ソケット プログラミングの概要
私は TCP/IP プログラミングの専門家ではないので、ここでは私の理解と経験を共有しているだけであることを皆さんにお伝えしたいと思います。

fsockopen 関数を使用してインターネット接続を開きます。関数の構文形式は次のとおりです。

int fsockopen(string hostname, int port, int [errno], string [errstr], int [timeout]);パラメータの意味を説明する必要はないと思います。はい、ここでは SMTP プロトコルが使用されているため、ポート番号は 25 です。接続が正常に開かれると、ファイル ハンドルのように使用できるソケット ハンドルが返されます。利用可能な操作には、fputs()、fgets()、feof()、fclose()
などが含まれます。

とても簡単な紹介は以上です。

SMTPの基本
TCP/IPをベースとしたインターネットプロトコルの一般的なコマンド形式は、リクエスト/レスポンス方式で実装されており、すべてテキスト情報が使用されるため、処理が容易です。 SMTP は Simple Mail Transfer Protocol の略称で、クライアントがサーバーに電子メールを送信できるようにします。したがって、以下で説明するコマンドは、クライアントがサーバーに要求命令を送信することを指し、応答はサーバーからクライアントに返される情報を指します。

SMTPはコマンドヘッダーとメッセージボディの2つの部分に分かれています。コマンドヘッダーは主にクライアントとサーバー間の接続、検証などを完了します。プロセス全体は複数のコマンドで構成されます。各コマンドがサーバーに送信されると、サーバーは応答情報 (通常は 3 桁の応答コードと応答テキスト) を返します。さまざまなサーバーから返される応答コードはプロトコルに準拠していますが、応答テキストはプロトコルに準拠していません。各コマンドと応答の最後にはキャリッジ リターン文字があるため、コマンドと応答は fputs() と fgets() を使用して処理できます。 SMTP コマンドと応答メッセージは 1 行です。メッセージ本文は電子メールの本文であり、最後の終了行は別の「.」で終わる必要があります。

クライアントで一般的に使用される SMTP コマンドは次のとおりです:

HELO hostname: サーバーに挨拶し、使用されているマシン名をクライアントに伝えます。気軽に入力できます
MAIL FROM: sender_id: サーバーに送信者のアドレスを伝えます
RCPT TO:receiver_id:サーバーの受信者のアドレスを伝えます
DATA:レターの内容は以下で送信され始め、それはのみを含む特別な行で終わる必要があります。
RESET:前のコマンドをキャンセルして最初から開始します
VERIFY userid: アカウントが存在するかどうかを確認します (このコマンドはオプションです)。サーバーがサポートしていない可能性があります)
QUIT: 接続を終了し、終了します
サーバーから返される応答情報は (形式: 応答コード + スペース + 説明) です):

220 サービス準備完了 (ソケット接続が成功するとこの情報が返されます)
221 処理中です
250 リクエスト電子メールアクションは正しく完了しています (この情報は、HELO、MAIL FROM、RCPT TO が成功した場合に返されます) 、QUIT コマンドが正常に実行されました)
354 データの送信を開始し、で終了します (DATA コマンドが正常に実行された場合、この情報が返されます。顧客端末は情報を送信するはずです)
500 構文エラー、コマンドが認識できません
550コマンドを実行できません。メールボックスが無効です
552 割り込み処理: ユーザーがファイル領域を超えています
簡単なコマンド ヘッダーを以下に示します (これはソケットを開いた後に行われます)。これは、stmp に電子メールを送信したテスト結果です。 .263.net:

HELO limodou
250 smtp.263.net
MAIL FROM:
chatme@263.net

250 OK RCPT TO: chatme@263.net

250 OK DATA3 54 エンドデータ
宛先:
chatme@263.net

差出人: chatme@263.net
件名: test差出人: chatme@263.net

test OK: C46411 としてキューに追加されました。 C5097E0
SMTPに関する簡単な知識です。関連コンテンツは RFC にあります。

RFC 821 では、電子メールの送受信に関する関連手順が定義されています。
RFC 822 は電子メールのコンテンツの形式を指定します。
RFC 2045-2048 はマルチメディア電子メール コンテンツの形式を策定し、
RFC 1113、1422-1424 は電子メールの機密性を向上させる方法について説明します。

send_mailクラスの実装
今回は私が作成したsend mailクラスを紹介します。上記の準備知識を踏まえて、以下のように実装していきます。

クラスのメンバー変数

var $lastmessage; //返された最後の応答メッセージを記録します
var $debug;使用する 使用する 使用する 使用する 使用する 使用する'sを通して - - - - - - - - - - - - $ lastmessageと$ lastActは、最後のA応答メッセージを記録するために使用され、エラーが発生したときにユーザーが使用できるコマンドを実行しました。テストの必要があるため、$debug 変数も定義しました。その値が true の場合、プロセスの実行中にいくつかの実行情報が表示されます。それ以外の場合は、出力はありません。 $fp は、開いたソケット ハンドルを保存するために使用されます。

クラスの構築


------------------------------------------ --- --------------------------------------
function send_mail($smtp, $welcome="" .
$this->welcome=gethostbyaddr("localhost");
$this->lastmessage=" ";
$this->lastact="";
$this-> - ------------------------------------------------- - -------
このコンストラクターは主にいくつかの初期値の決定と設定を完了します。 $welcome は、サーバーにユーザー名を伝えるために HELO ディレクティブで使用されます。 HELO 命令にはマシン名が必要ですが、マシン名がなくても使用できます。ユーザーが $welcome を指定しない場合、ローカル マシン名が自動的に検索されます。

デバッグ情報を表示

------------------------------------------ -- --------------------------------------
1 function show_debug($message, $inout)
2 {
3 if ($this->debug)
4 if($inout=="in") //応答情報
6 {
7 $m='<< '; }
" ";
13 $message=nl2br( $message);
14 echo "${m}${message}";
15 }
16 }
------------ -------------------------------------- ------------ ------------------
この関数はデバッグ情報を表示するために使用されます。 $inout には、アップロード命令であるか返される応答であるかを指定できます。アップロード命令である場合は "out" を使用し、返される応答である場合は "in" を使用します。

3行目はデバッグ情報を出力するかどうかを決定します。
5 行目で、応答メッセージであるかどうかを判断し、そうでない場合は 7 行目の情報の前に「>>」を追加します。アップロードの指示。
11-12行目、最後の情報文字列が改行文字かどうかを判断し、そうでない場合はHTML改行マークを追加します。 13 行目は、すべての改行文字を HTML 改行タグに変換します。
14 行目、メッセージ全体を出力し、違いを示すためにメッセージの色をグレーに設定します。コマンドを実行します

-------------------------------------- --- ----------------------------------
1    function do_command($command, $code)
2    {
3        $this->lastact=$command;
4        $this->show_debug($this->lastact, "out");
5        fputs ( $this->fp, $this->lastact );
6        $this->lastmessage = fgets ( $this->fp, 512 );
7        $this->show_debug($this->lastmessage, "in");
8        if(!ereg("^$code", $this->lastmessage))
9        {
10            return false;
11        }
12        else
13            return true;
14    }
--------------------------------------------------------------------------------
  在编写socket处理部分发现,一些命令的处理很相似,如HELO,MAIL FROM,RCPT TO,QUIT,DATA命令,都要求根据是否显示调试信息将相关内容显示出来,同时对于返回的响应码,如果是期望的,则应继续处理,如果不是期望的,则应中断出理。所以为了清晰与简化,专门对这些命令的处理编写了一个通用处理函数。函数的参数中$code为期望的响应码,如果响应码与之相同则表示处理成功,否则出错。

第3行,记录最后执行命令。
第4行,将上传命令显示出来。
第5行,则使用fputs真正向服务器传换指令。
第6行,从服务器接收响应信息将放在最后响应消息变量中。
第7行,将响应信息显示出来。
第8行,判断响应信息是否期待的,如果是则第13行返回成功(true),否则在第10行返回失败(false)。

  这样,这个函数一方面完成指令及信息的发送显示功能,别一方面对返回的响应判断是否成功。

メール送信処理

以下が本当の秘密ですが、よく見てください。 :)

---------------------------------------------- --- ----------------------------------
1 関数 send( $to,$from,$subject ,$message )
2 {
3
4 //サーバーに接続します
5 $this->lastact="connect";
6
7 $this->show_debug("SMTP サーバーに接続します : ".$this ->smtp . set_socket_blocking( $this->fp, true );
13 $this->lastmessage=fgets($this->fp,512);
14 $this->show_debug($this- >lastmessage, " in");
15
16 if (! ereg ( "^220", $this->lastmessage ) )
17 {
18 return false;
19 }
2 1 lastact="HELO " $this->ようこそ。"n";
23 ($this ->fp);
26 「」 if(!$this->do_command($ this->lastact, " 250"))
31 }
35
36 to" . "n";
37 if(!$this->do_command($this->lastact, "250"))
38 {
39 ;
40 false を返します ;
41 If(!$this->do_command($this->lalastact, "354" ))
46 1 $件名) && !例($head, $message))
54
58                //处理From头
59                $head="From: $from\n";
60                if(!empty($from) && !ereg($head, $message))
61                {
62                    $message = $head.$message;
63                }
64
65                //处理To头
66                $head="To: $to\n";
67                if(!empty($to) && !ereg($head, $message))
68                {
69                    $message = $head.$message;
70                }
71
72                //加上结束串
73                if(!ereg("\n\.\n", $message))
74                    $message .= "\n.\n";
75                $this->show_debug($message, "out");
76                fputs($this->fp, $message);
77
78                $this->lastact="QUIT\n";
79                if(!$this->do_command($this->lastact, "250"))
80                {
81                    fclose($this->fp);
82                    return false;
83                }
84            }
85            return true;
86        }
87        else
88        {
89            $this->show_debug("Connect failed!", "in");
90            return false;
91        }
92    }
--------------------------------------------------------------------------------
  有些意思很清楚的我就不说了。

  这个函数一共有四个参数,分别是$to表示收信人,$from表示发信人,$subject表求邮件主题和$message表示邮件体。如果处理成功则返回true,失败则返回false。

第8行,连接邮件服务器,如果成功响应码应为220。
第12行,设置阻塞模式,表示信息必须返回才能继续。详细说明看手册吧。
第16行,判断响应码是否为220,如果是,则继续处理,否则出错返回。
第22-27行,处理HELO指令,期望响应码为250。
第29-34行,处理MAIL FROM指令,期望响应码为250。
第36-41行,处理RCPT TO指令,期望响应码为250。
第44-49行,处理DATA指令,期望响应码为354。
第51-76行,生成邮件体,并发送。
第52-56行,如果$subject不为空,则查找邮件体中是否有主题部分,如果没有,则加上主题部分。
第59-63行,如果$from不为空,则查找邮件体中是否有发信人部分,如果没有,则加上发信人部分。
第66-70行,如果$to不为空,则查找邮件体中是否有收信人部分,如果没有,则加上收信人部分。
第73-74行,查找邮件体是否有了结束行,如果没有则加上邮件体的结束行(以"."作为单独的一行的特殊行)。
第76行,发送邮件体。
第78-83行,执行QUIT结否与服务器的连接,期望响应码为250。
第85行,返回处理成功标志(true)。
第81-91行,与服务器连接失败的处理。
 以上为整个send_mail类的实现,应该不是很难的。

以下に例を示します。

電子メール送信例

まず最も単純な例を示します:
1 include "sendmail.class.php3";
2 $email="Hello, this is a test Letter!";
3 $sendmail=new send_mail("smtp.263.net", "limodou", true); //調整情報を表示
4 if($sendmail->send("chatme@263.net", ") chatme@263.net", "test", $email))
5 {
6 echo "送信に成功しました!
";
7 }
8 else
9 {
10 echo "送信に失敗しました! ";
11 }
?>

1行目、send_mailクラスを読み込みます。
3行目:クラスのインスタンスを作成し、表示したくない場合は
$sendmail=new send_mail("smtp.263.net"); と設定します。
4 行目、メールを送信します。


シンプルですね。次に、前の MIME 電子メールの送信例に基づいて、HTML 添付ファイルを送信する例を示します。


include "MIME.class.php3";
//MIME メールの送信に関する記事では、このクラス ファイルの名前は MIME.class ですが、ここでは次のように変更しました

$ to = 'chatme@263.net'; //受信者のメールアドレスに変更されました
$str = "ニュースレター for ".date('MY', time());

//情報が変更されました私
$html_data = ''. $str. '

こんにちは!はテストです!

';

//MIME クラス インスタンスを生成します
$mime = new MIME_mail("chatme@263.net", $to, $str);

//HTML 添付ファイルを追加します
$mime->attach($html_data, "", HTML, BASE64);

//コメントアウトしてメール送信プロセスを使用します
//$mime->send_mail() ;

//メールを生成
$mime->gen_email(); "sendmail.class.php3"

//インスタンスを作成する
$sendmail=new send_mail("smtp.263.net", "limodou) 」 , true)

//メールを送信
$sendmail->send ( "
chatme@263.net
", "
chatme@263.net
", $str, $mime->email);
?>
コメントは非常に明確なので、これ以上の説明はしません。実際のアプリケーションで使用する場合は、send_mail コンストラクタの debug を false に設定するか、記述しないでください。



関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート