MODX EvolutionのDittoCalの文字化け対策

MODX Evolution 1.0.7Jを構築して、カレンダー表示させたかったので、DittoCalをダウンロードしてinstallationの通りにスニペットを作成したが、UTF-8で構築しているのにも関わらず年、月の文字列が文字化けを起こしてしまう。http://forum.modx.jp/viewtopic.php?f=7&t=283の解決策のようにするこで解決することも可能だが、いまいち納得できずにいたのでソースをみたところ、以下の点がわかった。

  • PHPマニュアルによるとhtmlentitiesは文字コードの指定がないとPHP5.3以下ではISO-8859-1として解釈する(動作環境ではPHP5.2)。そのためUTF-8文字列が変換されてしまうようだ(PHP5.4以降はUTF-8になるとのこと)。
  • 曜日の文字列をdayNameLengthで切りつめるがその際substr()を使用している。マルチバイトの場合はmb_substr()を使用するべきで、かつ文字コードの指定は内部文字エンコーディングに依存する。

よって、htmlentitiesにUTF-8を指定すること、substr()をmb_substr()に変更して文字コードもハードコーディングしてみたところうまくいった。

--- snippet.DittoCal.php.org	2007-11-02 18:51:46.000000000 +0900
+++ snippet.DittoCal.php	2012-12-06 15:09:58.000000000 +0900
@@ -319,7 +319,7 @@
 
 	list($month, $year, $month_name, $weekday) = explode(',',gmstrftime('%m,%Y,%B,%w',$first_of_month));
 	$weekday = ($weekday + 7 - $first_day) % 7; #adjust for $first_day
-	$title   = htmlentities(ucfirst($month_name)).' '.$year;  #note that some locales don't capitalize month and day names
+	$title   = htmlentities(ucfirst($month_name), ENT_COMPAT | ENT_HTML401, 'UTF-8').' / '.$year;  #note that some locales don't capitalize month and day names
 
 	#Begin calendar. Uses a real <caption>. See http://diveintomark.org/archives/2002/07/03
 	@list($p, $pl) = each($pn); @list($n, $nl) = each($pn); #previous and next links, if applicable
@@ -331,7 +331,7 @@
 	if($day_name_length){ #if the day names should be shown ($day_name_length > 0)
 		#if day_name_length is >3, the full name of the day will be printed
 		foreach($day_names as $d)
-			$calendar .= '<th abbr="'.htmlentities($d).'">'.htmlentities($day_name_length < 4 ? substr($d,0,$day_name_length) : $d).'</th>';
+			$calendar .= '<th abbr="'.htmlentities($d, ENT_COMPAT | ENT_HTML401, 'UTF-8').'">'.htmlentities($day_name_length < 4 ? mb_substr($d,0,$day_name_length, 'UTF-8') : $d, ENT_COMPAT | ENT_HTML401, 'UTF-8').'</th>';
 		$calendar .= "</tr>\n<tr>";
 	}

以下のようにコンテンツに指定しても大丈夫だ。

[!DittoCal?&calSource=`16`&setLocal=`ja_JP.UTF-8`&futureDate=`1`&dayNameLength=`1`!]