PHP Logo HTTP / パスワードハッシュ / メール操作

更新日:2018年07月02日

HTTP

ブラウザで Web ページを表示する場合、ブラウザ(クライアントやユーザーエージェント UA とも言います)と Web サーバーは相互に通信を行います。その際に Web ページを送受信するのに使われるのが「HTTP(HyperText Transfer Protocol)」というルール(プロトコル)です。

ブラウザとサーバーが通信してコミュニケーションをする際に必要になるのが HTTP ヘッダになります。

ブラウザに URL を入力したりリンクをクリックすると、ブラウザは「GET」リクエストを組み立ててサーバーに送信します。このリクエストはヘッダをパッケージにしたもので、それぞれのヘッダにはリクエストに関する情報(ブラウザがサーバーに対して何を望んでいるか)が記述されています。

サーバーはブラウザからのリクエストを受信すると、レスポンスを送信します。このレスポンスの中のヘッダには、コンテンツの種類やサイズなどの情報が記述されていて、ブラウザはそれを元にコンテンツを表示します。

Web ページのコンテンツは、サーバーが送るレスポンスのヘッダのすぐ後に続いて送られます。それは、HTML であったり、PDF データや JPEG のイメージだったりします。

例えば、あるページを取得すためにブラウザがサーバーに送信する HTTP のリクエストメッセージは、以下のようなのものです。

HTTP ヘッダの例

Request Header
メソッド パス名 バージョン
GET / HTTP/1.1
プロパティ 意味
Host: www.webdesignleaves.com サーバ名
Connection: keep-alive 接続を持続するのか(keep-alive)、毎回接続を切断するのか(close)。
Cache-Control: max-age=0 キャッシュに関する指示。ブラウザやプロキシが、データのキャッシュをどう扱うかの情報
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 ブラウザが受信可能なデータ形式(MIMEタイプ)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36 ブラウザ(ユーザエージェント)の情報
Accept-Encoding: gzip, deflate, sdch ブラウザが受信可能なエンコード方式
Accept-Language: ja,en-US;q=0.8,en;q=0.6 ブラウザが受信可能な言語
Cookie: visited=visited; _gaxc=GA1.2.65432.123456789; クッキーの情報。ブラウザに保存されているクッキーデータは毎回サーバーに送られている。
  • 1行目がリクエスト行で、リソースを取得するための指示(GET)と HTTP バージョン(1.1)です。
  • 2行目以降がヘッダになります。
  • 「プロパティ名: 値 」の形式のフィールドで、メタデータなどをサーバーに送ります。上記の他に Referer(どのページから発生したリクエストなのか)や If-Modified-Since(更新されていたら)、 If-None-Match(同じでなければ) といったローカルキャッシュのデータが変更されていないかチェックするための管理情報などがあります。

リクエスト行は下記の書式になっています。

メソッド    パス名    HTTP/バージョン
  • メソッドは「GET」や「POST」などになります。(Web ページを表示する場合は、GET になります)
  • パス名は 「/」や「/xxxx/yyyyy/」のような、スラッシュで始まるパス名や、URL が指定されます。
  • バージョンは現在は 1.1 が主流です。

リクエストに対してサーバは下記のような応答メッセージを返します。

Response Header
バージョン ステータスコード 補足メッセージ
HTTP/1.1 200 OK
プロパティ 意味
Date: Sat, 28 Nov 2015 20:35:52 GMT 応答を返す時刻
Server: Apache サーバ情報
Cache-Control: max-age=10 キャッシュに関する指示
Expires: Sat, 28 Nov 2015 20:36:02 GMT 有効期限。取得したデータを再度サーバーに問い合わせなくてもブラウザが再利用していい期限(キャッシュの制御に使われる)
Vary: Accept-Encoding,User-Agent サーバ主導型ネゴシエーションで使用されたヘッダ情報
Content-Encoding: gzip コンテンツのエンコード方式
Content-Length: 2827 コンテンツの長さ(バイト単位)
Connection: Keep-Alive 接続機能
Content-Type: text/html; charset=utf-8 コンテンツの種別(MIMEタイプ)
空行
メッセージボディ <html> ~ </html> など
  • 1行目は処理の結果を示す応答コード(レスポンス行)です。
  • 2行目以降がヘッダ部分です。
  • ヘッダの後に、1行の空行を挟んで、Web ページのコンテンツ(メッセージボディ:HTML等)が送信されます。

レスポンス行は下記の書式になっています。

HTTP/バージョン    ステータス番号(ステータスコード)    補足メッセージ
  • ステータス番号は成功やエラーの種類を表す番号です。
  • 補足メッセージには、OK や Not Found など、ステータス番号の意味や詳細を補足するメッセージが返されます。

ステータスコードは3桁の数字で表され、次のように分類されています。

  • 200番台:成功
  • 300番台:リダイレクト
  • 400番台:クライアント側のエラー
  • 500番台:サーバー側のエラー

ブラウザでの HTTP ヘッダの確認

ブラウザを開いて F12キーを押します。そしてページを再読み込みします。(以下はブラウザのバージョンや言語設定で異なります。)

Chrome の場合は、「Network」タブで左側のファイルのリストで確認したいファイルを選択します。必要に応じて「View source」をクリックします。

HTTP ヘッダの確認 Chrome

Firefox の場合は、「ネット」タブで表示されるリストで確認したいファイルを選択します。必要に応じて「ソースを表示」をクリックします。

HTTP ヘッダの確認 Firefox

IE の場合は、「Network」タブで「Network traffic capturing」を有効にして再読み込みします。表示されるリストで確認したいファイルをダブルクリックします。

HTTP ヘッダの確認 IE

参考サイト

header() 関数

PHP で HTTP ヘッダ情報を送信するには、header() 関数を使用します。

header() 関数はサーバからブラウザへ HTTP ヘッダを送信します。ヘッダは一番先に送られる必要があるため、たった1文字でも空白でもヘッダに先立って送られるとブラウザにエラーではじかれてしまいます。

このため、通常の HTML タグまたは PHP からの出力にかかわらず、すべての実際の出力の前に呼び出す必要があります。

<?php タグの前に空白があったとしてもエラーの原因になります。

以下が、header() 関数の構文です。

void header( $string [, $replace [, $http_response_code ]] )

パラメータ

  • string(string):ヘッダ文字列。
  • replace(bool):オプションのパラメータ replace は、ヘッダが 前に送信された類似のヘッダを置換するか、または、同じ形式の二番目の ヘッダを追加するかどうかを指定します。デフォルト(true)では、この関数は 置換を行ないますが、二番目の引数に FALSE を指定すると、同じ型の 複数のヘッダを強制的に生成します。
  • http_response_code(int):HTTP レスポンスコード(ステータスコード)を強制的に指定の値にします。このパラメータが意味をなすのは string が空文字列でないときだけです。

戻り値

値を返しません。

リダイレクト

あるページから別のページへジャンプ(移動)させるには、header() 関数で Location ヘッダーを使用します。

if 文などで条件分岐させて、条件により別のページに移動させるなどの使い方ができます。

リダイレクトする場合は、300番台のステータスコードを使用します。 リダイレクトを表すステータスコードには「301」「302」「303」「307」の4種類があり、用途によって使い分けます。

例えばサイトの移転などで、移転先にジャンプさせたいときは、ステータスコード「301 Moved Permanently」を使用します。

リダイレクトを表すステータスコード
ステータスコード 意味
301
Moved Permanently
URL が新しい URL へ恒久的に変更されたことを表します。
302
Found
一時的に別のURLへ遷移させたい時に使用します。(PHP のデフォルト)
303
See Other
新しい URL に GET メソッドでアクセスすることが決められたリダイレクトです。 例えば、フォームページで POST した後にリダイレクトでトップページヘ戻したい場合、 トップページに対して同じデータを POST しても意味がないため、303リダイレクトで GET に変更する、といった用途で使われます。
307
Temporary Redirect
一時的に別のURLへ遷移させたい時に使用します。 内容的には302リダイレクトとほぼ同じですが、307では「リクエストに使用したメソッド(GET や POST)をそのまま使う」という違いがあります。 そのため、リダイレクト元で GET や POST の指定があり、それをそのままリダイレクト先へ引き継がせたい場合は、307リダイレクトを使います。

以下はリダイレクトの例です。 注意点としては、header() 関数以下のスクリプトが実行されないように、必ず exit() 関数を使ってスクリプトを終了するようにします。

また、以下の例では、PHP の終了タグを記述していますが、この例のような PHP のみのコードで終了タグの後に何もない場合は、終了タグを省略したほうが良いです。

301 恒久的な移動

<?php
header('HTTP/1.1 301 Moved Permanently');
header('Location: http://example.com/');
exit();
?> 

または、

<?php
header('Location: http://example.com/', true, 301);
exit();
?> 

302 一時的な移動

<?php
header('Location: http://example.com/', true, 302);
exit();
?>

または、(302 はデフォルトなので省略可能)

<?php
header('Location: http://example.com/');
exit();
?>

サンプルページ:「redirect.php

303 POST の処理後のリダイレクトなど

<?php
header('Location: http://example.com/', true, 303);
exit();
?>

Refresh ヘッダ

特定の時間経過後ページをリフレッシュします。ヘッダのURLで自身のページを参照すれば、自分自身をリフレッシュすることができます。

<?php
header('Refresh: 5; url=http://example.com/');  
  echo '5秒後に example.com に移動します。移動しない場合は<a href="http://example.com/">こちら</a>をクリック';
?>

サンプルページ:「refresh.php

キャッシュを無効にする

PHP では動的にページを生成するので、ブラウザのキャッシュを無効にしたい場合があります。(キャッシュされると内容を変更しても、古い内容が表示されてしまうことがあります)

header() 関数を使用すると、強制的にキャッシュを無効にすることができます。但し、header() 関数を使ってキャッシュを無効にしても、ブラウザの種類やユーザーの設定によりキャッシュされてしまうこともあります。

キャッシュを無効にするには、Cache-Control、Pragma を以下のように指定すると、強制的に無効にすることができます。また、Expires で過去の日付を指定するとキャッシュを無効にできます。

<?php
header("Expires: on, 01 Jan 1970 00:00:00 GMT");   
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");  
header("Cache-Control: post-check=0, pre-check=0", false);  
header("Pragma: no-cache");  
?>

PHP マニュアルには以下の方法が紹介されています。

<?php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // 過去の日付
?>
ファイルのダウンロード

ファイルをダウンロードさせるには、header() 関数を使います。

ダウンロードさせるファイルの形式により、Content-Type (以下の表参照)を指定して最後に readfile() 関数でファイルを出力します。readfile() 関数は、ファイルを読み込んで標準出力(ブラウザ)に書き出します。

以下は、画像ファイル(JPEG)をダウンロードさせるサンプルです。

<?php
$filename = "sample.jpg";
$filesize = filesize($filename);
$mime = "application/octet-stream";
header('Content-Type: "' . $mime . '"');
header('Content-Disposition: attachment; filename="'. $filename .'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE") || strstr($_SERVER['HTTP_USER_AGENT'], "Trident")) { //IE 
  header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  header('Pragma: public');
}else{
  header('Pragma: no-cache');
}
header('Content-Length: ' . $filesize);
readfile($filename);
//終了タグは省略

サンプルページ:「download.php

  • Content-Type :送信されるデータがどのようなものかを示す情報です。
  • Content-Disposition :attachment(添付ファイル)と宣言して、添付ファイルの名前を指定します。
  • Content-Transfer-Encoding :ファイルを転送するエンコード(binary)を指定します。
  • Content-Length :データのサイズをブラウザに伝えます。

また、IE と他のブラウザ(Firefox や Chrome など)は仕様が異なるため、処理を分けます。

IE では、「Expires: 0」と「header('Cache-Control:~)」でキャッシュを無効化し、他のブラウザでは「Expires: 0」と「Pragma: no-cache」でキャッシュを無効化します。

IE では、「Pragma: no-cache」を送信するとデータをダウンロードさせられなくなってしまうため「no-cache」以外の「public」か「private」を指定します。

代表的な Content-Type の例
ファイル形式 拡張子 Content-Type
HTML .html   .htm text/html
テキスト .txt text/plain
XHTML .xhtml application/xhtml+xml
XML .xml text/xml
RSS .rss application/rss+xml
CSS .css text/css
JavaScript .js text/javascript
PHP .php application/x-httpd-php
PDF .pdf application/pdf
CSV .csv application/csv
Excel .xls application/vnd.ms-excel
Word .doc application/msword
ZIP .zip application/zip
tar tar.gz application/x-tar
JPEG .jpg   .jpeg image/jpeg
GIF .gif image/gif
PNG .png image/png
Flash .swf application/x-shockwave-flash
MPEG .mpg   .mpeg video/mpeg
MP4 .mp4 video/mp4
MP3 .mp3 audio/mpeg
MIDI .mid   .midi audio/midi
WAVE .wav audio/wav

参考リンク:ファイルのアップロード

Basic 認証

Basic 認証(Basic Authentication)とは、HTTP で定義される認証(HTTP 認証)方式の一つです。

但し、Basic 認証は、パスワードがそのままHTTPのネットワークを流れるので、盗聴などセキュリティ上の危険性があるため、機密性の高いセキュリティが必要な場合は向いていません。

以下は、おおまかな Basic 認証の通信の流れです。

  1. クライアントが認証が必要なページをリクエストします。(この時点では、クライアントはそのページが認証を必要とするかを知りません)
  2. サーバは 401 レスポンスコードを返し、認証領域 (authentication realm) や認証方式 (Basic 認証) に関する情報をクライアントに知らせます。
  3. クライアントは、認証領域をユーザに示して、ユーザ名とパスワードの入力を求めます。
  4. ユーザによりユーザ名とパスワードが入力されると、クライアントはリクエストに認証ヘッダを追加して再度送信します。
  5. 認証に成功すると、サーバは認証の必要なページのリクエストを処理します。ユーザ名やパスワードが間違っている場合は、サーバは再び401レスポンスコードを返し、それによりクライアントは再びユーザにユーザ名とパスワードの入力を求めます。

参考:Basic 認証/wikipedia

header() 関数の WWW-Authenticate ヘッダを使うと、ユーザー名 とパスワードを入力する認証画面が表示されます。

認証画面で入力されたユーザー名 とパスワードは Web サーバーに保存され、PHP では以下のグローバル変数で取得することができます。

  • $_SERVER['PHP_AUTH_USER']:ユーザー名
  • $_SERVER['PHP_AUTH_PW']:パスワード

以下は Basic 認証して、成功したら「Welcome: ユーザー名」と表示する例です。

<?php
  $user = "admin";  
  $pw = "password";  
  if (!isset($_SERVER['PHP_AUTH_USER'])) {
      /* 初めてこのページにアクセスした時の処理 */
      header('WWW-Authenticate: Basic realm="Restricted Area"');
      header('HTTP/1.0 401 Unauthorized');
      header('Content-type: text/html; charset=utf-8');
      //または、header('Content-type: text/html; charset='.mb_internal_encoding());
      die("User ID and Password required.");
  } else {
      /* 認証画面でユーザー名とパスワードを入力してOKを押した時の処理 */
      if ($_SERVER['PHP_AUTH_USER']!= $user || $_SERVER['PHP_AUTH_PW'] != $pw) {
          header('WWW-Authenticate: Basic realm="Restricted Area"');
          header('HTTP/1.0 401 Unauthorized');
          header('Content-type: text/html; charset=utf-8');
          die("Authorization Failed.");
      }
  }
  /* 認証に成功 */  
  echo "<p>Welcome: " . htmlspecialchars($_SERVER['PHP_AUTH_USER']);
?>   

サンプルページ:「basic_auth_no_encrypt.php

isset() を使って、$_SERVER['PHP_AUTH_USER'] が存在するかを調べます。初回アクセス時は、存在しないので、以下の記述により認証画面が表示されます。

header('WWW-Authenticate: Basic realm="Restricted Area"');
header('HTTP/1.0 401 Unauthorized');

WWW-Authenticate ヘッダで認証方式を「Basic」に指定して、認証画面で表示される認証領域(realm)の文字列を「Restricted Area」と指定しています。

Basic realm = "識別するための値"
Basic realm は、セキュリティゾーンというものを定義して、特定のユーザー名とパスワードを保護することができます。ユーザー名とパスワードが正しく入力されると、ブラウザはそれを覚えておいて、同一の realm では後続する認証ヘッダに対する認証ウィンドウを表示しないようにします。

Basic realm = "識別するための値" で指定した文字列は認証画面で表示されます。表示のされ方はブラウザにより異なります。

Basic realm

$_SERVER['PHP_AUTH_USER'] が存在すれば、ユーザー名とパスワードが一致するかを調べます。一致しない場合は、再度認証画面が表示されます。

ユーザーがキャンセルをクリックすると、die() によりメッセージが表示され終了します。

文字化けする場合は、header('Content-type: text/html; charset=utf-8'); を使って文字コードを指定します。

ユーザー名とパスワードをハッシュ化

前述の例では、ユーザー名とパスワードをそのままファイルに記述していますが、実際にはユーザー名とパスワードはハッシュ化しておく方が安全です。(または、ユーザー名とパスワードをパブリックからアクセスできない別ファイルに保存しておく方法もあります)

以下は、ユーザー名とパスワードを crypt() 関数を使ってハッシュ化しておく例です。

Basic 認証の場合、パスワードは crypt() 関数を使って簡単に生成できます。

crypt(“ハッシュ化する文字列”, "任意の2文字");

単純にPHP ファイルに以下のように記述して、ブラウザでプレビューできます。以下の例では、"任意の2文字"(salt)も crypt() 関数を利用しています。

<?php
$user = "user";
$pass = "test";
echo crypt($user, substr(crypt($user), -2)). "<br>";
echo crypt($pass, substr(crypt($pass), -2)). "<br>";
?>
m/5cEpIlY3kpA
0/UYgSRUG9bv.

ハッシュ化したユーザー名とパスワードの判定では、以下のように crypt を使用し、異なったハッシュアルゴリズムが使用された際の問題を避けるために、crypt() の結果($hashed_user や $hashed_password)をパスワード比較用の salt として渡す必要があります。

crypt($_SERVER['PHP_AUTH_USER'], $hashed_user) != $hashed_user

<?php
  $hashed_user = 't.Xqhzo7lQWUo';  //別途 crypt 関数を使って作成しておく user
  $hashed_password = 's0ztT1X0BXSH2';  //同上 test
  if (!isset($_SERVER['PHP_AUTH_USER'])) {
      header('WWW-Authenticate: Basic realm="Restricted Area"');
      header('HTTP/1.0 401 Unauthorized');
      header('Content-type: text/html; charset=utf-8');
      die("User ID and Password required.");
  } else {
      if (crypt($_SERVER['PHP_AUTH_USER'], $hashed_user) != $hashed_user || crypt($_SERVER['PHP_AUTH_PW'], $hashed_password) != $hashed_password) {
          header('WWW-Authenticate: Basic realm="Restricted Area"');
          header('HTTP/1.0 401 Unauthorized');
          header('Content-type: text/html; charset=utf-8');
          die("Authorization Failed.");
      }
  }
  /* 認証に成功 */  
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Basic Authentication</title>
</head>
<body>
<?php
echo "<h1>Welcome ! </h1><br>";
?>
</body>
</html>

サンプルページ:「basic_auth.php

複数のページで認証スクリプトを使う

複数のページで認証スクリプトを使う場合は、以下の認証スクリプトを「authorize.php」として保存し、認証が必要なページの先頭で「require_once」で読み込むようにします。

または、.htaccess を使ってベーシック認証をする方法もあります。

//authorize.php
<?php
$hashed_user = 't.Xqhzo7lQWUo';  //別途 crypt 関数を使って作成しておく user
$hashed_password = 's0ztT1X0BXSH2';  //同上 test
if (isset($_SERVER['PHP_AUTH_USER']) and isset($_SERVER['PHP_AUTH_PW'])){
  if (crypt($_SERVER['PHP_AUTH_USER'], $hashed_user) == $hashed_user && crypt($_SERVER['PHP_AUTH_PW'], $hashed_password) == $hashed_password){
    return;
  }
}
header('WWW-Authenticate: Basic realm="Restricted Area"');
header('HTTP/1.0 401 Unauthorized');
header('Content-type: text/html; charset='.mb_internal_encoding());
die("Authorization Failed.");
?>

認証スクリプトを使うページでは以下のように記述します。

//authorize_test.php
<?php
require_once('authorize.php');
//認証スクリプトを読み込む
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Basic Authentication Test</title>
</head>
<body>
<?php
echo "<h1>Authorization passed ! </h1><br>";
?>
</body>
</html>

サンプルページ:「authorize_test.php

参考:PHPでお手軽ベーシック認証 (Basic認証)

パスワードハッシュ

ハッシュ化とは、元のデータから一定の計算手順に従ってハッシュ値と呼ばれる擬似乱数を求め、その値によって元のデータを置き換えることで、パスワードの保管などでよく用いられるます。

任意長のデータを固定長のデータに圧縮する操作を「ハッシュ関数」といい、ハッシュ関数によって得られた固定長データのことを「ハッシュ」、あるいは「ハッシュ値」といいます。

また、連想配列(文字列をキーとする配列)のことをハッシュと呼ぶのは、連想配列の実装にハッシュアルゴリズムを用いていたことに由来ししているとのことです。

ハッシュ関数の特性の1つに一方向性というものがあり、ハッシュ値から元の文字列を求めることが困難ということです。このような性質のため、パスワードを保管する際にパスワードそのものではなくパスワードのハッシュ値を保管し、認証の際には入力値のハッシュ値と比較する手法(パスワードのハッシュ化)がよく用いられます。

crypt() 関数

以下は、 crypt() 関数の構文です。

string crypt ( $str [, $salt ] )

salt パラメータは必須ではありませんが、これを省略すると crypt() が作るパスワードが弱いものになってしまいます。 PHP 5.6 以降は、このパラメータを省略した場合に E_NOTICE が発生するようになりました。 十分に強いソルトを指定して、セキュリティを確保するようにします。

パラメータ

  • str(string):ハッシュ化したい文字列。
  • salt(string):ハッシュのもととなる salt 文字列。省略した場合の挙動は アルゴリズムの実装によって決まるので、予期せぬ結果となることがあり得ます。

戻り値

ハッシュ化した文字列を返します。 失敗した場合は、salt とは異なることが保証されている 13 文字未満の文字列を返します。

以下は、crypt() 関数を使って文字列(パスワード)をハッシュ化するフォームの例です。

<body>
<div id="wrapper">
<h1>Password Generator</h1>
<?php
$id = '';
$pass = '';
$_POST = checkInput($_POST);
if(isset($_POST['pass'])) { $pass = $_POST['pass'];}
//crypt() 関数を使って文字列(パスワード)をハッシュ化
$pwd = crypt($pass, substr(crypt($pass), -2));

function checkInput($var){
  if(is_array($var)){
    return array_map('checkInput', $var);
  }else{
    if(get_magic_quotes_gpc()){  //php.iniでmagic_quotes_gpcが「on」の場合の対策
      $var = stripslashes($var);
    }
    if(preg_match('/\0/', $var)){  //NULLバイト攻撃対策
      die('不正な入力です。');
    }
    if(!mb_check_encoding($var, 'UTF-8')){  //文字エンコードのチェック
      die('不正な入力です。');
    }
    return $var;
  }
}
?>
<!--$_SERVER['PHP_SELF']も自分自身への送信になるが、$_SERVER['PHP_SELF']をaction属性値として直接<form>タグに記述するとXSS脆弱性となるので、絶対に避ける。「""」(空)にするか、以下のようにhtmlspecialchars()関数でエスケープする。-->
<div id="form_wrapper">
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8'); ?>">
	ハッシュ化する文字列(パスワード): <input type="text" name="pass" value="<?php echo htmlspecialchars($pass, ENT_QUOTES, 'UTF-8'); ?>" size="50" /><br />
<input type="submit" value="パスワード生成" />
</form>
</div>
<p>以下がハッシュ化されたパスワードです。</p>
<p id="idpw"><?php echo htmlspecialchars($pwd, ENT_QUOTES, 'UTF-8'); ?></p>
</div>
</body>

サンプルページ:「crypt_password.php

password_hash()

password_hash() は、強力な一方向ハッシュアルゴリズムを使って 新しいパスワードハッシュを作ります。 password_hash() は crypt() と互換性があるので、 crypt() が作ったパスワードハッシュは password_hash() でも使えます。PHP マニュアルの crypt() 関数のページには以下のような記述があります。

  • password_hash() は、強力なハッシュを使い、強力なソルトを生成して、それを複数回自動的に適用します。
  • password_hash() は crypt() のシンプルなラッパーであり、既存のパスワードハッシュと互換性があります。
  • password_hash() を使うことを推奨します。

以下は、password_hash() の構文です。

string password_hash ( $password , $algo [, $options ] )

パラメータ

  • password(string):ユーザーのパスワード。
  • algo(integer):パスワードのハッシュに使うアルゴリズムを表すパスワードアルゴリズム定数
  • options(array):オプションを含む連想配列。各アルゴリズムがサポートするオプションについては、 パスワードアルゴリズム定数のページを参照。 省略した場合は、ランダムな salt を生成してデフォルトのコストを使います。

戻り値

ハッシュしたパスワードを返します。失敗した場合に FALSE を返します。

以下は、パスワード「password」を「PASSWORD_BCRYPT」アルゴリズムを指定してハッシュ化する例です。

<?php
echo password_hash("password", PASSWORD_BCRYPT);
?>
$2y$10$9aHYmLi3u0OrjGYj2/cDmeLaGFg99n.oCOe.uuBHUMeVb3EkouOKu

以下は、password_hash() を使って文字列(パスワード)をハッシュ化するフォームの例です。

前述の「crypt() 関数を使って文字列(パスワード)をハッシュ化するフォームの例」との違いは、「$pwd = crypt($pass, substr(crypt($pass), -2));」 の部分が「$pwd = password_hash($pass, PASSWORD_BCRYPT);」に変更になっている点だけです。

<body>
<div id="wrapper">
<h1>Password Generator</h1>
<?php
$id = '';
$pass = '';
$_POST = checkInput($_POST);
if(isset($_POST['pass'])) { $pass = $_POST['pass'];}
$pwd = password_hash($pass, PASSWORD_BCRYPT);

function checkInput($var){
  if(is_array($var)){
    return array_map('checkInput', $var);
  }else{
    if(get_magic_quotes_gpc()){  //php.iniでmagic_quotes_gpcが「on」の場合の対策
      $var = stripslashes($var);
    }
    if(preg_match('/\0/', $var)){  //NULLバイト攻撃対策
      die('不正な入力です。');
    }
    if(!mb_check_encoding($var, 'UTF-8')){  //文字エンコードのチェック
      die('不正な入力です。');
    }
    return $var;
  }
}
?>
<div id="form_wrapper">
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8'); ?>">
	ハッシュ化する文字列(パスワード): <input type="text" name="pass" value="<?php echo htmlspecialchars($pass, ENT_QUOTES, 'UTF-8'); ?>" size="50" /><br />
<input type="submit" value="パスワード生成" />
</form>
</div>
<p>以下がハッシュ化されたパスワードです。</p>
<p id="idpw"><?php echo htmlspecialchars($pwd, ENT_QUOTES, 'UTF-8'); ?></p>
</div>
</body>

サンプルページ:「password_hash.php

password_verify()

パスワードがハッシュにマッチするかどうかを調べるには、password_verify() 関数を使用します。以下が構文です。

boolean password_verify ( $password , $hash )

パラメータ

  • password(string):ユーザーのパスワード。
  • hash(string):password_hash() が作ったハッシュ。

戻り値

パスワードとハッシュがマッチする場合に TRUE、それ以外の場合に FALSE を返します。

以下は、パスワードがハッシュにマッチするかどうかを調べる例です。ハッシュは前述の例で使用したものです。

<?php
$hash = '$2y$10$9I4AugDWXesDUpAWKBAydO7uiyU56p9c1ugkooXxpuH9CTMcgcm3a';

if (password_verify('password', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>
Password is valid!

ハッシュの文字列はシングルクォートで囲むようにします。ハッシュには「$」が含まれる可能性があるので、ダブルクォートだと誤って変数と認識されないようにするために。

以下は、password_hash() を使ってハッシュ化したユーザー名とパスワードを使った Basic 認証の例です。

<?php
  $hashed_user = '$2y$10$tBpQbMZJJaOJYmxwwUCKX.pTJLkSkTvU0zFJ//dwDK7/delLO442G';  //別途 password_hash を使って作成 admin
  $hashed_password = '$2y$10$V.7fwZ7yvaMbjr6XbzvSXu6OSkXx2EBE3djB.Jkm/iGSs9QC5Lm4y';  //同上 password
  if (!isset($_SERVER['PHP_AUTH_USER'])) {
      header('WWW-Authenticate: Basic realm="Restricted Area"');
      header('HTTP/1.0 401 Unauthorized');
      header('Content-type: text/html; charset=utf-8');
      die("User ID and Password required.");
  } else {
       //password_verify() を使ってユーザー名とパスワードのハッシュを検証
       if(!password_verify($_SERVER['PHP_AUTH_USER'], $hashed_user) || !password_verify($_SERVER['PHP_AUTH_PW'], $hashed_password)) {
          header('WWW-Authenticate: Basic realm="Restricted Area"');
          header('HTTP/1.0 401 Unauthorized');
          header('Content-type: text/html; charset=utf-8');
          die("Authorization Failed.");
      }
  }
  /* 認証に成功 */  
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Basic Authentication</title>
</head>
<body>
<?php
echo "<h1>Welcome ! </h1><br>";
?>
</body>
</html>

サンプルページ:「basic_auth_pwhash.php

メール操作

PHP を使うとメールの送受信が行えます。以下はメールの送受信の方法などについてです。

まずは、ローカル環境(XAMPP)でメールが送信できるようにします。

XAMPP を使ってメールを送信

XAMPP のデフォルトの設定では、メールを送信すると「mailtodisk.exe」というプログラムが起動してディスク上にテキストファイルでメールを保存します(実際にはメールは送信されません)。テキストファイルはデフォルトでは「C:\xampp\mailoutput」に保存されます。

XAMPP で「sendmail.exe」(fakemail)というプログラムを使ってメールを送信するには、以下のファイルを編集する必要があります。

  • php.ini(C:\xampp\php\php.ini)
  • sendmail.ini(C:\xampp\sendmail\sendmail.ini)

メールアドレスを設定する必要があるので、利用できるメールアドレスを用意します。この例では、Gmail のアカウントを使ってメールを送信します。

php.ini の編集

編集する前に「C:\xampp\php\」にある「php.ini」を別名(php_original.ini など)で保存してバックアップしておきます。

「php.ini」をテキストエディタ等で開き、「mail function」という文字列を検索します。

以下のような部分が見つかります。行頭の「;」はコメントアウトで、これを外すとその行が有効になります。

 [mail function]
; XAMPP: Comment out this if you want to work with an SMTP Server like Mercury
; SMTP = localhost
; smtp_port = 25

; For Win32 only.
; http://php.net/sendmail-from
;sendmail_from = postmaster@localhost

; XAMPP IMPORTANT NOTE (1): If XAMPP is installed in a base directory with spaces (e.g. c:\program filesC:\xampp) fakemail and mailtodisk do not work correctly.
; XAMPP IMPORTANT NOTE (2): In this case please copy the sendmail or mailtodisk folder in your root folder (e.g. C:\sendmail) and use this for sendmail_path.  
; XAMPP: Comment out this if you want to work with fakemail for forwarding to your mailbox (sendmail.exe in the sendmail folder)
;sendmail_path = "\"C:\xampp\sendmail\sendmail.exe\" -t"

; XAMPP: Comment out this if you want to work with mailToDisk, It writes all mails in the C:\xampp\mailoutput folder
sendmail_path="C:\xampp\mailtodisk\mailtodisk.exe"   

デフォルトでは、sendmail_path="C:\xampp\mailtodisk\mailtodisk.exe" が有効になっているので、「mailtodisk.exe」というプログラムが起動してディスク上にテキストファイルでメールが保存されます。まずこの行頭に「;」を挿入して、この行をコメントアウトします。

次に、;sendmail_path = "\"C:\xampp\sendmail\sendmail.exe\" -t"の先頭の「;」を外して、この行を有効にしてファイルを保存します。これで、メールを送信すると「sendmail.exe」(fakemail)というプログラムが起動してメールが送信されるようになります。

編集後の「php.ini」の [mail function] の部分は以下のようになります。

 [mail function]
; XAMPP: Comment out this if you want to work with an SMTP Server like Mercury
; SMTP = localhost
; smtp_port = 25
・・・中略・・・  
; XAMPP: Comment out this if you want to work with fakemail for forwarding to your mailbox (sendmail.exe in the sendmail folder)
sendmail_path = "\"C:\xampp\sendmail\sendmail.exe\" -t"

; XAMPP: Comment out this if you want to work with mailToDisk, It writes all mails in the C:\xampp\mailoutput folder
; sendmail_path="C:\xampp\mailtodisk\mailtodisk.exe"   
sendmail.ini の編集

編集する前に「C:\xampp\sendmail\」にある「sendmail.ini」を別名(sendmail_original.ini など)で保存してバックアップしておきます。

「sendmail.ini」をテキストエディタ等で開き、「smtp_server」という文字列を検索します。「smtp_server=mail.mydomain.com」という行が見つかるので、これを「smtp_server=smtp.gmail.com」に変更し、送信サーバーを指定(この場合は gmail)します。

smtp_server=smtp.gmail.com

続いて、「smtp_port=」という文字列を検索します。「smtp_port=25」という行が見つかるので、これを「smtp_port=587」に変更し、SMTP の送信ポートを指定します。

smtp_port=587

更に、「auth_username」という文字列を検索し、認証のためのユーザー ID とパスワードを指定します。以下のような行が見つかります。

auth_username=
auth_password=

「auth_username=」を「auth_username=xxxxxx@gmail.com」に、「auth_password=」を「auth_password=password」に変更して保存します。
※ xxxxxx は実際の gmail のアカウントを、password は実際のパスワードを指定します。

auth_username=my_account@gmail.com
auth_password=X76%abc!+3 (実際のパスワード)

gmail 以外のメールアカウントも設定できます。その場合はメールサーバーの仕様に合わせて、以下を設定します。

pop3 before smtpサーバ(受信前にPOP3認証を行う)の場合には、受信サーバ(POP3サーバ)のサーバ名、ユーザーIDとパスワードを指定します。gmail の場合は不要。

pop3_server=pop.server.xxxx.com
pop3_username=user_name
pop3_password=User_password

編集が済んでファイルを保存したら XAMPP の Apache を再起動します。

動作確認(メール送信テスト)

以下の PHP を記述して、適当な名前(例:sendmail_test.php)で「C:\xampp\htdocs」に保存します。

このファイルをブラウザで開くとメールが送信され、問題なくメールが送信されると「メールを送信しました。」と表示されます。

<?php
$name = '山田太郎';  //送信者名
$email = 'xxxxxx@gmail.com';  //送信者メールアドレス
$subject = 'Subject:件名です。'; //件名
$body = 'Body:これは本文になります。 '; //本文

//メールの宛先
$mailTo = 'info@xxxxxx.com';

//mbstringの日本語設定(文字化け対策)
mb_language('ja');
mb_internal_encoding('UTF-8');

//From ヘッダー(メールソフトの From に表示される)
$header = 'From: ' . mb_encode_mimeheader($name). ' <' . $email. '>';

if(mb_send_mail($mailTo, $subject, $body, $header)) {
  echo "メールを送信しました。";
}else{
  echo "メールの送信に失敗しました。";
}
PHPMailer

「PHPMailer」は外部の SMTP サーバを利用(経由)してメールを送信するためのライブラリです。「PHPMailer」を XAMPP で利用できるようにする方法です。まずは、「PHPMailer」をダウンロードします。

PHPMailer を上記ページの「Download ZIP」ボタンをクリックしてダウンロードして「C:\xampp\php」に解凍します。

phpmailer

ライブラリを読み込むため「C:\xampp\php\php.ini」を編集して、include_path にライブラリの PATH を追加します。

「include_path」という文字列を検索すると、以下のような部分が見つかります。

行頭にセミコロンが付いていたら、削除して有効にします。

; Windows: "\path1;\path2"
include_path=".;C:\xampp\php\PEAR"

include_path にライブラリの PATH を追加します。それぞれのパスはセミコロン「;」で区切られているので、以下のようになります。

include_path=".;C:\xampp\php\PEAR;C:\xampp\php\PHPMailer"

「C:\xampp\php」に解凍した PHPMailer を PHPMailer として保存した場合。

SSLを使って通信する必要があるため、php.ini を編集して「extension=php_openssl.dll」 を有効にします。

「php_openssl.dll」という文字列を検索すると、以下のような部分が見つかります。

extension=php_openssl.dll

行頭にセミコロンが付いていたら、削除して有効にします。そして Apache を再起動します。

変更された内容は、phpinofo() で確認することができます。

include_path

include_path には、require()、include()などの関数がファイルを探すディレクトリのリストをセミコロン「;」区切りで指定します。それにより、インクルードパスで指定されたディレクトリ内のファイルは、相対パスや絶対パスをつけずにファイル名だけで呼ぶことができます。

PHP マニュアルの「include 」に、「ファイルのインクルードは、指定されたパスから行います。パスを指定しない場合は、 include_path の設定を利用します。」という記述があります。

動作確認(メール送信テスト)

以下の PHP を記述して、適当な名前(例:phpmailer_test.php)で「C:\xampp\htdocs」に保存します。

このファイルをブラウザで開くとメールが送信され、問題なくメールが送信されると「Message has been sent」と表示されます。(このままでは、日本語を使用すると文字化けします)

<?php
require_once('PHPMailerAutoload.php');  //PHPMailer の読み込み
$mail = new PHPMailer;  //PHPMailer のインスタンスを生成

$mail->isSMTP();    // SMTP を使用
$mail->Host = 'smtp.gmail.com';  // SMTP サーバーを指定
$mail->SMTPAuth = true;         // SMTP authentication を有効に
$mail->Username = 'xxxxxxxx@gmail.com';   // SMTP ユーザ名
$mail->Password = 'password';   // SMTP パスワード
$mail->SMTPSecure = 'tls';   // TLS encryption を有効に
$mail->Port = 587;    // TCP ポートを指定

$mail->setFrom('xxxxxxxx@gmail.com', 'Mailer');    //差出人
$mail->addAddress('info@example.com', 'WDL');     // 受信アドレス
$mail->addReplyTo('xxxxxxxx@gmail.com', 'Information');    //返信用アドレス
$mail->addCC('test@example.com');    //Cc アドレス

//$mail->addAttachment('photo_01.jpg');  // 添付ファイルを追加
$mail->isHTML(true);   // HTML形式のメールに設定

$mail->Subject = 'Here is the subject';
$mail->Body    = 'This is the HTML message body <b>in bold!</b>';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

if(!$mail->send()) {
    echo 'Message could not be sent.';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
    echo 'Message has been sent';
}    

mb_send_mail()

日本語のメールを PHP で送信する場合、マルチバイト文字のメールを送る mb_send_mail() を使用することができます。

以下は、mb_send_mail() の構文です。

bool mb_send_mail( $to , $subject , $message [, $additional_headers [,  $additional_parameter ]] )

ヘッダと本文は mb_language() の設定に基づき変換、エンコードされます。

パラメータ

  • to(string):送信先のメールアドレス。 各アドレスをカンマで区切ると、複数の宛先を to に指定できます。 このパラメータは、自動的にはエンコードされません。
  • subject(string):メールの件名。
  • message(string):メールの本文。
  • additional_headers(オプション)(string):メールヘッダの最後に挿入される文字列。 このパラメータは、自動的にはエンコードされません。
    通常、これは追加のヘッダ(From、Cc、Bcc)のために用いられます。 複数のヘッダを追加する場合は CRLF(\r\n)で区切ります。
  • additional_parameter(string):MTA へ渡す コマンドライン引数です。sendmail を利用する際に正しい Return-Path を設定するためなどに利用すると便利です。

戻り値

成功した場合に TRUE を、失敗した場合に FALSE を返します。

以下は、mb_send_mail() を使ってメールを送信する簡単なサンプルです。

mb_send_mail()関数には、送信者(Fromヘッダー)を指定する専用の引数がないので、第4引数の「追加ヘッダー」で指定する必要があります。また、第4引数の追加ヘッダーは、自動で日本語処理(エンコード)をしてくれないので、mb_encode_mimeheader() 関数で日本語部分を処理します。

<?php
//mbstringの日本語設定(文字化け対策)。言語、内部エンコーディングの指定
mb_language('ja');
mb_internal_encoding('UTF-8');

$to = 'info@example.com';  //メールの宛先
$subject = 'Subject:件名です。'; //件名
$body = 'Body:これは本文になります。 '; //本文

$sender_name = '山田太郎';  //送信者名
$from = 'xxxxxx@gmail.com';  //送信者メールアドレス

//From ヘッダー(メールソフトの From に表示される)
$header = 'From: ' . mb_encode_mimeheader($sender_name). ' <' . $from. '>';

if(mb_send_mail($to, $subject, $body, $header)) {
  echo "メールを送信しました。";
}else{
  echo "メールの送信に失敗しました。";
}    

reply-to など他のヘッダ情報に日本語を含ませたい場合も mb_encode_mimeheader() でエンコードします。

また、cc や bcc には "名前 <xxxxx@xxxx.xxx>" 形式のアドレス指定はできないので、必ず "xxxxx@xxxx.xxx" のように アドレスのみを指定します。(To ヘッダは使用しません→第1パラメータで指定)

以下は、Reply-To, cc, bcc ヘッダを追加する例です。「\r\n」はダブルクォートで囲む必要があります。

<?php
//mbstringの日本語設定(文字化け対策)。言語、内部エンコーディングの指定
mb_language('ja');
mb_internal_encoding('UTF-8');

$to = 'info@example.com';  //メールの宛先
$subject = 'Subject:件名です。'; //件名
$body = 'Body:これは本文になります。 '; //本文

$sender_name = '山田太郎';  //送信者名
$from = 'xxxxxx@gmail.com';  //送信者メールアドレス
$cc = 'xxxxxx@xxxxxx.com';
$bcc = 'xxxxxx@xxxxxx.com';

//From ヘッダー
$header = 'From: ' . mb_encode_mimeheader($sender_name). ' <' . $from. ">\r\n";
//Reply-To ヘッダー
$header .= 'Reply-To: ' . mb_encode_mimeheader ($sender_name) .' <' . $from. ">\r\n";
//Cc ヘッダー
$header .= 'cc: ' .$cc. "\r\n";
//Bcc ヘッダー
$header .= 'bcc: '.$bcc ; 

if(mb_send_mail($to, $subject, $body, $header)) {
  echo "メールを送信しました。";
}else{
  echo "メールの送信に失敗しました。";
}

Return-Path の設定

$header に Return-Path を追加しても意味がないので、Return-Path を設定するには、MTA(sendmail) へ渡すコマンドライン引数を指定します。それには、第5パラメータに次のような $parameter を渡します。 但し、Return-Path を付加するのはメールサーバーなので、以下の指定が反映されるかはサーバーによります。

$parameter="-f xxxxxx@gmail.com";

また、PHP がセーフモードの場合、セーフモードの制限により、第5パラメータは使えず、指定した場合はエラーになります。そのため、ini_get() 関数を使ってセーフモードが ON かどうかを判定しています。

<?php
//mbstringの日本語設定(文字化け対策)。言語、内部エンコーディングの指定
mb_language('ja');
mb_internal_encoding('UTF-8');

$to = 'info@example.com';  //メールの宛先
$subject = 'Subject:件名です。'; //件名
$body = 'Body:これは本文になります。 '; //本文

$sender_name = '山田太郎';  //送信者名
$from = 'xxxxxx@gmail.com';  //送信者メールアドレス
$cc = 'xxxxxx@xxxxxx.com';
$bcc = 'xxxxxx@xxxxxx.com';

//From ヘッダー
$header = 'From: ' . mb_encode_mimeheader($sender_name). ' <' . $from. ">\r\n";
//Reply-To ヘッダー
$header .= 'Reply-To: ' . mb_encode_mimeheader ($sender_name) .' <' . $from. ">\r\n";
//Cc ヘッダー
$header .= 'cc: ' .$cc. "\r\n";
//Bcc ヘッダー
$header .= 'bcc: '.$bcc ; 

//Return-Path を設定
$parameter  = "-f" .$from;

if(ini_get('safe_mode')){
  $result = mb_send_mail($to, $subject, $body, $header);
}else{
  $result = mb_send_mail($to, $subject, $body, $header, $parameter);
}

if($result) {
  echo "メールを送信しました。";
}else{
  echo "メールの送信に失敗しました。";
}

スパムメール扱いになってしまうような場合は、以下を確認します。

  • From ヘッダに存在するドメインが指定されているか
  • Return-Path ヘッダと From ヘッダが一致しているか

メールヘッダ

以下はメールヘッダの種類(一部)とその意味です。

メールヘッダの種類(一部)
ヘッダ 意味
From 差出人アドレス。
Sender 実際の差出人のアドレス。
To 宛先アドレス
cc カーボンコピー先のアドレス。
bcc ブラインドコピー先のアドレス。
Subject メール件名
Reply-To メールの返信先。指定されていない場合には、通常 From が返信先として使用される
Return-Path 返信先。Reply-To と異なるのは、メールサーバが付加する点。

PHPMailer の設定

mb_send_mail() はシンプルなメールを送る場合には問題ありませんが、HTML メールや添付ファイルを使ったメールを送る場合は、PHPMailer を利用したほうが簡単です。

  • require_once を使って PHPMailer を読み込みます。
  • 文字化けしないように、mb_language() と mb_internal_encoding() を使って言語、内部エンコーディングを指定します。
  • new PHPMailer で PHPMailer のインスタンス($mail)を生成します。
  • $mail のプロパティとメソッドを使ってそれぞれの設定を指定します。
  • ヘッダに日本語を使用する場合は、mb_encode_mimeheader() でエンコードします。
  • 日本語を使う場合、CharSet や Encoding を指定します。
  • 本文を mb_convert_encoding() でエンコードします。(AltBody は、テキスト表示の場合の本文です)
  • send() メソッドでメールを送信して、成功した場合と失敗した場合で表示する文字列を指定します。
<?php
require_once('PHPMailerAutoload.php');  //PHPMailer の読み込み

//言語、内部エンコーディングを指定
mb_language("japanese");
mb_internal_encoding("UTF-8");

$to = "info@wexample.com"; //宛先
$subject = "xxxx の件"; //件名
$body = "これが本文です。<b>HTML </b>が使用できます。"; //本文
$from = "xxxxxx@gmail.com"; //差出人アドレス
$fromname = "山田太郎"; //差し出し人名
$attachfile = "photo_01.jpg"; //添付ファイルパス
$cc = 'xxxxxx@xxxxxxxx.com';   //Cc アドレス
$bcc = 'xxxxxx@xxxxxxxx.com';  //Bcc アドレス

$mail = new PHPMailer;  //PHPMailer のインスタンスを生成

$mail->isSMTP();    // SMTP を使用
$mail->Host = 'smtp.gmail.com';  // SMTP サーバーを指定
$mail->SMTPAuth = true;         // SMTP authentication を有効に
$mail->Username = $from;   // SMTP ユーザ名
$mail->Password = 'password';   // SMTP パスワード
$mail->SMTPSecure = 'tls';   // TLS encryption を有効に
$mail->Port = 587;    // TCP ポートを指定

$mail->setFrom($from);    //差出人アドレス
$mail->addAddress($to);  // 宛先アドレス
$mail->addCC($cc);    //Cc アドレス
$mail->addBcc($bcc);    //Bcc アドレス
$mail->addAttachment($attachfile);  // 添付ファイルを追加

$mail->isHTML(true);   // HTML形式のメールに設定

//日本語用設定
$mail->FromName = mb_encode_mimeheader($fromname);
$mail->Subject = mb_encode_mimeheader($subject);
$mail->CharSet = "iso-2022-jp";
$mail->Encoding = "7bit";
$mail->Body  = mb_convert_encoding($body,"JIS","UTF-8");
$mail->AltBody = mb_convert_encoding($body,"JIS","UTF-8"); //テキスト表示の本文

if(!$mail->send()) {
    echo 'メールを送信できませんでした。';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
    echo '送信完了';
}

上記では、差し出し人名を $mail->FromName (34行目)で指定していますが、以下のように $mail->setFrom() でまとめて指定することもできます。

$mail->setFrom($from, mb_encode_mimeheader($fromname))

IMAP でメールの受信

PHP で POP3 や IMAP などのメールサーバーに接続して、メールを受信するには imap_ から始まるメール受信用の関数(IMAP 関数)を利用します。

IMAP モジュールの利用

IMAP 関数を利用するには、 imap 拡張モジュールがインストールされていて、それが有効になっている必要があります。

imap 拡張モジュールを有効にするには、php.ini で「php_imap.dll」を検索します。そして行の先頭にセミコロン「;」が付いている場合は、セミコロンを外して、php.ini を保存します。

extension=php_imap.dll

php.ini の設定変更後は、必ず Apache を再起動します。

以下は、Gmail の受信したメールデータをテーブルに書き出す簡単なメールを受信するスクリプトの例です。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>メールの受信(IMAP)</title>
</head>
<body>
<table border="1">
<tr>
<td>No.</td>
<td>Subject</td>
<td>Name</td>
<td>E-mail</td>
<td>Date/Time</td>
<td>Contents</td>
</tr>
<?php
//imap_open() で使用するパラメータを変数に格納
$host = 'imap.googlemail.com';
$port = 993;
$username = 'xxxxxx@gmail.com';
$password = 'password';
$server = '{'. $host .':'. $port.'/novalidate-cert/imap/ssl}';

if($mbox = imap_open($server."INBOX", $username, $password)) {
  $mboxes = imap_check($mbox);
  $mail_cnt = $mboxes -> Nmsgs;
  mb_internal_encoding("UTF-8");  //タイトルが文字化けする場合は指定
  if($mail_cnt > 0) {
    for($i = 1; $i <= $mail_cnt; $i++) {
      $header = imap_headerinfo($mbox, $i);
      $title = htmlspecialchars(mb_decode_mimeheader($header -> subject));
      $from_date = $header -> date;
      $from = $header -> from;
      $email = $from[0] -> mailbox . "@". $from[0] ->host;
      $name = mb_decode_mimeheader($from[0] -> personal);
      $content = imap_fetchbody($mbox, $i, 1, FT_PEEK);
      $content = htmlspecialchars(mb_convert_encoding($content, "UTF-8", "ISO-2022-JP"));
?>
<tr>
<td><?= $i ?></td>
<td><?= $title ?></td>
<td><?= $name ?></td>
<td><?= $email ?></td>
<td><?= $from_date ?></td>
<td><?= $content ?></td>
</tr>
<?php
    }
  }
  imap_close($mbox);
}else{
  echo "could not connect to the mail box";
}
?>
</table>
<br><?= $mail_cnt ?>件のメールを受信しました。
</body>
</html>

おまかな流れは以下のようなものです。

  • imap_open() でパラメータに、サーバー名、ポート番号、ユーザー名、パスワードなどを指定して、メールボックスへの IMAP ストリームをオープンします。
  • IMAP ストリームのオープンに成功したら、imap_check() でールボックスに関する情報を調べます。
  • それぞれのメールに対して、imap_headerinfo() でメールのメッセージヘッダの情報を取得したり、imap_fetchbody() でメッセージ本文を取得したりします。
imap_open()

imap_open() 関数は、メールボックスへの IMAP ストリームをオープンします。以下が構文です。(オプションのパラメータは省略)

resource imap_open( $mailbox , $username , $password )

この関数は、POP3 や NNTP サーバーへのストリームをオープンする際にも使用可能です。 しかし、いくつかの関数および機能は IMAP サーバーでしか利用できません。

パラメータ

  • mailbox(string):メールボックス名(mailbox)は、サーバー名の部分と使用するサーバーにおける メールボックスへのパスから構成されます。特別な名前 INBOX は、 カレントユーザーの個人メールボックスを意味します。
  • username(string):ユーザー名。
  • password(string):username のパスワード。

戻り値

成功した場合は IMAP ストリームを、失敗した場合は FALSE を返します。

mailbox パラメータ

パラメータの「mailbox」は以下のような構成になっています。

{サーバー名 : ポート / オプションのフラグ } メールボックス名
  • サーバー名:Internet ドメイン名 あるいは括弧でかこまれたサーバーの IP アドレス。
  • ポート:オプションの TCP ポート番号。デフォルトは そのサービスのデフォルトポート。
  • オプションのフラグ:以下の表を参照ください。
  • メールボックス名:リモートメールボックス名。デフォルトは INBOX 。

前述の Gmail の例の場合は、以下のようになっています。

{imap.googlemail.com:993/novalidate-cert/imap/ssl}INBOX

オプションのフラグ名
フラグ 説明
/service=service メールボックスにアクセスするサービス。デフォルトは "imap" 。
/user=user サーバーへのログイン時のユーザー名。
/authuser=user リモートの認証ユーザー。指定されていた場合は、このユーザーのパスワードが 認証に使用されます(例: administrator)。
/anonymous 匿名ユーザーとしてアクセスします。
/debug プロトコルの通信内容をアプリケーションのデバッグログに記録します。
/secure ネットワーク越しにプレーンテキストのパスワードを送信しません。
/imap, /imap2, /imap2bis, /imap4, /imap4rev1 /service=imap と同じです。
/pop3 /service=pop3 と同じです。
/nntp /service=nntp と同じです。
/norsh 事前に認証済みの IMAP セッションを確立する際に、rsh や ssh を使用しません。
/ssl セッションの暗号化に Secure Socket Layer を使用します。
/validate-cert TLS/SSL サーバーの証明書を検証します(デフォルトの挙動です)。
/novalidate-cert TLS/SSL サーバーの証明書を検証しません。サーバーが自己証明の 証明書を使用している際に必要となります。
/tls セッションの暗号化に start-TLS の使用を強制し、それを サポートしていないサーバーとの接続を拒否します。
/notls たとえサーバーがそれをサポートしていたとしても、 セッションで start-TLS による暗号化を使用しません。
/readonly 読み込み専用でのメールボックスのオープンを要求します(IMAP のみ。 NNTP では無視され、SMTP および POP3 ではエラーとなります)。
imap_check()

imap_check() 関数は、現在のメールボックスに関する情報を調べます。以下が構文です。

object imap_check( $imap_stream )

パラメータ

  • imap_stream(resource):imap_open() が返す IMAP ストリーム。

戻り値

以下のプロパティをもつオブジェクトの情報を返します。失敗した場合には FALSE を返します。

  • Date - 現在のシステム時刻をフォーマットしたもの。
  • Driver - メールボックスにアクセスする際に使用するプロトコル: POP3、IMAP、NNTP
  • Mailbox - メールボックスの名前。
  • Nmsgs - メールボックス内のメッセージの数。
  • Recent - メールボックス内の新規メッセージの数。

前述の例では、Nmsgs プロパティからメールボックス内のメッセージの数を取得しています。

$mboxes = imap_check($mbox);
$mail_cnt = $mboxes -> Nmsgs;
imap_headerinfo()

imap_headerinfo() 関数は、メールのメッセージヘッダを読み込んで、指定したメッセージ番号についての情報を取得します。以下が構文です。(オプションのパラメータは省略)

object imap_headerinfo( $imap_stream , $msg_number )

パラメータ

  • imap_stream(resource):imap_open() が返す IMAP ストリーム。
  • msg_number(int):メッセージ番号。

戻り値

以下のプロパティ(抜粋)をもつオブジェクト(の配列)を返します。

  • to - To: 行から、次のプロパティを含むオブジェクトの配列を返します。 personal、adl、 mailbox および host
  • from - From: 行から、次のプロパティを含むオブジェクトの配列を返します。 personal、adl、 mailbox および host
  • reply_to - Reply-To: 行から、次のプロパティを含むオブジェクトの配列を返します。 personal、adl、 mailbox および host
  • sender - Sender: 行から、次のプロパティを含むオブジェクトの配列を返します。 personal、adl、 mailbox および host
  • date - ヘッダにあるメッセージの日付。
  • subject - メッセージの件名。
  • Size - メッセージのサイズ。

personal、adl、 mailbox および host は以下になります。

  • mailbox - メールボックス名(ユーザー名)。
  • host - ホスト名。
  • personal - 個人名(名前)。
  • adl - ドメインソースルート。

前述の例では、以下のような情報を取得しています。

//メールのヘッダー情報のオブジェクトを取得
$header = imap_headerinfo($mbox, $i);
//件名 $header -> subject をデコードしてエスケープ処理
$title = htmlspecialchars(mb_decode_mimeheader($header -> subject));
//受信日を取得
$from_date = $header -> date;
//差出人情報のオブジェクトを取得
$from = $header -> from;
//$from[0] -> mailbox (ユーザー名)、$from[0] ->host(ホスト名)でメールアドレスを作成
$email = $from[0] -> mailbox . "@". $from[0] ->host;
//差出人名をデコード
$name = mb_decode_mimeheader($from[0] -> personal);
imap_fetchbody()

imap_fetchbody() 関数は指定されたメッセージ本文中の特定のセクションを取得します。 本文パートは、この関数ではデコードされません。以下が構文です。

string imap_fetchbody( $imap_stream , $msg_number , $section [, $options ] )

パラメータ

  • imap_stream(resource):imap_open() が返す IMAP ストリーム。
  • msg_number(int):メッセージ番号。
  • section(string):パート番号。ピリオドで区切られた整数文字列を指定します。 これは、IMAP4 仕様における本文パートのリストへのインデックスとなります。
  • options(int):ビットマスクであり、以下の組合わせとなります。
    • FT_UID : msg_numberは UID である
    • FT_PEEK : 既に設定されていない場合、 \Seen(既読)フラグを設定しない
    • FT_INTERNAL : 内部フォーマットで文字列を返す。CRLF に正規化しない。

section/パート番号は、添付のないテキストメッセージの場合は以下のようになります。

  • 0 - Message header(メッセージヘッダ)
  • 1 - Body text(本文のテキスト)

UID(Unique Identifier)とメッセージ番号

POP3と同様に、IMAP4でもUID(POP3ではUIDL)とメッセージ番号が存在している。メッセージ番号はメールボックス内の連番であり、次回セッションでは再度振り直され変わってしまう可能性がある(永続的ではない)が、UIDはメールボックス内で必ず永続的に一意であることが保証される。メールへのアクセスは主にメッセージ番号で行われるが、UIDによってメッセージを識別することもできる。特に、クライアントがローカルにメールボックスのコピーを保持している場合に有効である。

http://www.atmarkit.co.jp/ait/articles/0106/13/news001.html

IMAP4のFETCHオプションの一覧

戻り値

指定されたメッセージ本文中の特定のセクションをテキスト文字列で返します。

前述の例では、本文のテキスト(1)を既読フラグを設定しない(FT_PEEK)で取得しています。

また、mb_convert_encoding() で「ISO-2022-JP」から「UTF-8」に変換して、エスケープ処理しています。

$content = imap_fetchbody($mbox, $i, 1, FT_PEEK);
$content = htmlspecialchars(mb_convert_encoding($content, "UTF-8", "ISO-2022-JP"));