PHPコーディング

SMTPソケットをオープンしてメール送信(メールヘッダ)

2010年01月26日
本当は【DIGEST-MD5】もコーディングしたかったのですが、どうしても【DIGEST-MD5】ではどのように認証すればいいのかの記述が見つかりません。英語で書いてある仕様書はあるようなのですが、日本語で書かれているページがみつかりません。

それよりも前に進まないといけないので、とりあえずメール送信に進みます。

$return-path = "return@domain.xxx";
$to = "sendto@yyyy.zzz";
$bcc1 = "bcc1@domain.xxx";
$bcc2 = "bcc2@domain.xxx";

まずはヘッダ部からです。

送信元メールアドレスをサーバーに知らせます。
この送信元はメーラーでは送信者としては表示されません。smtpサーバーによりreturn-pathに設定されます。メーラーに送信者として表示されるのはDATA部のfromです。
@fputs($sock, "MAIL FROM:<".$return-path.">\r\n");

送信先メールアドレスをサーバーに知らせます。
@fputs($sock, "RCPT TO:<".$to.">\r\n");

BCCがある場合はBCC分【RCPT TO】コマンドを送信します。
@fputs($sock, "RCPT TO:<".$bcc1.">\r\n");
@fputs($sock, "RCPT TO:<".$bcc2.">\r\n");

次回はDATA部です。

PHPコーディング

SMTPソケットをオープンしてメール送信(CRAM-MD5認証)

2010年01月24日
今日はsmtp認証の中の【CRAM-MD5】方式です。

まずは、サーバーに【CRAM-MD5】方式で認証を行うことを通知します。
@fputs($sock, "AUTH CRAM-MD5\r\n");

成功すると、サーバーは認証に使うキーを返してきます。
$line = @fgets($sock, 128);
キーは、【334 キー】の形式で返ってくるので、【334 】を取り除きます。
$key = str_replace("334 ", "", $line);

この方式は
Base64エンコード(ユーザ名+空白1文字+サーバーからのキーをパスワードをキーとしてMD5ダイジェストした値)
を認証に用います。

$ninsyo = base64_encode($id.' '.hash_hmac('md5', $key ,$pw));
で認証用文字列が得られます。


次に、得られた文字列で認証です。
@fputs($sock, $ninsyo."\r\n");
$result = @fgets($sock, 128);
成功していたら、【235 ・・・】が返ってきます。


この方式は、IDやパスワードがデコードできなきので、セキュリティが高い方式です。

PHPコーディング

SMTPソケットをオープンしてメール送信(LOGIN認証)

2010年01月22日
今日はsmtp認証の中の【LOGIN】方式です。

まずは、サーバーに【LOGIN】方式で認証を行うことを通知します。
@fputs($sock, "AUTH LOGIN\r\n");

成功すると、サーバーはIDを入力するように求めてきます。
IDをBase64エンコードして送信します。
@fputs($sock, "base64_encode($id)."\r\n");

成功すると今度はパスワードを入力するように求めてきます。
パスワードをBase64エンコードして送信します。
@fputs($sock, "base64_encode($pw)."\r\n");

成功していたら、【235 ・・・】が返ってきます。

この方式は、base64でエンコードしていますが、簡単にIDやパスワードをデコードできるので、セキュリティが高い方式ではありません。

PHPコーディング

SMTPソケットをオープンしてメール送信(PLAIN認証)

2010年01月21日
昨日の続きです。
昨日はソケットオープンまで成功しました。

今日はsmtp認証です。
smtp認証にも色々ありますが、まずは【PLAIN】方式についてです。

この方式は、【PLAIN】方式で認証を行うことをサーバーに通知する文字列に続いて、認証文字列をサーバーに送信します。

認証文字列に使うのは、サーバーによって
$id."\0".$id."\0".$pw

$id."\0".$pw
をbase64でエンコードした文字列です。

$ninsyo = base64_encode($id."\0".$id."\0".$pw);
@fputs($sock, "AUTH PLAIN ".$ninsyo."\r\n");
$result = @fgets($sock, 128);
成功してれば、【235 ・・・】と先頭が【235】の文字列がサーバーから返ってきます。

もしエラーなら、
$ninsyo = base64_encode($id."\0".$pw);
@fputs($sock, "AUTH PLAIN ".$ninsyo."\r\n");
を送ってみます。

この方式は、base64でエンコードしていますが、簡単にIDやパスワードをデコードできるので、セキュリティが高い方式ではありません。

PHPコーディング

SMTPソケットをオープンしてメール送信(ソケットオープン)

2010年01月20日
PHPからメールを送信するには色々な方法があります。

手っ取り早く日本語メールを送信するには【mb_send_mail】があります。
添付ファイルやhtmlメールを送信するには、【mail】を使ってヘッダなど書かけば可能です。
しかし、この【mail】関数、いちいちSMTPソケットを開いたり閉じたりするので、メールマガジン、ステップメールを配信するのには不向きのようです。
その上、sendmailやqmailなどがインストールされていて、PHPから使えないとメールが送信できないそうです。
更に更に、処理が重たいのです。

ソケットのオープン・クローズも制御するには直接ソケットをオープンしてメールを送信しなければいけないようです。

もちろんその辺りの無料のツールも出回ってます。有名どころではPEAR::Mail_Mime

利用してもいいのですが、なにかあったとき自分でちゃっちゃと直せないと困るので、自分で書いてみることにしました。

大まかな流れは、
1. ソケットオープン
2. メール送信
3. ソケットクローズ
です。

メールマガジンやステップメール配信の場合は2を繰り返します。

まずはソケットオープンです。

$smtp = "smtp.domain.xxx";
$port = 587;
メール送信時にも認証が必要になってから、ポート番号は587のことが多くなってます。
$id = "mail_id";
$pw = "smtp_pw";

$sock = @fsockopen($smtp, $port);
でオープンできます。

成功したら、
@fputs($sock, "EHLO ".$port."\r\n");
認証が必要になってからコマンドはEHLO。

ここでサーバーが色々な情報を返してきます。
そのうちの1つが、利用可能な認証方式。
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM
の形式で返ってきます。

利用できる認証方式は、PLAIN、LOGIN、DIGEST-MD5、CRAM-MD5、NTLM。
それぞれの認証方式は後にして、ここで問題が発生です。

while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
として、サーバーから返された文字列を取得しようとしたのですが、どうやら無限ループに陥っているようで、タイムアウトになってしまいます。

調べていくと、【feof($sock)】でfalseが返ってきていないようです。【fsockopen】でオープンしたストリームでも使えるのに・・・なぜ?PHPのバグ??それとも終了しないとeofが返ってこないのか???

仕方ないので、別の方法に切替です。

while ($result = @fgets($sock, 128)) {
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
で一応解決です。
でも、ものすごく時間がかかってしまいます。
時間がかかっているのは、【$result = @fgets($sock, 128)】の部分です。
文字列が返ってくる場合は早いのですが、falseが返ってくるのに時間がかかっています。

PHPのマニュアルを読むと、【feof】関数はタイムアウトになったときもfalseを返すとのこと。

そこで、タイムアウトまでの時間を短くし、処理時間の短縮を試みました。

【stream_set_timeout】でタイムアウトまでの時間を設定できます。
タイムアウトになったら【stream_get_meta_data】で値が取得できます。

stream_set_timeout ($sock, 5);    //タイムアウトまでの時間を5秒に設定
while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $info = stream_get_meta_data($sock);
  if($info['timed_out'] == true){  //タイムアウトになったらこの値がtrueになる
    break;
  }
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}

5秒ではなくもっと小さな値にすればもっと早くなります。

これで解決!のようです。動かしているうちまた不都合が見つかるかもしれませんが。

長くなったので、smtp認証はまた次回。

PHPコーディング

パーミッションの変更

2009年11月21日
PHPからファイルのパーミッションを変更する方法です。

まずは、現在の8進形式のパーミッションの取得から。
$file_path = "パーミッションを取得したいファイルパス";
$now = substr(sprintf('%o', fileperms($file_path)), -4);
これで、【0755】や【0644】などが返ってきます。

次に、パーミッションの設定です。
$permission = 0600;
$file_path = "パーミッションを変更したいファイルパス";
$result = @chmod($file_path, intval($permission, 8));

本当に設定できたか確認。
$after = substr(sprintf('%o', fileperms($file_path)), -4);

しかし、この流れだと問題が起こります。
ファイル情報がキャッシュされるので、パーミッションが無事変更できたとしても、$afterには$nowと同じ値が返ってきます。

そこで、キャッシュ情報をクリアします。関数【clearstatcache】をコールするだけです。

$file_path = "パーミッションを取得したいファイルパス";
$now = substr(sprintf('%o', fileperms($file_path)), -4);

clearstatcache();

$permission = 0600;
$file_path = "パーミッションを変更したいファイルパス";
$result = @chmod($file_path, intval($permission, 8));

$after = substr(sprintf('%o', fileperms($file_path)), -4);

これでOKです。

PHPコーディング

ftp接続でファイルをアップロード&ダウンロード

2009年11月20日
読み込み権限のないファイルをコピーするには、ftpでサーバーに接続し、ダウンロードする必要があります。

それをPHPから行う方法です。cronなど自動実行と組み合わせると、別サーバーにあるファイルと同期をとるなど、色々なことが自動でできるようになります。

//ftp接続
$connection = @ftp_connect(ftpサーバーのアドレス);

//ユーザ名とパスワードでログイン
$loginResult = @ftp_login($connection, ユーザーID, パスワード);

//パッシブモードオン
@ftp_pasv($connection, true);

//ファイルをダウンロードし、サーバー(ローカル)へ保存
@ftp_get($connection, ローカルパス, リモートパス, FTP_BINARY);
//ASCIIモードで転送する場合は、【FTP_BINARY】を【FTP_ASCII】へ

//ファイルをアップロードし、サーバー(リモート)へ保存
@ftp_put($connection, リモートパス, ローカルパス, FTP_ASCII);
//ASCIIモードで転送する場合は、【FTP_ASCII】を【FTP_BINARY】へ

//いらなくなったファイルを削除
@ftp_delete($connection, 削除するファイルのパス);

//接続を閉じる
@ftp_close($connection);

ここで、ftpの転送モード、バイナリモードとASCIIモードについて。
バイナリモードの場合はまったく同じファイルとして転送しますが、テキストのASCIIモード転送の場合はOS側で異なる改行コードを自動的に修正してくれます。
ftpでファイルをアップロード、ダウンロードする場合はどちらかを選べます。

※ サーバーにPHPのftpモジュールが組み込まれていないと使えません。
  私の使っているレンタルサーバーはオプションなので、自分でphp.iniを設定しました。

PHPコーディング

フォームを介さずPOSTする

2009年09月14日
まず、POSTするデータそのものは、
 key1=value1&key2=value2・・・
の形式にします。

$modeと$data['item']をPOSTする場合、
 mode=urlencode(stripslashes($mode)&data[item]=urlencode(stripslashes($data['item'])
となります。
$post_value = "mode=".urlencode(stripslashes($mode)."&data[item]=".urlencode(stripslashes($data['item']);
とします。


//postするURL
$server = "http://sample-site.com/transaction.php";
//URL分解
$url = parse_url($server);
//ポート番号
$port = 80;
//タイムアウトまでの秒数
$timeout = 30;

//ソケット接続オープン
$sock = fsockopen($url['host'], $port, $err_no, $err_msg, $timeout);
//エラーが起こったら、エラーナンバー、エラーメッセージ、$sockにfalseが返される

fputs($sock, "POST " . $url['path'] . " HTTP/1.0\r\n");
fputs($sock, "Host: ".$url['host']."\r\n");
fputs($sock, "Content-Type: application/x-www-form-urlencoded\r\n");
fputs($sock, "Content-length: " .strlen($post_value). "\r\n");
fputs($sock, "User-Agent: request\r\n");
fputs($sock, "\r\n");
fputs($sock, $post_value."\r\n");


postするドメインとpostされるドメインが違う場合はこれで動いていたのですが、ドメインが同じ場合は動きませんでした。
$url['host'] = "localhost";
とすると動きました。

PHPコーディング

文字コードの取得

2009年08月31日
変数に入っている文字のコードを確かめたい場合、【mb_detect_encoding】関数を使うと文字コードが返ってきます。

mb_detect_encoding(コードを確かめたい文字列, 確かめる順番, 検出方法);
で、2番目と3番目の引数は省略可能です。(特に3番目は省略)

なので、
mb_detect_encoding(コードを確かめたい文字列);
として文字コードを取得しようとしたのですが、エラーが出てどうしても取得できません。

そこで、2番目の引数に、順番を指定してやると・・・
あっさり文字コードを返してきました。文字列が短すぎたのか?

【mb_detect_encoding(コードを確かめたい文字列, "EUC-JP, SJIS, JIS, UTF-8, ASCII");】

PHPコーディング

ブラウザからファイルのダウンロード

2009年08月29日
ファイルをサーバーにアップロードするには、htmlのフォームから行えます。
しかし、ダウンロードするのはそうはいきません。

PHPからサーバーへコマンドを出力します。

$dl_filepath = ダウンロードしたいファイル名;
header("Cache-Control: public");
header("Pragma: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".basename($dl_filepath)."\"");
header("Content-Length: ".filesize(dl_filepath));
readfile(dl_filepath);

サーバーにあるファイルをダウンロードします。
編集して出力する場合、テンポラリファイルを作るのが簡単です。


こんな例もありました。
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($dl_filepath));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($dl_filepath));
ob_clean();
flush();
readfile($dl_filepath);


しかし、ブラウザによって正常にダウンロードできないこともあるようです。特にIE系がダメなようです。
やっぱり困ったブラウザだ・・・

PHPコーディング

【mail】関数で添付ありhtml形式メールを送信(代替本文あり)

2009年08月27日
だんだん複雑になっていきます。

ヘッダ部
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
"Content-Type: multipart/mixed; boundary=[バウンダリ文字列]\n
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


ボディ部
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This is a multi-part message in MIME format.\n\n

--[バウンダリ文字列]\n
Content-Type: multipart/alternative; boundary=[バウンダリ文字列_1]\n

//ボディ代替
--[バウンダリ文字列_1]\n
Content-type: text/plain; charset=ISO-2022-JP\r\n
Content-Transfer-Encoding: 7bit\n\n

[JISにエンコードした代替本文]
\n

//htmlボディ
--[バウンダリ文字列_1]\n
Content-type: text/html; charset=ISO-2022-JP\r\n
Content-Transfer-Encoding: 7bit\n\n
[JISにエンコードした本文]
\n

--[バウンダリ文字列_1]\n

//添付ファイル
\n\n
--[バウンダリ文字列]\n
Content-Type: [MIMEファイルタイプ]\n
Content-Transfer-Encoding: base64\n
Content-Disposition: attachment; filename="[添付ファイルにつける名前]"\n\n
chunk_split(base64_encode([base64でエンコードした添付ファイルの内容]))\n
//添付ファイル

--[バウンダリ文字列]--
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ボディ本体、代替本文のバウンダリ文字列を別に定義していることがポイントです。

※ []には送信するメールの内容を設定します。

PHPコーディング

【mail】関数で添付なしhtml形式メールを送信(代替本文あり)

2009年08月26日
ヘッダ部
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
Content-Type: multipart/alternative; boundary=[バウンダリ文字列]\n
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
【multipart/alternative】で、html本体と、代替本文があることを宣言しています。


ボディ部
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This is a multi-part message in MIME format\n\n

--[バウンダリ文字列]\n
Content-Type: text/plain; charset=ISO-2022-JP\n
Content-Transfer-Encoding: 7bit\n\n

[JISにエンコードした代替本文]
\n

--[バウンダリ文字列]\n
Content-type: text/html; charset=ISO-2022-JP\r\n
Content-Transfer-Encoding: 7bit\n\n
[JISにエンコードした本文]
\n

--[バウンダリ文字列]--
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(html形式の【Content-Transfer-Encoding】に【quoted-printable】が指定してある例もあり)

※ []には送信するメールの内容を設定します。

PHPコーディング

【mail】関数で添付ありhtml形式メールを送信(代替本文なし)

2009年08月25日
添付ありプレーンテキスト形式メールを送信する場合とほとんど同じです。

違うのは、ボディ部の【Content-type】で、【text/plain】から【text/html】にします。

ヘッダ部は
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
Content-Type: multipart/mixed; boundary=[バウンダリ文字列]\n
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


ボディ部は
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This is a multi-part message in MIME format\n\n
--[バウンダリ文字列]\n
Content-type: text/html; charset=ISO-2022-JP\r\n
Content-Transfer-Encoding: 7bit\n\n

[JISにエンコードした本文]

//添付ファイル
\n\n
--[バウンダリ文字列]\n
Content-Type: [MIMEファイルタイプ]\n
Content-Transfer-Encoding: base64\n
Content-Disposition: attachment; filename="[添付ファイルにつける名前]"\n\n
chunk_split(base64_encode([base64でエンコードした添付ファイルの内容]))\n
//添付ファイル

--[バウンダリ文字列]--
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


※ []には送信するメールの内容を設定します。

PHPコーディング

【mail】関数で添付なしhtml形式メールを送信(代替本文なし)

2009年08月24日
本文がhtml形式のメールを送る場合、メーラーが対応していないことを考慮して、代替本文も付けて送ることがありますが、まずは代替本文のないメールを送ります。

この場合、プレーンテキストメールを送信する場合とほとんど同じです。

違うのは、ヘッダの【Content-type】で、【text/plain】から【text/html】にします。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
Content-type: text/html; charset=ISO-2022-JP\r\n
Content-Transfer-Encoding: 7bit
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


本文には、JISでエンコードした文字列を引き渡します。

※ []には送信するメールの内容を設定します。

PHPコーディング

【mail】関数で添付ありプレーンテキスト形式メールを送信

2009年08月23日
次はプレーンテキスト形式のメールに、添付ファイルを付けて送ってみます。


ヘッダ部は
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
Content-Type: multipart/mixed; boundary=[バウンダリ文字列]\n
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

バウンダリ文字列は区切りをあらわす文字列で、メールごとに定義します。
md5(uniqid(rand()));
などでランダムに作成します。

--[バウンダリ文字列]\n
で区切りを表し、

--[バウンダリ文字列]--
で終了を表します。



ボディ部は
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This is a multi-part message in MIME format\n\n
--[バウンダリ文字列]\n
Content-Type: text/plain; charset=ISO-2022-JP\n
Content-Transfer-Encoding: 7bit\n\n

[JISにエンコードした本文]

//添付ファイル
\n\n
--[バウンダリ文字列]\n
Content-Type: [MIMEファイルタイプ]\n
Content-Transfer-Encoding: base64\n
Content-Disposition: attachment; filename="[添付ファイルにつける名前]"\n\n
chunk_split(base64_encode([base64でエンコードした添付ファイルの内容]))\n
//添付ファイル

--[バウンダリ文字列]--
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


添付ファイルを複数つける場合は、【//添付ファイル】を繰り返します。


プレーンテキストやhtmlファイルなど、テキスト形式のファイルを添付する場合、MIMEタイプは【text/plain】や【text/html】ではなく、【application/octet-stream】にします。


他にも書き方がありますが、これが私には一番分かりやすくしっくりきました。

※ []には送信するメールの内容を設定します。

PHPコーディング

【mail】関数で添付なしプレーンテキスト形式メールを送信

2009年08月22日
まずは一番簡単な、添付ファイルのないプレーンテキスト形式のメールを、【mail】関数で送ってみます。


【mail】関数の仕様は
$result = mail($to, $subject, $body, $headers, $parameters);

【$to】は宛先メールアドレス、
【$subject】は件名、
【$body】はメール本文、
【$headers】はヘッダ、
【$parameters】は拡張パラメータです。



添付ファイルのない、プレーンテキスト形式のメールを送るには、MIME形式にする必要はないのですが、送られてくるメールを調べると、MIME形式で送られてくるので、私もMIME形式で送ることにします。

メールの文字コードはJISが一般的なので、メール本文はJISで送信します。


まずはどんなメールにも共通の宛先とサブジェクトです。

宛先メールアドレスへは、メールアドレスを引き渡してやればOKです。

サブジェクトにもし日本語が含まれる場合は、エンコードしてから引渡します。
mb_encode_mimeheader($subject, "EUC", "B");
(EUCへは$subjectの文字コードを引き渡す)


さて、問題のヘッダです。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
From: [宛先メールアドレス]\r\n
Reply-To: [返信メールアドレス]\r\n
Bcc: [メールアドレス]\r\n
MIME-version: 1.0\n
Content-Type: text/plain; charset=ISO-2022-JP\n
Content-Transfer-Encoding: 7bit
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ここで、from句に日本語宛先名を加える場合ですが、日本語名をサブジェクトと同様にエンコードします。
From: [エンコードした日本語宛先名]<[宛先メールアドレス]>\r\n

もちろんbccしない場合はbccの行はなし、ccする場合は同じように加えます。


メール本文は、JISにエンコードしてから引き渡します。
mb_convert_encoding([メール本文], "JIS", "ASCII,JIS,UTF-8,EUC-JP,SJIS");
本文の最後に【\n】を付加して引き渡します。


拡張パラメータには、リターンパスを設定することにしました。
-fリターンパスメールアドレス


※ []には送信するメールの内容を設定します。

PHPコーディング

ファイル更新日時の取得

2009年06月21日
PayPalは決済完了に自動で独自メールを送信できません。
なので、送信できるツール(PayPalオートメール)を配っているのですが、そのツールが動かないと連絡がありました。

自分のサーバーでは動いているので、全然気が付きませんでした。
サーバーによってエラーを吐いて止まるものと、無視して実行するものとあり、自分のところで動いているからといって、すべてのサーバーで動くとは限らないのが難しいところです。

で、その部分を直したら、今度は別のところで止まってしまった、という連絡が・・・

【get_headers】という関数を使って、ネットワーク上のファイルの更新日時を取得している部分があります。エラーメッセージを見てみると、この【get_headers】が認識できていないようなのです。

【get_headers】はPHPバージョン5以上でサポートしています。連絡を下さった方はバージョン5以上なのですが、インストールされていないようなのです。

困ったもんだ。

ロリポップを使っておられるのですが、以前私がロリポップでテストしたときは動いていたんだけどな・・・ なぜ動かない???
摩訶不思議。

もう一つファイルの更新日時を取得する関数に【filectime】がありますが、この関数は同じサーバー上のファイルにしか使えません。

で、結局【get_headers】は使わないようにソースを変更しました。

PHPコーディング

実行ファイルのフルパスを取得

2009年04月21日
ビジネス羅針盤 ステップメール】を動かしていて、ファイルのフルパスを取得する必要に迫られました。

cronから実行する場合、ファイルのパスと実行パスが変わるので、実行ファイルの絶対パスが必要になります。

【__FILE__】で取得できます。

$fullpath = __FILE__;
$base = basename(__FILE__);

【__DIR__】はファイルが存在するディレクトリです。

これらの定数は自動的に定義されるようです。
他にも色々あります。

自動的に定義される定数

PHPコーディング

不達メールの取り出し

2009年03月02日
送ったメールアドレスが無効な場合、エラーメールが返ってきます。

PHPからエラーメールをメールボックスから取り出します。

メールボックスにあるメールの全てのヘッダとボディを見ていると、不達で返ってきたリターンメールにはどうやら、

Final-Recipient: rfc822; メールアドレス
Action: failed

が含まれているようです。


不達メールを調べるには、【Final-Recipient】と【Action: failed】を探し、メールアドレスを抽出するのがよさそうです。


はじめはボディに含まれている【User Unknown】と【Host Unknown】を調べていたのですが、Yahoo!メールはアカウントがないという意味のエラーメールを返しているようなのです。Yahoo!メールからのリターンメールには【User Unknown】も【Host Unknown】も含まれていません。


他に共通するものがないかと調べてみると、全てに共通するのが、
Final-Recipient: rfc822; メールアドレス
Action: failed
のようなのです。

エラーナンバーからの調査も考えたのですが、他のものもひっかかりそうなので、断念しました。



メールボックスのメールを読むの各々のメールを取得の部分を少し書き換えればエラーメールのみ抽出できます。

for($id=1; $id<=$mail_num; $id++){
  @fputs($fp, "RETR ".$id."
");
  $line = @fgets($fp);
  while(!eregi("^.
? ", $line)){
    $mail = fgets($fp, 512);
  }
}

$mailの中に【stristr】関数を使って【Final-Recipient】と【Action: failed】が含まれるかチェックすればわかります。

PHPコーディング

メールボックスのメールを読む

2009年03月01日
PHPからメールボックスへ接続して、ボックス内のメールを取得する方法です。

まずはログインです。

接続
$fp = @fsockopen("メールサーバー", "ポートナンバー");
$line = @fgets($fp, 512);

ユーザー認証
@fputs($fp, "USER "."ユーザーID"."\r\n");
$line = @fgets($fp, 512);
@fputs($fp, "PASS "."パスワード"."\r\n");
$line = @fgets($fp, 512);

うまく接続できれば、最後の$lineに【OK】の文字が含まれています。これでログインした状態です。


ボックス内の情報を取得します。
@fputs($fp, "STAT\r\n");
$line = @fgets($fp, 512);

$lineにはステータス、メール数、サイズが半角スペースで区切られて返されます。
list($stat, $mail_num, $size) = explode(' ', $line);
でそれぞれの値が取り出せます。


各々のメールを取得するには、
for($id=1; $id<=$mail_num; $id++){
  @fputs($fp, "RETR ".$id."\r\n");
  $line = @fgets($fp);
  while(!eregi("^\.\r?\n", $line)){
    $mail.= fgets($fp, 512);
  }
}

$mailにはヘッダもボディも含まれます。


メールをサーバーから削除するには、
@fputs($fp, "DELE "."削除するメールのid"."\r\n");
です。


終了は
@fputs($fp, "QUIT\r\n");
@fclose($fp);
です。

PHPコーディング

エラーメールの受け取りアドレスを指定する

2009年01月09日
宛先のメールアドレスがない、などの理由で返信されるエラーメール。このエラーメールを受け取るアドレスを指定できれば便利です。

まず思い浮かぶのが【Return-Path】です。が、実はこの【Return-Path】、メールを送るときこちらが指定しても、経由するサーバーによって勝手に書き換えられてしまいます。

書き換えられるアドレスは、【envelope FROM】というものだそうです。

PHPからメールを送信するとき、この【envelope FROM】を指定できればエラーメールを受け取るアドレスを指定できるのだそうです。

【mail】関数や【mb_send_mail】関数の第5引数を使います。

mb_send_mail("送信先アドレス", "件名", "ボディ", "Fromなどのヘッダ", "-fエラーを受け取るアドレス");
となります。
【エラーを受け取るアドレス】が【err@domain.xxx】だとすると、第5引数には【-ferr@domain.xxx】を渡してやればいいのです。

【-f】オプションは【sendmail】のオプションです。

PHPコーディング

【mb_send_mail】関数でcc、bccが送信できない!?

2008年12月05日
改正迷惑メール防止法施行に伴い、ステップメールで登録があったとき、管理者アドレスへも登録メールを送信するようにソースを変更してみました。これでログになるはずです。

実際の送信は、bccで管理者へ送信します。


ところが・・・
送信できない。

なぜだ???

bccはヘッダへ書き込みますが、間違っているのか??
色々調べまわったのですが、合っている。


色々変えてみてもやっぱり送信できていない・・・


考えていて思い出しました。Webに置いている連絡フォームはbccが送信できていました。同じソースを利用しているので、ソースが違っているわけではないようです。


そこで、送信先のメールアドレスを変更してみました。

なんと!! ちゃんと来るではありませんか。

同じドメインが含まれているとサーバーが弾いているのかと思い、色々アドレスを変更してみました。私が持っているドメインで送れないようです。

レンタルサーバーの同じIDに契約しているドメインがダメなようです。
なんだぁ。あっさり解決です。


日本語メールを送るソースです。

//言語設定
mb_language("Japanese");
//内部エンコーディング
mb_internal_encoding("EUC") ;

$to = "aaa@bb.xxx";
$subject = "サブジェクト";
$body = "本文";
$from_name = "Aさん";

$from_name = mb_encode_mimeheader($from_name,"EUC","B");
$headers = "From: ".$from_name."<".$from.">\r\n";
$headers.= 'Reply-To:'.$Reply."\r\n";
$headers.= 'BCC:'.$bcc."\r\n";

//送信
$send_result = mb_send_mail($to, $subject, $body, $headers);


【mb_send_mail】関数は、サブジェクト、本文はエンコーディングしてくれますが、ヘッダはエンコーディングしてくれません。なので、【Form】に日本語を含める場合は事前にエンコーディングしておきます。

【to】、【bcc】、【cc】には日本語は含められません。

PHPコーディング

画像のアップロードと同時に【img】タグを書き込む

2008年12月04日
自前ブログ記事投稿画面で画像の登録をコーディングしてみました。







のようなフォームがあるとします。
画像をアップロードすると、テキストエリアに【img】タグを追加します。


PHP側ではくhtml
<form method="post" action="処理ファイル名" enctype="multipart/form-data" name="frmINS">
 <textarea name="body" cols="80" rows="40" wrap="soft">$body</textarea>
 <input type="button" value="ファイルアップロード" onClick='AddImageTag()'>
 <input type="submit" value="登録">
</form>


JavaScript
function AddImageTag(){
 document.frmINS.mode.value = 'UpLoad';
 document.frmINS.submit();
}

$modeの値をUpLoadに設定してサブミットします。


PHPで$modeがUpLoadのときの処理
アップロードしたファイルの情報は、$_FILESから利用できます。

$result = move_uploaded_file($_FILES['file']['tmp_name'], 保存ファイルパス);
アップロードしたファイルは、テンポラリファイルに保存されています。それを保存したいディレクトリ、ファイル名で移動させます。

list($width, $height, $type, $attr) = getimagesize(保存ファイルパス);
保存したファイルの情報を取得できます。

$body.= "<img src=\"保存ファイルパス\" width=\"".$width."\" height=\"".$height."\" alt=\"\" />\n";


これでテキストエリアに【img】タグが追加できます。

またずいぶん楽になりました。

PHPコーディング

文字列フォーマット

2008年11月24日
例えば、数字の【2】を【02】としたい場合、【sprintf】関数を使います。

$val = 2;
sprintf('%02d', $val);

これで【02】と表示されます。

PHPコーディング

ある月の日数を求める

2008年11月23日
ブログを移動させてます。ドメイン変更に伴って、ブログも動かす必要に迫られてます。

今までムーバブルタイプを使っていたのですが、ちょっと自作してみてます。


Movable Typeでは使いにくい点が結構あるし、逆に使わない機能もたくさんあるし。この際、シンプルに作ってみようとしてます。


で、カレンダーの表示に挑戦です。

今までは、現在時日時のカレンダーしか表示されなかったので、今度は自由にどんな年月のカレンダーも表示できるようにします。


指定した年月の最終日を取得する必要がでてきます。
月初めは1日から始まると決まっているので、来月の1日の1日前で、今月の末日が求められます。
この方法で1つ面倒なのは、年が変わる場合です。

で、調べてみると、【date】関数を使えば、任意の年月の日数がわかります。

date('t', 求めたい年月が含まれる日付のタイムスタンプ);
で求められます。

例えば、2008年11月の日数を求めるには、
date('t', mktime(0, 0, 0, 11, 1, 2008));
です。

【mktime】で作っているタイムスタンプは、2008年11月1日 00:00:00です。

PHPコーディング

日本語をエンコードしてURLに付加する

2008年10月26日
フォームのhiddenオブジェクトでpostやgetをする場合はいいのですが、urlに付加してgetする場合、日本語は化けてしまいます。

例えば、Webページ内にリンクがある場合です。

<a href="xxx.html?word=文字列">リンク</a>
のようにして$word変数の内容をpostする場合です。

このままだと【文字列】は化けるので、エンコードします。


<a href="xxx.html?word=urlencode(文字列)">リンク</a>


デコードするときは、
urldecode($word);

です。

PHPコーディング

実行時間を設定する

2008年10月05日
PHPスクリプトの実行時間(これを過ぎてもスクリプトが終了していなければタイムアウトとなる)は、デフォルトで30 秒、php.iniで決められている場合もあります。


これを一時的に変更するには、
set_time_limit(実行秒数をintで指定);
を用います。


set_time_limit関数がコールされると、秒数カウンタはクリアされます。引数に設定した秒数は、set_time_limit関数がコールされてからの秒数になります。

PHPコーディング

ブラウザなどからのキャンセルを無視する

2008年10月04日
PHPスクリプトを実行しているとき、ブラウザからキャンセルボタンを押されても、スクリプトの実行を途中でやめたくない場合があります。

スクリプトの中に、
ignore_user_abort(true);
と書くと、ユーザーがキャンセルしてもスクリプトは最後まで実行されます。


ignore_user_abort(false);
だと、途中で中断されます。


ユーザーキャンセルを無視する場合、きちんと動作しているか確認した後に設定します。無限ループなどにはまってしまうと、自分では止められなくなってしまうことがあるので、注意が必要です。

PHPコーディング

ファイルロック

2008年09月16日
DBではなく、通常のファイルで、データ管理をしていると、ファイルをロックしたくなる場面に遭遇します。
DBが使えると、その辺はDBが処理してくれるので、楽なんですけどね・・・


ファイルをロックするには、まずファイルをオープンして、
$handle = @fopen(ファイルパス, モード);

ロックをかけます
$result = @flock($handle, LOCK_EX);
【LOCK_EX】なら排他的ロック(書き手)
【LOCK_SH】なら共有ロック(読み手)
となります。

ファイルへの書き込みなど処理をして、ロック解除です。
@flock($handle, LOCK_UN);

クローズしても解除されます。
@fclose($handle);


ファイルの中身を読み込んで、処理を行い、変更した内容を同じファイルに上書きしたいので、ファイルを開いたまま中身を全てクリアにする方法を調べてみした。

ファイルクリア
$result = @ftruncate($handle, 0);
ファイルポインタを先頭に持ってくる
$result = @fseek($handle, 0);

でいけます。

PHPコーディング

ファイルの更新日時書き換え

2008年09月13日
以前、PHPでのファイルの更新日時の変更方法を書きましたが、2番目の引数である更新日時(UNIXタイムスタンプ)を指定すると、エラーになってしまいます。
【Warning: touch(): Utime failed: Operation not permitted】
一番肝心な引数なのに!


いろいろ調べてみましたが、これといった解決方法が見つかりません。

指定しなければ、現在時刻を設定します。これはきちんと動作しています。


では、指定の仕方が悪いということになりますが、マニュアル通りに指定しても駄目です。
touch(ファイルのパス, time());


PHPをモジュール版で動かしているとエラーになるようなのですが、CGI版が動いています。


はい、あっさりお手上げです。


仕方ないので、手動でタイムスタンプを更新したいファイルを選択して、現在時刻に変更することにしました。

PHPコーディング

クッキー発行、クッキー読み取り

2008年09月07日
PHPでのクッキー読み書きです。

以前Perlでのクッキーの読み書きについて書きました。クッキーの設定項目は同じですが、読み書きの方法はPerlに比べてずいぶん簡単です。

クッキー設定
 setcookie(クッキー名, 値, 有効期限, パス, ドメイン, セキュア);
で設定できます。


読み取りは、$_COOKIE 配列に値が入っているので、読み取れます。
$_COOKIE['クッキー名'] = 値
です。


クッキー削除は、
 setcookie(クッキー名);
でOK。


有効期限を設定しなければ、ブラウザを閉じた時点でクッキーが削除されます。


Perlに比べ、すいずん簡単です。

PHPコーディング

日時形式チェック

2008年09月04日
PHPで日時のフォーマット形式をチェックする方法をネットで探したら、いくつか見つかりました。

その中の1つをアレンジして、

function chkDateTime($date_time){
 //YYYY-MM-DD HH:mm:ss
 if(isset($date_time) && (trim($date_time) != '')) {
  if(!preg_match('/^(\d\d\d\d)\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/', $date_time, $dates)) {
   return false;
  }
  else{
   if(!mktime($dates[4], $dates[5], $dates[6], $dates[1], $dates[2], $dates[3])) {
    return false;
   }
  }
 }
 return true;
}

という関数を作って、実行してみました。


ところが、不正な日時を入力しても通ってしまいます。調べていくと、【mktime】関数が思った動きをしていません。
ありえない日時を入力しても、修正して値を返してくれています。かしこい・・・


そこで、日付については、【checkdate】関数を使うことにしました。
checkdate($dates[2], $dates[3], $dates[1]);

時刻についても、関数を調べたのですが、適当な関数がないようです。で、泣く泣く(オーバー)自分で作りました。
時間は0以上23以下、分と秒は0以上59以下をチェックします。


合わせると、こんな関数になりました。

function chkDateTime($date_time){
 //YYYY-MM-DD HH:mm:ss
 if(isset($date_time) && (trim($date_time) != '')) {
  if(!preg_match('/^(\d\d\d\d)\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/', $date_time, $dates)) {
   return false;
  }
  else{
   if(!checkdate($dates[2], $dates[3], $dates[1])){
    return false;
   }
  
   if((0 <= $dates[4]) && ($dates[4] <= 23)){
   }
   else{
    return false;
   }
   
   if((0 <= $dates[5]) && ($dates[5] <= 59)){
   }
   else{
    return false;
   }
  
   if((0 <= $dates[6]) && ($dates[6] <= 59)){
   }
   else{
    return false;
   }
  }
 }
 return true;
}


コール側で、YYYY-MM-DD HH:mm:ss形式にして、コールします。日付の区切り文字【-】が【/】の場合、変換をかけてからコールします。


もっとスマートなやり方がある気がするのですが・・・ 見つかりませんでした。とりあえず動いているようです。

PHPコーディング

正規表現による入力チェック(ファイル名)

2008年09月03日
PHPで文字列のチェックをするには、正規表現でチェックする方法があります。

ファイル名に日本語が含まれていないかチェックするには、

ereg("^[-0-9a-zA-Z_.]+$", $var);

とします。

以前書いた、
ereg("[a-zA-Z0-9]", $var);
は、$varの1文字めしかチェックしないので、ファイル名のように複数文字列のチェックには使えません。


そこで、ちょっと拡張です。

まず、【[]】の中身です。

a-zは小文字のアルファベットa~zまで。
同じくA-Zは大文字、0-9は数字の0~9です。
【-】、【_】、【.】も使うことができます。
ここで、【-】を先頭に持っています。a-zの【-】と区別するためのようです。


次に、
【^】は$varの最初、という意味です。
【$】は$varの最後、という意味です。
【+】は、直前の文字が少なくとも1個以上あることを示しています。


つまり、【^[-0-9a-zA-Z_.]+$】を訳すと、
【文字列の先頭から終わりまで、少なくとも1つ以上の文字を含み、その文字は、大文字小文字の半角アルファベット、数字、-、_、.のみで構成される】ということになるようです。


慣れていないため、正規表現は苦手です。

使えるようになれば、この上なく便利なのだろうと思うのですが、あまり機会がないので、慣れれない・・・


更に、PHPには【PCRE 関数】と【POSIX 正規表現関数(Perl互換)】の2種類の正規表現チェック関数があり、チェックの仕方も微妙に異なるようです。
ますますわからない・・・

PHPコーディング

ブラウザからWebサーバーへファイルをアップロード

2008年09月02日
以前、Web系の会社にいたときやっていたのですが、どうやっていたのか全く思い出せず、ネットをあれこれ調べてみました。嫌ってほど見ていたのに・・・ 人間の記憶ってこんなもん?

アップロードそのものは、思っていたより簡単でした。


まずはフォームからULする準備です。
<form method="post" action="処理を行うURL" enctype="multipart/form-data" name="frmInfo">
 <input type="file" name="file" size="50">
 <input type="submit" value="アップロード">
</form>


ローカルのファイルを指定するボックスとディレクトリを指定するボタンが表示されます。ファイルのパスを入力して、【アップロード】ボタンをクリック。


次に、【処理を行うURL】では、ファイルの情報を受け取って処理します。

PHPでは、$_FILESにアップロードファイルの情報が格納されています。

$_FILES['file']['name']  :  ローカルのファイル名
$_FILES['file']['tmp_name']  :  サーバーにアップロードされたテンポラリファイル
$_FILES['file']['error']  :  エラー情報(0はエラーなし)
$_FILES['file']['type']  :  ファイルタイプ
$_FILES['file']['size']  :  ファイルサイズ

サーバーには【$_FILES['file']['tmp_name']】にすでにULされているので、【move_uploaded_file】関数でこれを本来ULしたいディレクトリに移動してやります。

move_uploaded_file($_FILES['file']['tmp_name'], "移動先");

PHPコーディング

文字列を前から検索する

2008年08月31日
PHPには、文字列を検索する関数が多々あります。どうしてかは知りません・・・


strstr  大文字小文字を区別して文字列が最初に現れる位置を見つける (PHP 4, PHP 5)

stristr  大文字小文字を区別せず文字列が最初に現れる位置を見つける (PHP 4, PHP 5)

stripos  大文字小文字を区別せずに文字列が最初に現れる位置を探す (PHP 5)


これ以外にも、最後に現れる位置を検索する関数もかります。

PHPコーディング

ディレクトリ以下のファイルとディレクトリを全て削除

2008年08月30日
ディレクトリの削除は【rmdir】関数を使えばできるのですが、ディレクトリが空の場合のみ削除できます。
つまり、ディレクトリの下に、ファイルやディレクトリがあると、エラーになります。


そのため、まず削除したいディレクトリにあるファイルとディレクトリを削除した後で、目的のディレクトリを削除します。

function DirAllDelete($del_dir, $flag="off"){
 $dir = @opendir($del_dir);
 if($dir == true){
  while($file = readdir($dir)){
   if(($file != ".") && ($file != "..")){
    //ファイルならすぐ削除
    if(is_file($del_dir."/".$file) === true){
     $result = unlink($del_dir."/".$file);
     if($result === false){
      return false;
     }
    }
    //ディレクトリなら、まずその下のファイルを削除
    elseif(is_dir($del_dir."/".$file) === true){
     //ファイル数カウント
     $result = $this -> CntFileNum($del_dir."/".$file, $cnt);
     if($result === false){
      return false;
     }
     //ファイルがなければディレクトリ削除
     if($cnt == 0){
      $result = rmdir($del_dir."/".$file);
      if($result === false){
       return false;
      }
     }
     //ファイルがあれば、まずファイルを削除
     else{
      $result = $this -> DirAllDelete($del_dir."/".$file);
      if($result === false){
       return false;
      }
      //その後ディレクトリを削除
      $result = rmdir($del_dir."/".$file);
      if($result === false){
       return false;
      }
     }
    }
   }
  }
 }
 else{
  return false;
 }
 
 //自分自身も削除の場合
 if($flag == 'on'){
  $result = rmdir($del_dir);
  if($result === false){
   return false;
  }
 }
 
 return true;
}

function CntFileNum($dir_cnt, &$cnt){
 $cnt = 0;
 $dir = @opendir($dir_cnt);
 if($dir == true){
  while($file = readdir($dir)){
   if(($file != ".") && ($file != "..")){
    $cnt++;
   }
  }
  return true;
 }
 else{
  return false;
 }
}


コール側は、
$result = DirAllDelete($del_dir, 'on');
です。

PHPコーディング

正規表現による入力チェック(メールアドレス)

2008年08月20日
メールアドレスが正しく入力されているかチェックするには、

ereg("^[^@]+@[^.]+\..+",$mail_address);

とします。もっと厳しくチェックする方法もあると思いますが、取り合えずメールの形式かチェックする方法です。

PHPコーディング

正規表現による入力チェック(アルファベット・数字)

2008年08月19日
PHPで文字列のチェックをするには、正規表現でチェックする方法があります。

ereg("[a-zA-Z0-9]", $var);

とします。

$varの1文字目に、a~z、A~Z、0~9以外の文字が使われていれば、falseが返されます。
a~zのように続いた文字列を指定する場合、[a-z]とできます。


これに、【_】も用いてよい場合、

ereg("[a-zA-Z0-9_]", $var);

とします。

PHPコーディング

ディレクトリの下の各ファイルの操作

2008年08月15日
PHPでの、任意のディレクトリ($open_dir)の下にあるファイルの操作です。

ディレクトリをオープンして、各ファイルへアクセスします。


$dir = @opendir($open_dir);
if($dir == true){
while($file = readdir($dir)){
  if(($file != ".") && ($file != "..")){
    ファイル操作
      ・
      ・
      ・
  }
}


【.】と【..】は自分自身と、一つ上のディレクトリなので、除外しています。

PHPコーディング

ファイルのタイムスタンプ書き換え

2008年08月14日
Webサーバー上のファイルのタイムスタンプを書き換える方法です。

PHPではtouch関数を使います。

touch(ファイルのパス);
で現在時刻が、更新日時、最終アクセス日時にセットされます。

日時を指定するときは、
touch(ファイルのパス, 更新日時, 最終アクセス日時);
です。

更新日時、最終アクセス日時は、整数で引き渡します。

PHPコーディング

PHPの四則演算

2008年08月03日
PHPで余りを簡単に計算できる方法はないか調べてみました。簡単な事なのに、今まで使う必要がなかったので、知りませんでした。
【%】を使うと余りが計算できます。


$numを3で割ったときの余り
$amari = ($num) % 3;


もちろん、
足し算は
$tashi = $num1 + $num2;

引き算は
$tashi = $num1 - $num2;

掛け算は
$tashi = $num1 * $num2;

割り算は
$tashi = $num1 / $num2;

当たり前か・・・

PHPコーディング

PHPで画像作成

2008年08月01日
コメントなど投稿する画面で時々見かける画像認証を作ってみようとしてます。
文字の入った画像を表示して、その文字を入力してもらい、正しければその後の処理を行う、というやつです。


画像を作るいい方法はないかと調べてみると、PHPやPerlにはそういう画像を作るモジュールがありました。【GD】です。
サーバーにもインストールされているし、使ってみることにしました。


【imagecreate】という関数を使って、画像を作ります。ブラウザに表示させることもできるし、ファイルとして保存することもできます。

【imagestring】関数で作った画像に文字を入れ、【imageline】関数で斜線を入れます。


ランダムな文字は
$ran_text = preg_replace("/[^[:alnum:]]/", "", base64_encode(hash("sha384", mt_rand(),true)));
で発生させます。


管理画面も作り、色、文字数、文字の大きさも設定できるようにするところまではできたのですが、さて、どうやって組み込むか・・・
独自の画面なら簡単ですが、ブログなどすでにある画面に組み込んで使うのはどうしたらよいか。
自分のブログならできそうですが、他人の場合はちょっと、いやかなり面倒かも・・

PHPコーディング

HTTP 404 エラー を起こしたい

2007年10月31日
ネットビジネス便利ツールのWebサイトですが、URLがなくても、各ページで使っているヘッダや左メニューが表示され、クローラーはページがあると認識していることがわかりました。


ファイルは全てPHPです。各ページに表示する内容は別ファイルに保存してあり、PHPから読み込んで表示します。どのファイルを表示するかはURLの後ろに引数をつけて指定します。

表示しようとするファイルがあるかどうかチェックして、なければエラーを返していたのですが、ちゃんとエラーを利用できていませんでした(つまりバグ)。

そこで、ファイルがない場合、トップページに移動するように書き直したのですが、よく考えると問題が・・・

存在しないはずのページとトップページが同じページとみなされ、SEO上不利になります。思い直し、やはりないものはないとすることにしました。


そこでいろいろ調べたのですが、接続しようとしたURIがなければ、Webサーバーが404エラーを返します。404エラーが返ってくると、ブラウザはあの見慣れた『ページが存在しません』を表示し、クローラーはページがないと認識するのです。

ということは、PHPでファイルの存在を調べ、ない場合はHTTPを通じて404エラーを起こしてやればいいのではないかと考えました。

でもこれは失敗(私の知識がないだけか・・・?)。サーバーからすでにファイルが存在するという返事が返っているのです。おそらくエラーを起こすタイミングが遅いのでしょう。

そこで、苦肉の策。ファイルがない場合は、存在しないURLへジャンプさせればうまくいくのではないのか。
Header("Location: sonzaishinai.php");
で存在しないURLへ飛ばしてみました。

いまのところこちらが意図しているように動いているようです。

PHPコーディング

文字列のエスケープ

2007年03月31日
まだサイトマップメーカーが動かないらしいので、色々調べてみました。ページでエラーが出ていたので、その箇所を見てみると、URLに【'】が入っていて、それが原因のようです。

URLに【'】って入れられるんだぁ、思いつかなかった。それならエスケープをしないといけません。
JavaScriptにURLを引き渡している部分もあります。JavaScriptでもPHPと同じエスケープ方法でOKです。

addslashes($URL) として、URLにある【'】、【"】、【\】、【Null】をエスケープします。
【'】は【\'】とエスケープされます。

そして、formでpostされた値は、PHPの設定によって、postされるとき自動的にエスケープされるかどうかが決まります。 magic_quotes_gpcの値がOn(1)ならエスケープされます。get_magic_quotes_gpc()でmagic_quotes_gpcの設定を確認できます。

magic_quotes_gpcがOn(1)なら、エスケープされた文字列を元に戻さなければいけません。stripslashes($URL)で元に戻ります。

これでエラーもなくなり、私のブラウザからは何の問題もなく動いています。

PHPコーディング

formのpost方式

2007年03月30日
サイトマップメーカーが動かないというバグですが、問題がわかりました。formに入力された値をpostしているのですが、PHP5以降では完全にオブジェクトで引き渡すのだそうです。
<form method="post" action="***.html">
<input type="text" name="atai" value="val">
</form>
とすると、テキストボックスに入力された値は、
$_POST['atai'] = val
でpostされるのです。

前にいた会社でも、PHP4、自分のサーバーもPHP4、オブジェクトで受けているコードを見たことがなかったので、気がつきませんでした。いやぁ勉強になります。

で、ここを直してみたのですが、まだ動かないとの問い合わせがきました。私がアクセスするときちんと動いています。こうなると後はブラウザかなと思います。

ブラウザのチェックも全てはできないので、IEに限定してテストしています。IEもバージョンが違うと動かないかもしれません。環境は色々あり、なかなか難しいです。