HTTP_Client 1.1 の不具合?

HTTP_Client が 1.1 になって get/post 時に <meta http-equiv=’Refresh’> のリダイレクトをたどってくれるようになりました。

(ChangeLogより抜粋)
* HTTP_Client now analyzes the response body for <meta http-equiv=”Refresh”>
tags and follows the redirects defined in them (request #5734)

 
便利になっていいのですが、このアップデート以降 “Fatal Error: Too many redirects” というエラーが出るようになりました。_maxRedirects の値に達するまで延々と同じURLをアクセスし続けて、その結果発生しているようです。
 
別の環境での再現を確認していないのですが、この現象は <meta http-equiv=”Refresh” contents=”…;url=/path/to/nextPage”> を利用しているサイトで post() でリクエストすると発生するようです(post($url.$query, array()) も不可)。全く同じコードでget() に置き換えると問題なく動作しました。

HTTP POST,GETパラメータを作成/解析する

http_build_query() で連想配列/配列から HTTP の GET/POST クエリ文字列を生成、parse_str() でHTTP の GET/POST クエリ文字列を解析(パース/パーズ)して連想配列にすることができます。
 
※GET/POSTクエリ文字列というのは key1=value1&key2=value2&… という形式の文字列を指します。
 
string http_build_query(array formdata [, string numeric_prefix])
void parse_str(string str [, array &arr])

基数変換

10進数を36進数(0 から Z まで36文字)に変換するという処理を書こうとしたら、ばっちりそのものが提供されてるんですね。
 
base_convert()
 

base_convert(“元にする値文字列”, $from_base, $to_base);

これを利用すると次のようになります。

echo base_convert(123, 10, 36); // 10進数123を 36進数にする
// 結果: 3f
echo base_convert(36, 10, 36);
// 結果: 10
echo base_convert(“10”, 36, 10); // 36進数 10 を 10進数にする
// 結果: 36
echo base_convert(“FF”, 16, 2); // 16進数 FF を 2 進数にする
// 結果: 100100

SERVER_NAME vs HTTP_HOST

$_SERVER[‘SERVER_NAME’] と $_SERVER[‘HTTP_HOST’] どっちを使うべきか。
 
SERVER_NAME Versus HTTP_HOST
 
結論から
– HTTP_HOST
単純に HTTP リクエストヘッダの host: そのままが格納される。省略されると空文字列になる。
– SERVER_NAME
HTTP リクエストヘッダで host: が指定されていれば、

htmlentities($_SERVER[‘HTTP_HOST’])

と等価になる。
host ヘッダが省略された場合はサーバデフォルト(Apache なら httpd.conf の ServerName)と等価になる。
 
とのことです。いずれにせよ、信頼できる値ではありませんね。
 
参考:
Chris Shiflett: SERVER_NAME Versus HTTP_HOST(blog.xole.net)

PHP で SHA-256 を使う

sha256,sha256_file関数を簡易実装している方がいました。
 
mhash()を利用すれば、さまざまなダイジェストが生成できるようです(ただし利用するには Mhash ライブラリを有効にする必要があります)。

$str = “あいうえお”;
$raw_hash = mhash(MHASH_SHA256, $str);
echo bin2hex($raw_hash);

 
もう一つの方法としてハッシュ拡張モジュールを利用する事もできます。

メールをパースするクラス

メールをハンドリングする上で、メールソースを解析するという処理を実装しようとしてクラスがないか探していて
Google で mail, email, parse, php, rfc822 とか検索していたのですがライセンス制限のないものが全然見付かりませんでした。
で、ふと PEAR を追ってみるとバッチリありました。
Mail_Mime でメールのエンコード、Mail_mimeDecode でメールのデコードが可能です。
 
Mail パッケージの Mail_mimeDecode を使うと次のようになります。

require_once(‘Mail/mimeDecode.php’);
$mail = ”;
// …
// ($mail にメールのソースを格納する処理)
// …
 
$params = array();
$params[‘include_bodies’] = true;
$params[‘decode_bodies’] = true;
$params[‘decode_headers’] = true;

$decoder = new Mail_mimeDecode($mail);
$structure = $decoder->decode($params);
var_dump($structure);

結果(一部省略. 文字コードは JIS):

object(stdClass)(5) {
  [“headers”]=>
  array(14) {
    [“from sender@example.org tue may 9 20”]=>
    string(10) “21:03 2006”
    [“return-path”]=>
    string(23) “<sender@example.org>”
    [“x-original-to”]=>
    string(14) “foo@example.com”
    [“delivered-to”]=>
    string(14) “foo@example.com”
    [“received”]=>
    array(3) { /* ..snip.. */ }
    [“date”]=>
    string(31) “Tue, 09 May 2006 20:20:59 +0900”
    [“from”]=>
    string(35) “”John” <sender@example.org>”
    [“to”]=>
    string(14) “foo@example.com”
    [“message-id”]=>
    string(45) “<114717365920816000001f81@example.org>“
    [“subject”]=>
    string(19) “テスト”
    [“x-mailer”]=>
    string(17) “Cybozu Office 6.5”
    [“mime-version”]=>
    string(3) “1.0”
    [“content-type”]=>
    string(31) “text/plain; charset=iso-2022-jp”
    [“content-transfer-encoding”]=>
    string(4) “7bit”
  }
  [“ctype_primary”]=>
  string(4) “text”
  [“ctype_secondary”]=>
  string(5) “plain”
  [“ctype_parameters”]=>
  array(1) {
    [“charset”]=>
    string(11) “iso-2022-jp”
  }
  [“body”]=>
  string(495) “Hello, world!!

}

 
文字コードについては一切感知していないのか mb_internal_encoding の値にかかわらず, charset で指定されている通り JIS (iso-2022-jp) でした。
 
From: や To: の解析には Mail_RFC822::parseAddressList() という便利なメソッドも存在します。

require_once ‘Mail/RFC822.php’;
// $structure は Mail_mimeDecode::decode() の結果
$from = Mail_RFC822::parseAddressList($structure->headers[‘from’]);
echo $from[0]->mailbox.’@’.$from[0]->host;
// 結果: sender@example.org

 
参考:
Mail_mimeDecode – メールデコードの例(pear.php.net)

.htaccess で php.ini の設定を上書きする

php_value (mod_php が提供するディレクティブ) を使う事で、 .htaccess で php.ini での設定を上書きすることが可能です。
これにより、Web サービス単位で php.ini でできる設定を使い分けるということが可能になります。

php_value output_handler mb_output_handler
php_value mbstring.language Japanese
php_value mbstring.internal_encoding SJIS
php_value mbstring.http_input auto
php_value mbstring.http_output SJIS
php_flag mbstring.encoding_translation On

ただし、mb_output_handler を利用する場合は、php.ini で指定してしまうと、.htaccess での設定を中途半端に反映するため文字化けする可能性があるため、php.ini の output_handler はデフォルト値のままにしておくほうがいいでしょう(この説明について間違っていれば指摘お願いします)。

output_handler =

 
参考:
【 ほでなすPHP 】 PHPのインストール -> 「.htaccess」ファイルでの設定
Re: .htaccessにてphp_value が設定できないパラメータがある(PHP-Users)
設定の際の注意。E_* (E_ALL など)を利用する際は定数ではなく対応する定数の値(2047など)で指定しましょう。

Smarty + overLIB で簡単にポップアップ

overLIB は簡単に見栄えのいいポップアップ表示を行うためのフリーの JavaScript ライブラリです。overLIB は PHP/Smarty なしでも勿論動作します。
overLIB 単体を利用する場合は次のようにします(公式マニュアルより抜粋)。

<!– 必須の初期化処理(ひとつのページにつき一度だけ必要) –>
<div id=”overDiv” style=”position:absolute; visibility:hidden; z-index:1000;”></div>
<script language=”JavaScript” src=”overlib.js”><!– overLIB (c) Erik Bosrup –></script>
<!– マウスオーバー時に ポップアップを表示する –>
 <a href=”javascript:void(0);” onmouseover=”return overlib(‘This is an ordinary
  popup.’);” onmouseout=”return nd();”>here</a>

Smarty のテンプレートでは popup_initpopup というカスタム関数を使えば、さらに簡単に overLIB を利用する事が可能です(Smarty の公式マニュアルより)。

{* popup_initは popup を使う前に、<BODY>から</BODY>の間で一度だけ呼び出す必要があります(訳注: この文章は原文とは異なります 😉 *}
{popup_init src=”/javascripts/overlib.js”}

{* マウスオーバー時にポップアップウィンドウを表示するリンクを作成する *}
<a href=”mypage.html” {popup text=”This link takes you to my page!”}>mypage</a>

{* popup の text ではリンク等の HTML 文も利用可能です *}
<a href=”mypage.html” {popup sticky=true caption=”mypage contents”
text=”<UL><LI>links<LI>pages<LI>images</UL>” snapx=10 snapy=10}>mypage</a>

参考:
overLIB 公式
Smarty 公式
Smarty 2.6.6 日本語マニュアル

tips – 汎用インクルード用ファイルでの include, require では dirname(__FILE__) を使う

他所様のコードを見ていると、

require ‘../include/conf.php’;

などというように(./ や ../ を含む)相対パス指定しているのを見掛けるのですが、これには落とし穴があります。
たとえば上のコードが書いてあるファイルが script/foo.php だとします。
別のディレクトリから foo.php を include したいと思い、
public_html/main.php で

> require ‘../script/foo.php’;

とすると、

Fatal error:main(../include/conf.php)[function.main]: failed to open sream: No such file or directory at …

のようなエラーが出て正常に動作しない、という結果になります。
 
これは PHP では

PHP5 で PEAR の内部で Non-Static method エラーが出る

PHP5 で set_error_handler() を利用していると、PEAR のライブラリの各所(PEAR::isError, DB::connect など)で E_STRICT(=2048) が発生します。
set_error_handler の呼び出しを次のように変更すると回避可能です。

変更前:
    set_error_handler(‘handler’);
変更後:
 define(‘PHP5’, version_compare( phpversion(), “5.0.0”, “>=” ));
 if(PHP5){
     $old_error_handler = set_error_handler(‘handler’, E_ALL);
 }else{
     $old_error_handler = set_error_handler(‘handler’);
 }

バージョンに応じて第二引数を与えているのがミソです。もちろん、PHP5だけに対応するのであれば、単純に既存のものに第二引数を与えればいいだけです。
 
PHP5 (5.0.5 で確認)では static メソッドの認識が微妙ですね。
 
参考:
#1491(Make code work on both PHP4 and PHP5) (LiveSupport – Trac)