PHPで正しいUTF-8バイト列を得る方法とXML/XHTMLその他

How to validate UTF-8 bytes in PHP

PHPにバンドルされている mbstrings 拡張を使う方法です.以下のスニペットは著作権の構成要件に該当しない程度のものなので,ご自由に利用ください.

\mb_internal_encoding ('UTF-8');
\mb_substitute_character (0xfffd); // U+FFFD

function validate_utf8 (string $text): string
{
	if (\mb_check_encoding ($text)) {
		return $text;
	}
	
	$replaced = \mb_convert_encoding ($text, 'UTF-8', 'UTF-8');
	if (\mb_check_encoding ($replaced)) {
		return $replaced;
	}
	
	return "\xef\xbf\xbd"; // U+FFFD
}

これによって文字列が正しいUTF-8表現であることを保証できます.

次に紹介するのは正しいXML (well-formed になるような) 文字列を得る方法各種です.

How to get valid (UTF-8) characters for XML/XHTML/SVG (in PHP)

$text = 'input text (maybe invalid)';
$escaped = \htmlspecialchars ($text, \ENT_NOQUOTES | \ENT_DISALLOWED | \ENT_XML1, 'UTF-8');
$output = \htmlspecialchars_decode ($escaped, \ENT_NOQUOTES | \ENT_XML1);
// $output now contains no invalid characters in XML 1.0

このコードによりXMLにおいて有効な文字列を取得できます.(See https://www.w3.org/TR/2008/REC-xml-20081126/#NT-Char)

この方法はPHP標準の関数 (PHP 5.4+) しか使いません.

function validate_xml_characters (string $text): string
{
	$escaped = \htmlspecialchars ($text, \ENT_NOQUOTES | \ENT_DISALLOWED | \ENT_XML1, 'UTF-8');
	return \htmlspecialchars_decode ($escaped, \ENT_NOQUOTES | \ENT_XML1);
}

次にに紹介するのがXMLのCDATA sectionの中身のエンコーディングの仕方です.

How to encode characters for a CDATA section in an XML document (in PHP)

CDATAセクションは上に挙げた文字すべてを含むことができますが,ひとつだけ例外があります.

CDATA sections cannot contain: “]]>”

そうです,”]]>”はCDATAセクションの終端を表すため.含めることができないのです.ただし迂回方法があります.

The workaround below.

Replace “]]>” with “]]]]><![CDATA[>

2つのCDATAセクションに分割してしまえば終端文字列として認識されないので含めることができます.

上の validate_xml_characters 関数を使えば以下のようになります.

$text = 'input text (maybe invalid)';
$validated = validate_xml_characters ($text);
$replaced = \str_replace (']]>', ']]]]><![CDATA[>', $validated);
$cdata_section = '<![CDATA[' . $replaced . ']]>';

いかがでしょうか.

最後に紹介するのは簡単なようで難しい,XML/XHTML/HTMLのコメントです.

How to compose a valid comment section for XML/XHTML/HTML (in PHP)

コメントには実はそのドキュメントにおいて有効な文字列であっても含めることができないものがあります.”–” (HYPHEN-MINUS x2) です.

そこで以下のようなコードを利用します.

$text = 'input text (maybe invalid)';
$validated = validate_xml_characters ($text);

// Use U+FFFD
$replaced = \str_replace ('--', "\xef\xbf\xbd", $validated);
$comment_section = '<!--' . $replaced . '-->';

We replace two continuous hypens with U+FFFD here.