[PHP]tidy_set_encoding完全解説|Tidyの文字エンコーディングを設定して日本語HTMLを正しく処理する方法

PHP

1. 関数概要

tidy_set_encoding は、PHP の Tidy 拡張が HTML を解析・出力する際の文字エンコーディングを設定する関数です。tidy_parse_string() の第3引数でもエンコーディングを指定できますが、tidy_set_encoding() は既存の tidy オブジェクトに対して後から変更を加えたい場合に使います。日本語などのマルチバイト文字を含む HTML を正しく処理するために欠かせない関数です。

項目内容
関数名tidy_set_encoding
所属拡張Tidy
戻り値の型bool
手続き型 / OOP手続き型(OOP 版: $tidy->setEncoding(string $encoding)
PHP バージョンPHP 5 以降
公式ドキュメントhttps://www.php.net/manual/ja/tidy.setencoding.php

2. 構文

// 手続き型
tidy_set_encoding(tidy $tidy, string $encoding): bool

// オブジェクト指向型
$tidy->setEncoding(string $encoding): bool

パラメータ

パラメータ説明
$tidytidyエンコーディングを設定したい tidy オブジェクト
$encodingstring設定するエンコーディング名(Tidy 固有の表記)

戻り値

結果戻り値
成功(有効なエンコーディング名)true
失敗(無効なエンコーディング名)false

3. Tidy が対応するエンコーディング名

tidy_set_encoding() に渡す文字列は Tidy 固有の表記を使います。PHP の mb_ 系や iconv とは異なる点に注意してください。

Tidy エンコーディング名対応する文字コード
'ascii'US-ASCII(デフォルト)
'latin0'ISO-8859-1(Latin-0)
'latin1'ISO-8859-1(Latin-1)
'utf8'UTF-8 ⭐ 日本語処理に推奨
'iso2022'ISO-2022-JP(JIS)
'mac'Mac Roman
'win1252'Windows-1252
'ibm858'IBM 858
'utf16'UTF-16
'utf16le'UTF-16 LE
'utf16be'UTF-16 BE
'big5'Big5(繁体字中国語)
'shiftjis'Shift_JIS

⚠️ 'UTF-8' 'UTF8' 'Shift-JIS' のような大文字・ハイフン混じりの表記は受け付けない場合があります。小文字・ハイフンなしの Tidy 固有名を使ってください。


4. 動作概念図

┌──────────────────────────────────────────────────────────┐
│  tidy オブジェクト(既存)                                │
│  現在の char-encoding: 'ascii'(デフォルト)              │
└─────────────────────┬────────────────────────────────────┘
                      │ tidy_set_encoding($tidy, 'utf8')
                      ▼
┌──────────────────────────────────────────────────────────┐
│  エンコーディング設定を更新                               │
│  char-encoding: 'utf8'                                    │
│  input-encoding: 'utf8'                                   │
│  output-encoding: 'utf8'                                  │
└─────────────────────┬────────────────────────────────────┘
                      │
          ┌───────────┴───────────┐
   有効名 │                       │ 無効名
          ▼                       ▼
       true                     false

  以後の tidy_parse_string() / tidy_clean_repair() /
  tidy_get_output() が utf8 ベースで動作する

5. 基本的な使い方

<?php
$html = '<html><body><p>日本語テキスト:UTF-8 サンプル</p></body></html>';

// まず tidy オブジェクトを生成
$tidy = tidy_parse_string($html, ['indent' => true], 'utf8');

// エンコーディングを確認・変更
echo "変更前: " . tidy_getopt($tidy, 'char-encoding') . PHP_EOL;

$result = tidy_set_encoding($tidy, 'utf8');
echo "設定結果: " . ($result ? '✅ 成功' : '❌ 失敗') . PHP_EOL;
echo "変更後: " . tidy_getopt($tidy, 'char-encoding') . PHP_EOL;

// 再解析して整形
$tidy = tidy_parse_string($html, ['indent' => true], 'utf8');
tidy_clean_repair($tidy);
echo PHP_EOL . tidy_get_output($tidy);

出力例:

変更前: utf8
設定結果: ✅ 成功
変更後: utf8

<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <p>日本語テキスト:UTF-8 サンプル</p>
</body>
</html>

6. 実践的なコード例

例1: エンコーディングを設定して日本語 HTML を整形するクラス

<?php
class JapaneseHtmlFormatter
{
    private tidy $tidy;

    public function __construct(
        private readonly string $encoding = 'utf8',
        private readonly array  $config   = ['indent' => true, 'wrap' => 80]
    ) {
        $this->tidy = tidy_parse_string('<html><body></body></html>', [], $this->encoding);
        tidy_set_encoding($this->tidy, $this->encoding);
    }

    public function format(string $html): string
    {
        $this->tidy = tidy_parse_string($html, $this->config, $this->encoding);
        tidy_set_encoding($this->tidy, $this->encoding);
        tidy_clean_repair($this->tidy);
        return tidy_get_output($this->tidy);
    }

    public function getEncoding(): string
    {
        return tidy_getopt($this->tidy, 'char-encoding');
    }
}

$formatter = new JapaneseHtmlFormatter('utf8');
$dirty     = '<HTML><BODY><H1>日本語タイトル</H1><P>本文テキスト<br>改行</BODY>';

echo "使用エンコーディング: " . $formatter->getEncoding() . PHP_EOL . PHP_EOL;
echo $formatter->format($dirty);

出力例:

使用エンコーディング: utf8

<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <h1>日本語タイトル</h1>
  <p>本文テキスト<br>
  改行</p>
</body>
</html>

例2: OOP スタイルで setEncoding() を使う

<?php
class TidyOopEncodingSetter
{
    private tidy $tidy;

    public function __construct(string $html, array $config = [])
    {
        $this->tidy = new tidy();
        $this->tidy->parseString($html, $config, 'utf8');
    }

    public function setEncoding(string $encoding): bool
    {
        return $this->tidy->setEncoding($encoding);
    }

    public function getEncoding(): string
    {
        return $this->tidy->getOpt('char-encoding');
    }

    public function process(): string
    {
        $this->tidy->cleanRepair();
        return $this->tidy->value;
    }
}

$setter = new TidyOopEncodingSetter(
    '<html><body><p>OOP エンコーディングテスト</p></body></html>',
    ['indent' => true]
);

echo "設定前: " . $setter->getEncoding() . PHP_EOL;

$ok = $setter->setEncoding('utf8');
echo "setEncoding('utf8'): " . ($ok ? '✅ 成功' : '❌ 失敗') . PHP_EOL;
echo "設定後: " . $setter->getEncoding() . PHP_EOL;

// 無効なエンコーディング名を試す
$ng = $setter->setEncoding('UTF-8');
echo "setEncoding('UTF-8'): " . ($ng ? '✅ 成功' : '❌ 失敗(無効な表記)') . PHP_EOL;

echo PHP_EOL . $setter->process();

出力例:

設定前: utf8
setEncoding('utf8'): ✅ 成功
設定後: utf8
setEncoding('UTF-8'): ❌ 失敗(無効な表記)

<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <p>OOP エンコーディングテスト</p>
</body>
</html>

例3: 複数のエンコーディングを検証するクラス

<?php
class TidyEncodingValidator
{
    /** @var string[] */
    private array $validEncodings = [
        'ascii', 'latin0', 'latin1', 'utf8',
        'iso2022', 'mac', 'win1252', 'ibm858',
        'utf16', 'utf16le', 'utf16be', 'big5', 'shiftjis',
    ];

    /** @var string[] */
    private array $invalidCandidates = [
        'UTF-8', 'UTF8', 'utf-8', 'Shift-JIS', 'ISO-8859-1',
        'euc-jp', 'eucjp', 'gb2312',
    ];

    public function validateAll(): void
    {
        $tidy = tidy_parse_string('<html><body></body></html>', [], 'utf8');

        echo "=== 有効なエンコーディング ===" . PHP_EOL;
        foreach ($this->validEncodings as $enc) {
            $ok   = tidy_set_encoding($tidy, $enc);
            $icon = $ok ? '✅' : '❌';
            printf("  %s %-12s%s", $icon, $enc, PHP_EOL);
        }

        echo PHP_EOL . "=== 無効な(よくある誤記)===" . PHP_EOL;
        foreach ($this->invalidCandidates as $enc) {
            $ok   = tidy_set_encoding($tidy, $enc);
            $icon = $ok ? '✅' : '❌';
            printf("  %s %-15s%s", $icon, $enc, PHP_EOL);
        }
    }
}

$validator = new TidyEncodingValidator();
$validator->validateAll();

出力例:

=== 有効なエンコーディング ===
  ✅ ascii
  ✅ latin0
  ✅ latin1
  ✅ utf8
  ✅ iso2022
  ✅ mac
  ✅ win1252
  ✅ ibm858
  ✅ utf16
  ✅ utf16le
  ✅ utf16be
  ✅ big5
  ✅ shiftjis

=== 無効な(よくある誤記)===
  ❌ UTF-8
  ❌ UTF8
  ❌ utf-8
  ❌ Shift-JIS
  ❌ ISO-8859-1
  ❌ euc-jp
  ❌ eucjp
  ❌ gb2312

例4: エンコーディング別に出力を比較するクラス

<?php
class TidyEncodingComparator
{
    private string $html;

    public function __construct(string $html)
    {
        $this->html = $html;
    }

    public function compare(array $encodings): void
    {
        echo "=== エンコーディング別出力比較 ===" . PHP_EOL;

        foreach ($encodings as $enc) {
            $tidy = tidy_parse_string($this->html, ['indent' => true], $enc);
            $ok   = tidy_set_encoding($tidy, $enc);

            if (!$ok) {
                printf("  ❌ %-10s: 無効なエンコーディング名%s", $enc, PHP_EOL);
                continue;
            }

            tidy_clean_repair($tidy);
            $output  = tidy_get_output($tidy);
            $current = tidy_getopt($tidy, 'char-encoding');

            printf(
                "  ✅ %-10s (char-encoding=%s): %d bytes%s",
                $enc,
                $current,
                strlen($output),
                PHP_EOL
            );
        }
    }
}

$html       = '<html><body><p>日本語:比較テスト</p></body></html>';
$comparator = new TidyEncodingComparator($html);
$comparator->compare(['utf8', 'ascii', 'shiftjis', 'iso2022', 'UTF-8']);

出力例:

=== エンコーディング別出力比較 ===
  ✅ utf8       (char-encoding=utf8): 196 bytes
  ✅ ascii      (char-encoding=ascii): 212 bytes
  ✅ shiftjis   (char-encoding=shiftjis): 198 bytes
  ✅ iso2022    (char-encoding=iso2022): 203 bytes
  ❌ UTF-8     : 無効なエンコーディング名

例5: エンコーディングを安全に設定するガードクラス

<?php
class TidyEncodingGuard
{
    /** @var string[] */
    private const VALID_ENCODINGS = [
        'ascii', 'latin0', 'latin1', 'utf8', 'iso2022',
        'mac', 'win1252', 'ibm858', 'utf16', 'utf16le',
        'utf16be', 'big5', 'shiftjis',
    ];

    /** @var array<string, string> エイリアス → 正規名マッピング */
    private const ALIASES = [
        'utf-8'     => 'utf8',
        'UTF-8'     => 'utf8',
        'UTF8'      => 'utf8',
        'Shift-JIS' => 'shiftjis',
        'shift-jis' => 'shiftjis',
        'ISO-8859-1'=> 'latin1',
        'iso-8859-1'=> 'latin1',
    ];

    public static function normalize(string $encoding): string
    {
        return self::ALIASES[$encoding] ?? strtolower($encoding);
    }

    public static function isValid(string $encoding): bool
    {
        return in_array(self::normalize($encoding), self::VALID_ENCODINGS, true);
    }

    public static function set(tidy $tidy, string $encoding): bool
    {
        $normalized = self::normalize($encoding);

        if (!in_array($normalized, self::VALID_ENCODINGS, true)) {
            throw new \InvalidArgumentException(
                "無効なエンコーディング名: '{$encoding}'" .
                " (正規化後: '{$normalized}')" .
                " 有効な値: " . implode(', ', self::VALID_ENCODINGS)
            );
        }

        return tidy_set_encoding($tidy, $normalized);
    }
}

$tidy = tidy_parse_string('<html><body><p>ガードテスト</p></body></html>', [], 'utf8');

// エイリアスで渡しても自動変換
$aliases = ['UTF-8', 'Shift-JIS', 'ISO-8859-1'];
foreach ($aliases as $alias) {
    $normalized = TidyEncodingGuard::normalize($alias);
    $ok         = TidyEncodingGuard::set($tidy, $alias);
    printf("  '%s' → 正規化: '%s' → %s%s", $alias, $normalized, $ok ? '✅ 成功' : '❌ 失敗', PHP_EOL);
}

// 無効名は例外
try {
    TidyEncodingGuard::set($tidy, 'euc-jp');
} catch (\InvalidArgumentException $e) {
    echo PHP_EOL . "❌ 例外: " . $e->getMessage() . PHP_EOL;
}

出力例:

  'UTF-8' → 正規化: 'utf8' → ✅ 成功
  'Shift-JIS' → 正規化: 'shiftjis' → ✅ 成功
  'ISO-8859-1' → 正規化: 'latin1' → ✅ 成功

❌ 例外: 無効なエンコーディング名: 'euc-jp' (正規化後: 'euc-jp') 有効な値: ascii, latin0, ...

例6: Shift_JIS ファイルを UTF-8 に変換して整形するクラス

<?php
class ShiftJisToUtf8Converter
{
    public function convert(string $sjisHtml): string|false
    {
        // PHP レベルで UTF-8 に変換
        $utf8Html = mb_convert_encoding($sjisHtml, 'UTF-8', 'Shift-JIS');

        $tidy = tidy_parse_string($utf8Html, ['indent' => true, 'wrap' => 80], 'utf8');
        $ok   = tidy_set_encoding($tidy, 'utf8');

        if (!$ok) {
            return false;
        }

        tidy_clean_repair($tidy);
        return tidy_get_output($tidy);
    }
}

// Shift_JIS 文字列をシミュレート(実際は Shift_JIS バイト列)
$sjisHtml = mb_convert_encoding(
    '<html><body><p>Shift_JIS の日本語テキスト</p></body></html>',
    'Shift-JIS',
    'UTF-8'
);

$converter = new ShiftJisToUtf8Converter();
$result    = $converter->convert($sjisHtml);

echo $result !== false
    ? "✅ 変換成功:" . PHP_EOL . $result
    : "❌ 変換失敗" . PHP_EOL;

出力例:

✅ 変換成功:
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <p>Shift_JIS の日本語テキスト</p>
</body>
</html>

例7: エンコーディング設定を含む統合レポートクラス

<?php
class TidyEncodingReport
{
    public function generate(string $html, string $encoding): void
    {
        $normalized = strtolower(str_replace(['-', '_'], '', $encoding));
        $tidy       = tidy_parse_string($html, ['indent' => true], $normalized);
        $setOk      = tidy_set_encoding($tidy, $normalized);

        if (!$setOk) {
            echo "❌ エンコーディング '{$encoding}' の設定に失敗しました。" . PHP_EOL;
            return;
        }

        tidy_clean_repair($tidy);

        $output = tidy_get_output($tidy);
        $status = tidy_get_status($tidy);

        echo "=== Tidy エンコーディングレポート ===" . PHP_EOL;
        echo "指定エンコーディング  : {$encoding}"                      . PHP_EOL;
        echo "char-encoding         : " . tidy_getopt($tidy, 'char-encoding')   . PHP_EOL;
        echo "input-encoding        : " . tidy_getopt($tidy, 'input-encoding')  . PHP_EOL;
        echo "output-encoding       : " . tidy_getopt($tidy, 'output-encoding') . PHP_EOL;
        echo "解析ステータス        : " . match($status) {
            0 => '✅ 正常', 1 => '⚠️  警告', 2 => '❌ エラー', default => '不明'
        }                                                                         . PHP_EOL;
        echo "出力バイト数          : " . strlen($output) . " bytes"             . PHP_EOL;
        echo "libtidy               : " . tidy_get_release()                     . PHP_EOL;
        echo PHP_EOL . "--- 整形済み出力 ---" . PHP_EOL;
        echo $output;
    }
}

$reporter = new TidyEncodingReport();
$reporter->generate(
    '<html><body><p>統合レポートテスト:日本語</p></body></html>',
    'utf8'
);

出力例:

=== Tidy エンコーディングレポート ===
指定エンコーディング  : utf8
char-encoding         : utf8
input-encoding        : utf8
output-encoding       : utf8
解析ステータス        : ✅ 正常
出力バイト数          : 205 bytes
libtidy               : 1st August 2023

--- 整形済み出力 ---
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <p>統合レポートテスト:日本語</p>
</body>
</html>

7. 関連関数との比較

関数名用途戻り値
tidy_set_encoding()tidy オブジェクトのエンコーディングを後から設定bool
tidy_parse_string() の第3引数解析時にエンコーディングを同時指定
tidy_parse_file() の第3引数ファイル解析時にエンコーディングを同時指定
tidy_getopt($t, 'char-encoding')現在の char-encoding 設定値を取得mixed
tidy_setopt($t, 'char-encoding', $v)char-encoding を個別オプションとして設定bool
tidy_reset_config()全オプション(エンコーディング含む)をリセットbool

使い分けのポイント: 解析と同時にエンコーディングを決めるなら tidy_parse_string() の第3引数、既存オブジェクトへの後付け変更なら tidy_set_encoding() を使います。


8. よくある落とし穴と注意点

① エンコーディング名は Tidy 固有の小文字表記を使う

tidy_set_encoding() に渡す文字列は PHP の mb_ 系とは別の独自表記です。'UTF-8''Shift-JIS' は認識されず false が返ります。

tidy_set_encoding($tidy, 'utf8');     // ✅ 正しい
tidy_set_encoding($tidy, 'UTF-8');    // ❌ false が返る
tidy_set_encoding($tidy, 'shiftjis'); // ✅ 正しい
tidy_set_encoding($tidy, 'Shift-JIS');// ❌ false が返る

② tidy_set_encoding() 後も再解析が必要

tidy_set_encoding() はオプション値を変更するだけです。変更を出力に反映するには tidy_parse_string() を再実行してください。

// NG: set_encoding だけでは出力は変わらない
$tidy = tidy_parse_string($html, [], 'ascii');
tidy_set_encoding($tidy, 'utf8');
echo tidy_get_output($tidy); // ascii ベースの出力のまま

// OK: 再解析する
tidy_set_encoding($tidy, 'utf8');
$tidy = tidy_parse_string($html, [], 'utf8');
tidy_clean_repair($tidy);
echo tidy_get_output($tidy); // utf8 ベースの出力

③ char-encoding / input-encoding / output-encoding の関係

tidy_set_encoding()char-encoding を変更し、それに連動して input-encodingoutput-encoding も更新されます。個別に制御したい場合は tidy_setopt() で各オプションを直接設定してください。

④ 日本語処理では必ず 'utf8' を指定する

デフォルトの 'ascii' のまま日本語 HTML を処理すると、マルチバイト文字が数値文字参照(&#12395; 等)に変換されたり、文字化けが起きたりします。

// NG: デフォルト ascii → 日本語が壊れる
$tidy = tidy_parse_string($japaneseHtml, ['indent' => true]);

// OK: utf8 を明示
$tidy = tidy_parse_string($japaneseHtml, ['indent' => true], 'utf8');
tidy_set_encoding($tidy, 'utf8');

9. まとめ

項目内容
主な用途既存の tidy オブジェクトのエンコーディングを後から変更する
戻り値true(成功)/ false(無効なエンコーディング名)
OOP 版$tidy->setEncoding(string $encoding)
日本語推奨設定'utf8'(小文字・ハイフンなし)
よくある誤記'UTF-8' 'UTF8' 'Shift-JIS' → いずれも false
再解析の要否変更を出力に反映するには tidy_parse_string() の再実行が必要
よく使う組み合わせtidy_parse_string(), tidy_getopt(), tidy_setopt(), tidy_clean_repair()

tidy_set_encoding() は日本語など ASCII 以外の文字を含む HTML を Tidy で正しく処理するための重要な関数です。'utf8' などの Tidy 固有の表記を使い、tidy_parse_string() の第3引数と組み合わせることでエンコーディング関連のトラブルを確実に防げます。

タイトルとURLをコピーしました