[PHP]tidy_is_xml完全解説|解析済みドキュメントがXMLかどうかを判定して処理を自動分岐する方法

PHP

1. 関数概要

tidy_is_xml は、PHP の Tidy 拡張が解析したドキュメントが XML であるかどうかを判定する関数です。XML 宣言や DOCTYPE をもとに Tidy ライブラリが内部的に判定した結果を bool で返します。HTML・XHTML・XML が混在するシステムでのドキュメント種別の振り分け、バリデーションパイプラインの条件分岐、出力形式の自動選択などに活用できます。

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

2. 構文

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

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

パラメータ

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

戻り値

戻り値意味
trueドキュメントが XML であると判定された
falseXML ではない(HTML・XHTML またはその他)

3. tidy_is_xml / tidy_is_xhtml / HTML の判定比較

入力ドキュメントの種別tidy_is_xml()tidy_is_xhtml()
HTML 4.01falsefalse
HTML5falsefalse
XHTML 1.0 Strictfalsetrue
XHTML 1.0 Transitionalfalsetrue
純粋な XML(非 HTML)truefalse
DOCTYPE なしfalsefalse

ポイント: XHTML は XML のサブセットですが、Tidy の判定では tidy_is_xml()tidy_is_xhtml()独立した判定です。XHTML ドキュメントに対して tidy_is_xml()true を返すとは限りません。


4. 動作概念図

┌──────────────────────────────────────────────────────────────┐
│  入力ドキュメントのパターン                                   │
│                                                               │
│  パターンA: 純粋な XML                                        │
│  <?xml version="1.0"?>                                        │
│  <root><item>値</item></root>                                 │
│  ※ HTML/XHTML DOCTYPE なし                                   │
│                                                               │
│  パターンB: XHTML(XML サブセット)                           │
│  <?xml version="1.0"?>                                        │
│  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...">          │
│  <html xmlns="http://www.w3.org/1999/xhtml">...</html>        │
│                                                               │
│  パターンC: HTML5                                             │
│  <!DOCTYPE html><html>...</html>                              │
└───────────────────────┬──────────────────────────────────────┘
                        │ tidy_parse_string()
                        ▼
┌──────────────────────────────────────────────────────────────┐
│  Tidy 解析エンジン                                            │
│  XML 宣言・DOCTYPE・名前空間をもとに判定                      │
└───────────┬───────────────────────┬──────────────────────────┘
            │ tidy_is_xml()         │ tidy_is_xhtml()
            ▼                       ▼
    A: true  B: false  C: false    A: false  B: true  C: false

5. 基本的な使い方

<?php
// 純粋な XML ドキュメント
$xml = '<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <book id="1">
    <title>PHPプログラミング</title>
    <price>3200</price>
  </book>
</catalog>';

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

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

出力例:

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

6. 実践的なコード例

例1: ドキュメント種別を3方向で判定するクラス

<?php
class DocumentKindDetector
{
    private tidy $tidy;

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

    public function isXml(): bool   { return tidy_is_xml($this->tidy); }
    public function isXhtml(): bool { return tidy_is_xhtml($this->tidy); }
    public function isHtml(): bool  { return !$this->isXml() && !$this->isXhtml(); }

    public function getKind(): string
    {
        return match(true) {
            $this->isXml()   => 'XML',
            $this->isXhtml() => 'XHTML',
            default          => 'HTML / その他',
        };
    }

    public function report(): void
    {
        echo "種別           : " . $this->getKind()                          . PHP_EOL;
        echo "tidy_is_xml()  : " . var_export($this->isXml(), true)          . PHP_EOL;
        echo "tidy_is_xhtml(): " . var_export($this->isXhtml(), true)         . PHP_EOL;
        echo "HTML ver       : " . tidy_get_html_ver($this->tidy)             . PHP_EOL;
        echo "ステータス     : " . tidy_get_status($this->tidy)               . PHP_EOL;
    }
}

$sources = [
    '純粋な XML'  => '<?xml version="1.0"?><root><item>値</item></root>',
    'XHTML 1.0'  => '<?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>',
    'HTML5'      => '<!DOCTYPE html><html><head><title>H5</title></head><body><p>HTML5</p></body></html>',
];

foreach ($sources as $label => $source) {
    echo "=== {$label} ===" . PHP_EOL;
    (new DocumentKindDetector($source))->report();
    echo PHP_EOL;
}

出力例:

=== 純粋な XML ===
種別           : XML
tidy_is_xml()  : true
tidy_is_xhtml(): false
HTML ver       : 0
ステータス     : 0

=== XHTML 1.0 ===
種別           : XHTML
tidy_is_xml()  : false
tidy_is_xhtml(): true
HTML ver       : 0
ステータス     : 0

=== HTML5 ===
種別           : HTML / その他
tidy_is_xml()  : false
tidy_is_xhtml(): false
HTML ver       : 0
ステータス     : 0

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

<?php
class TidyOopXmlChecker
{
    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
    {
        $isXml   = $this->tidy->isXml();
        $isXhtml = $this->tidy->isXhtml();
        echo "isXml()  : " . var_export($isXml,   true) . PHP_EOL;
        echo "isXhtml(): " . var_export($isXhtml, true)  . PHP_EOL;
        echo "判定結果 : " . match(true) {
            $isXml   => '✅ XML',
            $isXhtml => '✅ XHTML',
            default  => 'ℹ️  HTML / その他',
        } . PHP_EOL;
    }
}

$xmlSource = '<?xml version="1.0" encoding="UTF-8"?><data><value>42</value></data>';
echo "--- XML ---" . PHP_EOL;
(new TidyOopXmlChecker($xmlSource))->check();

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

出力例:

--- XML ---
isXml()  : true
isXhtml(): false
判定結果 : ✅ XML

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

例3: 複数ファイルを一括判定して種別ごとに仕分けるクラス

<?php
class DocumentSorter
{
    /** @var array<string, list<string>> */
    private array $buckets = ['XML' => [], 'XHTML' => [], 'HTML' => []];

    public function addAll(array $documents): self
    {
        foreach ($documents as $name => $source) {
            $tidy = tidy_parse_string($source, [], 'UTF8');
            $kind = match(true) {
                tidy_is_xml($tidy)   => 'XML',
                tidy_is_xhtml($tidy) => 'XHTML',
                default              => 'HTML',
            };
            $this->buckets[$kind][] = $name;
        }
        return $this;
    }

    public function printResult(): void
    {
        echo "=== ドキュメント種別仕分け結果 ===" . PHP_EOL;
        foreach ($this->buckets as $kind => $names) {
            echo "[{$kind}] " . count($names) . " 件" . PHP_EOL;
            foreach ($names as $name) {
                echo "  - {$name}" . PHP_EOL;
            }
        }
    }
}

$sorter = new DocumentSorter();
$sorter->addAll([
    'config.xml'   => '<?xml version="1.0"?><config><debug>true</debug></config>',
    'feed.xml'     => '<?xml version="1.0"?><rss version="2.0"><channel><title>Blog</title></channel></rss>',
    'page.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>X</title></head><body><p>X</p></body></html>',
    'index.html'   => '<!DOCTYPE html><html><body><p>HTML5</p></body></html>',
    'legacy.html'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"><html><body><p>HTML4</p></body></html>',
])->printResult();

出力例:

=== ドキュメント種別仕分け結果 ===
[XML] 2 件
  - config.xml
  - feed.xml
[XHTML] 1 件
  - page.xhtml
[HTML] 2 件
  - index.html
  - legacy.html

例4: XML の場合に専用パーサーへ誘導するルーティングクラス

<?php
class DocumentRouter
{
    public function route(string $name, string $source): void
    {
        $tidy = tidy_parse_string($source, [], 'UTF8');

        match(true) {
            tidy_is_xml($tidy)   => $this->handleXml($name, $source),
            tidy_is_xhtml($tidy) => $this->handleXhtml($name, $source),
            default              => $this->handleHtml($name, $source),
        };
    }

    private function handleXml(string $name, string $source): void
    {
        echo "[XML ルート] {$name} → SimpleXML で処理します。" . PHP_EOL;
        $xml = simplexml_load_string($source);
        echo "  ルート要素: <" . $xml->getName() . ">" . PHP_EOL;
    }

    private function handleXhtml(string $name, string $source): void
    {
        echo "[XHTML ルート] {$name} → XHTML バリデーターへ送ります。" . PHP_EOL;
    }

    private function handleHtml(string $name, string $source): void
    {
        $tidy = tidy_parse_string($source, ['indent' => true], 'UTF8');
        tidy_clean_repair($tidy);
        echo "[HTML ルート] {$name} → Tidy で修復・整形します。" . PHP_EOL;
        echo "  整形後バイト数: " . strlen(tidy_get_output($tidy)) . PHP_EOL;
    }
}

$router = new DocumentRouter();
$router->route('data.xml',  '<?xml version="1.0"?><items><item>A</item><item>B</item></items>');
$router->route('index.html','<!DOCTYPE html><html><body><p>HTML</p></body></html>');

出力例:

[XML ルート] data.xml → SimpleXML で処理します。
  ルート要素: <items>
[HTML ルート] index.html → Tidy で修復・整形します。
  整形後バイト数: 183

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

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

    public function log(string $label, string $source): void
    {
        $tidy    = tidy_parse_string($source, [], 'UTF8');
        $isXml   = tidy_is_xml($tidy);
        $isXhtml = tidy_is_xhtml($tidy);
        $kind    = match(true) {
            $isXml   => 'XML',
            $isXhtml => 'XHTML',
            default  => 'HTML',
        };

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

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

$logger = new DocumentTypeLogger('/tmp/doctype_audit.log');
$logger->log('feed.xml',   '<?xml version="1.0"?><rss><channel><title>Blog</title></channel></rss>');
$logger->log('page.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>X</title></head><body></body></html>');
$logger->log('index.html', '<!DOCTYPE html><html><body><p>HTML5</p></body></html>');

出力例:

[2026-07-19 12:00:00] feed.xml             | kind=XML   | isXml=true  | isXhtml=false | status=0
[2026-07-19 12:00:00] page.xhtml           | kind=XHTML | isXml=false | isXhtml=true  | status=0
[2026-07-19 12:00:00] index.html           | kind=HTML  | isXml=false | isXhtml=false | status=0

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

<?php
/**
 * tidy_is_xml() は「入力」の種別判定。
 * output-xml オプションは「出力」形式の制御。
 * 両者は独立しており、混同に注意。
 */
class XmlInputOutputInspector
{
    public function inspect(string $source, array $config): void
    {
        // 入力判定(config の影響を受けない)
        $tidyIn = tidy_parse_string($source, [], 'UTF8');
        $isXml  = tidy_is_xml($tidyIn);

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

        echo "--- 入力判定 ---" . PHP_EOL;
        echo "tidy_is_xml()  : " . var_export($isXml,    true) . PHP_EOL;
        echo PHP_EOL;
        echo "--- 出力設定 ---" . PHP_EOL;
        echo "output-xml     : " . var_export($outputXml, true) . PHP_EOL;
        echo PHP_EOL;
        echo "--- 出力(先頭120文字)---" . PHP_EOL;
        echo substr($output, 0, 120) . "..." . PHP_EOL;
    }
}

$inspector = new XmlInputOutputInspector();

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

出力例:

=== HTML5 入力 → output-xml=true ===
--- 入力判定 ---
tidy_is_xml()  : false    ← 入力は HTML5 なので false

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

--- 出力(先頭120文字)---
<?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 DocProfile
{
    public function __construct(
        public bool   $isXml,
        public bool   $isXhtml,
        public string $kind,
        public int    $htmlVersion,
        public int    $status,
        public string $release,
        public string $errorBuffer,
    ) {}
}

class DocumentProfiler
{
    public function profile(string $source): DocProfile
    {
        $tidy    = tidy_parse_string($source, [], 'UTF8');
        $isXml   = tidy_is_xml($tidy);
        $isXhtml = tidy_is_xhtml($tidy);

        return new DocProfile(
            isXml:       $isXml,
            isXhtml:     $isXhtml,
            kind:        match(true) { $isXml => 'XML', $isXhtml => 'XHTML', default => 'HTML' },
            htmlVersion: tidy_get_html_ver($tidy),
            status:      tidy_get_status($tidy),
            release:     tidy_get_release(),
            errorBuffer: tidy_get_error_buffer($tidy) ?: '',
        );
    }

    public function print(DocProfile $p): void
    {
        echo "=== ドキュメントプロファイル ===" . PHP_EOL;
        echo "種別            : {$p->kind}"                                    . PHP_EOL;
        echo "isXml()         : " . var_export($p->isXml,   true)             . PHP_EOL;
        echo "isXhtml()       : " . var_export($p->isXhtml, true)             . 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 DocumentProfiler();

$docs = [
    'XML'   => '<?xml version="1.0"?><root><node>値</node></root>',
    '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>X</title></head><body><p>XHTML</p></body></html>',
    'HTML5' => '<!DOCTYPE html><html><head><title>H5</title></head><body><p>HTML5</p></body></html>',
];

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

出力例:

【XML】
=== ドキュメントプロファイル ===
種別            : XML
isXml()         : true
isXhtml()       : false
HTML バージョン : HTML5 / 不明
ステータス      : ✅ 正常
libtidy         : 1st August 2023
エラーバッファ  : (なし)

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

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

7. 関連関数との比較

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

混同しやすいポイント: tidy_is_xml()入力の種別判定であり、tidy_getopt($tidy, 'output-xml')出力形式の設定値です。また tidy_is_xml()tidy_is_xhtml() は排他的ではなく、Tidy の内部ロジックによって独立して判定されます。


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

① XHTML に対して tidy_is_xml() は false を返す

XHTML は XML のサブセットですが、Tidy の判定では tidy_is_xhtml()true を返す場合、tidy_is_xml() は通常 false を返します。「XHTML は XML だから is_xmltrue のはず」という思い込みは誤りです。

// XHTML ドキュメント
$tidy = tidy_parse_string($xhtmlSource, [], 'UTF8');
var_dump(tidy_is_xml($tidy));   // bool(false)
var_dump(tidy_is_xhtml($tidy)); // bool(true)

② output-xml オプションと混同しない

tidy_is_xml()入力の種別判定、output-xml出力形式の設定です。HTML5 入力でも output-xml=true を指定すれば XML に近い形式で出力されますが、tidy_is_xml()false のままです。

③ XML 宣言だけでは true にならない場合がある

<?xml version="1.0"?> があっても、XHTML の DOCTYPE が続く場合は tidy_is_xhtml()true になり、tidy_is_xml()false になることがあります。

④ tidy_clean_repair() の前後で結果が変わることがある

修復処理によって DOCTYPE や XML 宣言が補完・変更されることがあり、判定結果が変わる場合があります。判定は tidy_parse_string() 直後、tidy_clean_repair() の前に行うのが安全です。

$tidy = tidy_parse_string($source, [], 'UTF8');
$isXml = tidy_is_xml($tidy);   // cleanRepair 前に判定
tidy_clean_repair($tidy);
// この後は $isXml の値が変わっている可能性がある

9. まとめ

項目内容
主な用途解析済みドキュメントが XML かどうかをプログラムで判定する
戻り値true(XML)/ false(XHTML・HTML またはその他)
OOP 版$tidy->isXml()
XHTML との関係XHTML は tidy_is_xml()=false / tidy_is_xhtml()=true
混同注意output-xml オプション(出力形式)とは別物
判定タイミングtidy_clean_repair() の前が安全
よく使う組み合わせtidy_is_xhtml(), tidy_get_html_ver(), tidy_get_status(), tidy_getopt()

tidy_is_xml()tidy_is_xhtml() と対になる関数で、HTML・XHTML・XML の3種を判別する際の核心となります。「XHTML は XML だから両方 true」という誤解を避け、Tidy が独立した基準で判定することを理解した上で、ドキュメントルーティングや出力形式の自動選択に活用してください。

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