[PHP]tidy_is_xhtml完全解説|解析済みドキュメントがXHTMLかどうかを判定して出力形式を自動切り替えする方法

PHP

1. 関数概要

tidy_is_xhtml は、PHP の Tidy 拡張が解析したドキュメントが XHTML であるかどうかを判定する関数です。DOCTYPE 宣言や名前空間をもとに Tidy ライブラリが内部的に判定した結果を bool で返します。出力形式の自動選択・ドキュメント種別ごとの処理分岐・バリデーションパイプラインの条件制御などに活用できます。

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

2. 構文

// 手続き型
tidy_is_xhtml(tidy $tidy): bool

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

パラメータ

パラメータ説明
$tidytidytidy_parse_string() 等で生成した tidy オブジェクト

戻り値

戻り値意味
trueドキュメントが XHTML であると判定された
falseXHTML ではない(HTML または不明)

3. 動作概念図

┌──────────────────────────────────────────────────────────────┐
│  入力 HTML/XHTML ソース                                       │
│                                                               │
│  パターンA: XHTML                                             │
│  <?xml version="1.0"?>                                        │
│  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"    │
│      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">    │
│  <html xmlns="http://www.w3.org/1999/xhtml">                 │
│                                                               │
│  パターンB: HTML                                              │
│  <!DOCTYPE html>                                              │
│  <html>                                                       │
└───────────────────────┬──────────────────────────────────────┘
                        │ tidy_parse_string()
                        ▼
┌──────────────────────────────────────────────────────────────┐
│  Tidy 解析エンジン                                            │
│  DOCTYPE / 名前空間 / XML 宣言 をもとに種別を判定            │
└───────────────────────┬──────────────────────────────────────┘
                        │ tidy_is_xhtml()
                        ▼
              ┌─────────┴─────────┐
              │                   │
          true (XHTML)       false (HTML等)

4. 基本的な使い方

<?php
// XHTML ドキュメント
$xhtml = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>XHTML サンプル</title></head>
<body><p>本文</p></body>
</html>';

$tidy = tidy_parse_string($xhtml, [], 'UTF8');

if (tidy_is_xhtml($tidy)) {
    echo "✅ このドキュメントは XHTML です。" . PHP_EOL;
} else {
    echo "ℹ️  このドキュメントは XHTML ではありません。" . PHP_EOL;
}

出力例:

✅ このドキュメントは XHTML です。

5. 実践的なコード例

例1: ドキュメント種別を判定して表示するクラス

<?php
class HtmlDocTypeDetector
{
    private tidy $tidy;

    public function __construct(string $source)
    {
        $this->tidy = tidy_parse_string($source, [], 'UTF8');
    }

    public function isXhtml(): bool
    {
        return tidy_is_xhtml($this->tidy);
    }

    public function getLabel(): string
    {
        return $this->isXhtml() ? 'XHTML' : 'HTML / その他';
    }

    public function report(): void
    {
        echo "ドキュメント種別 : " . $this->getLabel()                              . PHP_EOL;
        echo "XHTML 判定       : " . ($this->isXhtml() ? 'true' : 'false')         . PHP_EOL;
        echo "HTML バージョン  : " . tidy_get_html_ver($this->tidy)                 . PHP_EOL;
        echo "解析ステータス   : " . tidy_get_status($this->tidy)                   . PHP_EOL;
    }
}

$xhtml = '<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>テスト</title></head>
<body><p>段落</p></body>
</html>';

$detector = new HtmlDocTypeDetector($xhtml);
$detector->report();

出力例:

ドキュメント種別 : XHTML
XHTML 判定       : true
HTML バージョン  : 0
解析ステータス   : 0

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

<?php
class TidyOopXhtmlChecker
{
    private tidy $tidy;

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

    public function check(): void
    {
        $result = $this->tidy->isXhtml();
        echo "isXhtml(): " . var_export($result, true) . PHP_EOL;
        echo "判定結果 : " . ($result ? '✅ XHTML' : 'ℹ️  HTML / その他') . PHP_EOL;
    }
}

// HTML5
$checkerA = new TidyOopXhtmlChecker('<!DOCTYPE html><html><body><p>HTML5</p></body></html>');
echo "--- HTML5 ---" . PHP_EOL;
$checkerA->check();

echo PHP_EOL;

// XHTML 1.0 Strict
$xhtml = '<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>XHTML</title></head><body><p>XHTML</p></body></html>';

$checkerB = new TidyOopXhtmlChecker($xhtml);
echo "--- XHTML 1.0 Strict ---" . PHP_EOL;
$checkerB->check();

出力例:

--- HTML5 ---
isXhtml(): false
判定結果 : ℹ️  HTML / その他

--- XHTML 1.0 Strict ---
isXhtml(): true
判定結果 : ✅ XHTML

例3: 複数ドキュメントを一括判定するクラス

<?php
class BulkXhtmlChecker
{
    /** @var array<string, string> */
    private array $documents;

    public function __construct(array $documents)
    {
        $this->documents = $documents;
    }

    public function checkAll(): void
    {
        echo "=== XHTML 一括判定 ===" . PHP_EOL;
        foreach ($this->documents as $name => $source) {
            $tidy   = tidy_parse_string($source, [], 'UTF8');
            $result = tidy_is_xhtml($tidy);
            $icon   = $result ? '✅' : 'ℹ️ ';
            printf(
                "  %s %-25s => %s%s",
                $icon,
                $name,
                $result ? 'XHTML' : 'HTML / その他',
                PHP_EOL
            );
        }
    }
}

$checker = new BulkXhtmlChecker([
    'html4.html'   => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"><html><body><p>HTML4</p></body></html>',
    'html5.html'   => '<!DOCTYPE html><html><body><p>HTML5</p></body></html>',
    'xhtml10.html' => '<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>X</title></head><body><p>XHTML</p></body></html>',
    'noDoctype.html' => '<html><body><p>DOCTYPE なし</p></body></html>',
]);

$checker->checkAll();

出力例:

=== XHTML 一括判定 ===
  ℹ️  html4.html              => HTML / その他
  ℹ️  html5.html              => HTML / その他
  ✅ xhtml10.html             => XHTML
  ℹ️  noDoctype.html          => HTML / その他

例4: XHTML かどうかで出力形式を自動切り替えするクラス

<?php
class AdaptiveHtmlSerializer
{
    public function serialize(string $source): string
    {
        $tidy   = tidy_parse_string($source, [], 'UTF8');
        $isXhtml = tidy_is_xhtml($tidy);

        // XHTML の場合は XHTML 出力、そうでなければ HTML 出力
        $config = $isXhtml
            ? ['output-xhtml' => true,  'indent' => true, 'wrap' => 80]
            : ['output-xhtml' => false, 'indent' => true, 'wrap' => 80];

        $tidyOut = tidy_parse_string($source, $config, 'UTF8');
        tidy_clean_repair($tidyOut);

        echo "入力種別 : " . ($isXhtml ? 'XHTML' : 'HTML') . PHP_EOL;
        echo "出力設定 : output-xhtml=" . var_export($config['output-xhtml'], true) . PHP_EOL . PHP_EOL;

        return tidy_get_output($tidyOut);
    }
}

$serializer = new AdaptiveHtmlSerializer();

$html5 = '<!DOCTYPE html><html><body><br><img src="a.jpg"></body></html>';
echo "=== HTML5 入力 ===" . PHP_EOL;
echo $serializer->serialize($html5) . PHP_EOL;

出力例:

=== HTML5 入力 ===
入力種別 : HTML
出力設定 : output-xhtml=false

<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <br>
  <img src="a.jpg">
</body>
</html>

例5: XHTML 判定結果をログに記録するクラス

<?php
class XhtmlAuditLogger
{
    public function __construct(private readonly string $logFile) {}

    public function audit(string $label, string $source): bool
    {
        $tidy    = tidy_parse_string($source, [], 'UTF8');
        $isXhtml = tidy_is_xhtml($tidy);
        $status  = tidy_get_status($tidy);

        $line = sprintf(
            "[%s] %-20s | isXhtml=%-5s | status=%d | htmlVer=%d\n",
            date('Y-m-d H:i:s'),
            $label,
            $isXhtml ? 'true' : 'false',
            $status,
            tidy_get_html_ver($tidy)
        );

        file_put_contents($this->logFile, $line, FILE_APPEND);
        echo $line;

        return $isXhtml;
    }
}

$logger = new XhtmlAuditLogger('/tmp/xhtml_audit.log');
$logger->audit('page-a', '<!DOCTYPE html><html><body><p>HTML5</p></body></html>');
$logger->audit('page-b', '<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>X</title></head><body><p>X</p></body></html>');

出力例:

[2026-07-09 12:00:00] page-a               | isXhtml=false | status=0 | htmlVer=0
[2026-07-09 12:00:00] page-b               | isXhtml=true  | status=0 | htmlVer=0

例6: output-xhtml オプションと tidy_is_xhtml() の関係を検証するクラス

<?php
/**
 * tidy_is_xhtml() は「入力」ドキュメントの種別を判定する。
 * output-xhtml オプションは「出力」形式を制御する。
 * 両者は独立しているため、混同しないようにこのクラスで明示する。
 */
class XhtmlInputOutputInspector
{
    public function inspect(string $source, array $config): void
    {
        // 入力の判定(output-xhtml オプションの影響を受けない)
        $tidyIn  = tidy_parse_string($source, [], 'UTF8');
        $isXhtml = tidy_is_xhtml($tidyIn);

        // 出力の生成(config の output-xhtml が効く)
        $tidyOut = tidy_parse_string($source, $config, 'UTF8');
        tidy_clean_repair($tidyOut);
        $output      = tidy_get_output($tidyOut);
        $outputXhtml = (bool) tidy_getopt($tidyOut, 'output-xhtml');

        echo "--- 入力ドキュメント ---" . PHP_EOL;
        echo "tidy_is_xhtml() : " . var_export($isXhtml, true) . PHP_EOL;
        echo PHP_EOL;
        echo "--- 出力設定 ---" . PHP_EOL;
        echo "output-xhtml    : " . var_export($outputXhtml, true) . PHP_EOL;
        echo PHP_EOL;
        echo "--- 出力(先頭150文字)---" . PHP_EOL;
        echo substr($output, 0, 150) . PHP_EOL;
    }
}

$inspector = new XhtmlInputOutputInspector();

// HTML5 入力を XHTML 形式で出力する
echo "=== HTML5 入力 → XHTML 出力 ===" . PHP_EOL;
$inspector->inspect(
    '<!DOCTYPE html><html><body><p>テスト</p></body></html>',
    ['output-xhtml' => true, 'indent' => true]
);

出力例:

=== HTML5 入力 → XHTML 出力 ===
--- 入力ドキュメント ---
tidy_is_xhtml() : false   ← 入力は HTML5 なので false

--- 出力設定 ---
output-xhtml    : true    ← オプションで XHTML 出力を指定

--- 出力(先頭150文字)---
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-s

例7: 判定結果を含む統合レポートを生成するクラス

<?php
readonly class DocumentProfile
{
    public function __construct(
        public bool   $isXhtml,
        public int    $htmlVersion,
        public int    $status,
        public string $release,
        public string $errorBuffer,
    ) {}
}

class TidyDocumentProfiler
{
    public function profile(string $source): DocumentProfile
    {
        $tidy = tidy_parse_string($source, [], 'UTF8');
        tidy_clean_repair($tidy);

        return new DocumentProfile(
            isXhtml:     tidy_is_xhtml($tidy),
            htmlVersion: tidy_get_html_ver($tidy),
            status:      tidy_get_status($tidy),
            release:     tidy_get_release(),
            errorBuffer: tidy_get_error_buffer($tidy) ?: '',
        );
    }

    public function printProfile(DocumentProfile $p): void
    {
        echo "=== ドキュメントプロファイル ===" . PHP_EOL;
        echo "XHTML 判定      : " . ($p->isXhtml ? '✅ XHTML' : 'ℹ️  HTML / その他') . PHP_EOL;
        echo "HTML バージョン : " . ($p->htmlVersion ?: 'HTML5 / 不明')              . PHP_EOL;
        echo "ステータス      : " . match($p->status) {
            0 => '✅ 正常', 1 => '⚠️  警告', 2 => '❌ エラー', default => '不明'
        }                                                                              . PHP_EOL;
        echo "libtidy リリース: " . $p->release                                       . PHP_EOL;
        echo "エラーバッファ  : " . ($p->errorBuffer ?: '(なし)')                   . PHP_EOL;
    }
}

$profiler = new TidyDocumentProfiler();

$sources = [
    'HTML5'  => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
    'XHTML'  => '<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>T</title></head><body><p>OK</p></body></html>',
];

foreach ($sources as $label => $source) {
    echo PHP_EOL . "【{$label}】" . PHP_EOL;
    $profiler->printProfile($profiler->profile($source));
}

出力例:

【HTML5】
=== ドキュメントプロファイル ===
XHTML 判定      : ℹ️  HTML / その他
HTML バージョン : HTML5 / 不明
ステータス      : ✅ 正常
libtidy リリース: 1st August 2023
エラーバッファ  : (なし)

【XHTML】
=== ドキュメントプロファイル ===
XHTML 判定      : ✅ XHTML
HTML バージョン : HTML5 / 不明
ステータス      : ✅ 正常
libtidy リリース: 1st August 2023
エラーバッファ  : (なし)

6. 関連関数との比較

関数名用途戻り値
tidy_is_xhtml()入力ドキュメントが XHTML かを判定bool
tidy_is_xml()入力ドキュメントが XML かを判定bool
tidy_get_html_ver()HTML バージョン番号を取得int
tidy_get_status()解析ステータスを取得int
tidy_getopt()output-xhtml 等のオプション現在値を取得mixed
tidy_get_output()整形済み HTML/XHTML を文字列で取得string

混同しやすいポイント: tidy_is_xhtml()入力ドキュメントの種別判定であり、tidy_getopt($tidy, 'output-xhtml')出力形式の設定です。HTML5 入力でも output-xhtml=true を指定すれば XHTML 形式で出力できますが、そのとき tidy_is_xhtml()false のままです。


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

① output-xhtml オプションと混同しない

tidy_is_xhtml()入力の種別を判定します。output-xhtml オプションは出力形式を制御するものであり、まったく別の概念です。

// 入力が HTML5 → tidy_is_xhtml() は false
$tidy = tidy_parse_string('<!DOCTYPE html><html><body></body></html>', ['output-xhtml' => true], 'UTF8');
var_dump(tidy_is_xhtml($tidy));        // bool(false) ← 入力は HTML
var_dump(tidy_getopt($tidy, 'output-xhtml')); // bool(true) ← 出力は XHTML に設定

② DOCTYPE がない場合は false になる

DOCTYPE 宣言のない HTML は XHTML と判定されません。XHTML と判定されるには XHTML 用の DOCTYPE と xmlns 属性が必要です。

// DOCTYPE なし → false
$tidy = tidy_parse_string('<html xmlns="http://www.w3.org/1999/xhtml"><body></body></html>', [], 'UTF8');
var_dump(tidy_is_xhtml($tidy)); // bool(false)

③ tidy_clean_repair() の前後で判定結果が変わる場合がある

修復処理によって DOCTYPE が補完・変更されることがあり、tidy_is_xhtml() の戻り値が変化することがあります。判定タイミングを統一するよう注意してください。

④ HTML5 は常に false になる

HTML5 の <!DOCTYPE html> は XHTML 宣言ではないため、tidy_is_xhtml() は常に false を返します。false だからといってドキュメントが無効というわけではありません。


8. まとめ

項目内容
主な用途解析済みドキュメントが XHTML かどうかをプログラムで判定する
戻り値true(XHTML)/ false(HTML またはその他)
OOP 版$tidy->isXhtml()
XHTML と判定される条件XHTML 用 DOCTYPE 宣言 + xmlns 属性の存在
HTML5 の場合常に false(エラーではない)
混同注意output-xhtml オプション(出力形式)とは別物
よく使う組み合わせtidy_is_xml(), tidy_get_html_ver(), tidy_getopt(), tidy_get_status()

tidy_is_xhtml() は「入力ドキュメントの素性を確認する」関数です。output-xhtml オプションによる出力形式の制御とは独立しているため、入力判定と出力制御を切り離して考えることが正確な理解への近道です。ドキュメント種別に応じた処理分岐や、バリデーションパイプラインの入口判定として活用してください。

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