[PHP]tidy_get_error_buffer関数でTidyのエラーログ文字列を取得する方法を解説

PHP

tidy_error_count()tidy_warning_count() でエラーや警告の「件数」はわかりますが、「具体的にどの行の何が問題なのか」を知るためには、ログ文字列そのものを取得する必要があります。

tidy_get_error_buffer() は、Tidyが解析・修正処理の中で蓄積したエラー・警告・診断メッセージをまとめてひとつの文字列として返す関数です。$tidy->errorBuffer プロパティと同じ内容を返す手続き型の関数バリアントでもあります。この記事では基本仕様から実践的な活用パターンまで詳しく解説します。

関数概要

項目内容
関数名tidy_get_error_buffer()
読み方タイディ・ゲット・エラーバッファ
分類Tidy関数
対応バージョンPHP 5以降(Tidy拡張が有効な場合)
引数tidyオブジェクト
戻り値string(ログ文字列)または false(失敗時)
必要な拡張tidy(多くの環境で標準バンドル)
OOPスタイル$tidy->errorBuffer(プロパティ)

構文

// 手続き型
tidy_get_error_buffer(tidy $tidy): string|false

// OOPスタイル(プロパティアクセス)
$tidy->errorBuffer

重要:tidy_get_error_buffer() の OOPスタイルはメソッドではなくプロパティです。$tidy->getErrorBuffer() というメソッドは存在せず、$tidy->errorBuffer というプロパティに直接アクセスします。手続き型と OOP で呼び出し方が異なる点に注意してください。

errorBuffer の内容

tidy_parse_string() / tidy_parse_file()
        │
        ▼
  tidy_clean_repair()
        │  ← エラー・警告・アクセス警告ログが書き込まれる
        ▼
  tidy_diagnose()(オプション)
        │  ← 文書統計情報が追記される
        ▼
  tidy_get_error_buffer() / $tidy->errorBuffer
        └─ すべてのログをまとめた文字列を返す

ログの各行は次のような形式です。

line {行番号} column {列番号} - {レベル}: {メッセージ}

例:

line 1 column 14 - Warning: missing </b> before </body>
line 1 column 14 - Access: [1.1.1]: <img> missing 'alt' text.
Info: Document content looks like HTML5
Tidy found 1 warnings and 0 errors!

基本的な使い方

<?php
$html = '<html><body><img src="photo.jpg"><p>テスト<b>未閉じ</body></html>';

$tidy = tidy_parse_string($html, ['accessibility-check' => 1], 'UTF8');
tidy_clean_repair($tidy);
tidy_diagnose($tidy);

// 手続き型
$buffer = tidy_get_error_buffer($tidy);
echo $buffer . PHP_EOL;

// OOPスタイル(同一内容)
echo $tidy->errorBuffer . PHP_EOL;

実行結果:

line 1 column 34 - Warning: missing </b> before </body>
line 1 column 13 - Access: [1.1.1]: <img> missing 'alt' text.

Info: Document content looks like HTML5
Tidy found 1 warnings and 0 errors!

実践的なコード例

例1:エラーバッファを整形して表示するクラス

<?php
class TidyErrorBufferFormatter
{
    public function format(string $html, array $config = []): void
    {
        $defaultConfig = [
            'output-xhtml'        => true,
            'accessibility-check' => 1,
            'char-encoding'       => 'utf8',
        ];

        $tidy = tidy_parse_string($html, array_merge($defaultConfig, $config), 'UTF8');
        tidy_clean_repair($tidy);
        tidy_diagnose($tidy);

        $buffer = tidy_get_error_buffer($tidy);

        if ($buffer === false || trim($buffer) === '') {
            echo "ログなし(問題は検出されませんでした)" . PHP_EOL;
            return;
        }

        $lines = array_filter(array_map('trim', explode("\n", $buffer)));

        foreach ($lines as $line) {
            if (stripos($line, 'Error:') !== false) {
                echo "[ERROR]   {$line}" . PHP_EOL;
            } elseif (stripos($line, 'Warning:') !== false) {
                echo "[WARNING] {$line}" . PHP_EOL;
            } elseif (stripos($line, 'Access:') !== false) {
                echo "[ACCESS]  {$line}" . PHP_EOL;
            } elseif (stripos($line, 'Info:') !== false) {
                echo "[INFO]    {$line}" . PHP_EOL;
            } else {
                echo "[OTHER]   {$line}" . PHP_EOL;
            }
        }
    }
}

$formatter = new TidyErrorBufferFormatter();
$formatter->format('<html><body><img src="x.jpg"><p>テスト<b>未閉じ</body></html>');

実行結果:

[WARNING] line 1 column 34 - Warning: missing </b> before </body>
[ACCESS]  line 1 column 13 - Access: [1.1.1]: <img> missing 'alt' text.
[INFO]    Info: Document content looks like HTML5
[OTHER]   Tidy found 1 warnings and 0 errors!

例2:バッファをログファイルに書き出す

<?php
class TidyBufferLogger
{
    public function __construct(
        private readonly string $logPath = '/tmp/tidy-errors.log'
    ) {}

    public function processAndLog(string $label, string $html): void
    {
        $tidy = tidy_parse_string($html, [
            'output-xhtml'        => true,
            'accessibility-check' => 1,
        ], 'UTF8');
        tidy_clean_repair($tidy);
        tidy_diagnose($tidy);

        $buffer = tidy_get_error_buffer($tidy);

        if ($buffer === false || trim($buffer) === '') {
            return; // 問題なければログに書かない
        }

        $entry = sprintf(
            "[%s] === %s ===\n%s\n\n",
            date('Y-m-d H:i:s'),
            $label,
            $buffer
        );

        file_put_contents($this->logPath, $entry, FILE_APPEND | LOCK_EX);
        echo "{$label}: ログを記録しました。" . PHP_EOL;
    }
}

$logger = new TidyBufferLogger();
$logger->processAndLog('商品ページ', '<html><body><img src="item.jpg"></body></html>');
$logger->processAndLog('正常ページ', '<html><body><h1>ホーム</h1></body></html>');

実行結果:

商品ページ: ログを記録しました。

問題がなかった「正常ページ」はログに記録されず、問題のあった「商品ページ」のみが記録されます。

例3:バッファをパースして構造化データに変換する

<?php
class ErrorBufferParser
{
    public function parse(string $html): array
    {
        $tidy = tidy_parse_string($html, [
            'output-xhtml'        => true,
            'accessibility-check' => 1,
        ], 'UTF8');
        tidy_clean_repair($tidy);

        $buffer = tidy_get_error_buffer($tidy);

        if ($buffer === false) {
            return [];
        }

        $results = [];
        $lines   = array_filter(array_map('trim', explode("\n", $buffer)));

        foreach ($lines as $line) {
            // "line N column M - Level: message" の形式をパース
            if (preg_match('/^line (\d+) column (\d+) - (\w+): (.+)$/', $line, $m)) {
                $results[] = [
                    'line'    => (int) $m[1],
                    'column'  => (int) $m[2],
                    'level'   => strtolower($m[3]),
                    'message' => $m[4],
                ];
            }
        }

        return $results;
    }
}

$parser  = new ErrorBufferParser();
$entries = $parser->parse(
    '<html><body><img src="a.jpg"><p>テスト</p><b>閉じ忘れ</body></html>'
);

foreach ($entries as $entry) {
    printf(
        "[%s] 行%d 列%d: %s\n",
        strtoupper($entry['level']),
        $entry['line'],
        $entry['column'],
        $entry['message']
    );
}

実行結果:

[WARNING] 行1 列43: missing </b> before </body>
[ACCESS] 行1 列13: [1.1.1]: <img> missing 'alt' text.

errorBuffer を正規表現でパースすることで、行番号・列番号・レベル・メッセージを構造化データとして扱えます。

例4:false を考慮した安全な取得

<?php
class SafeErrorBufferAccessor
{
    public function get(string $html): string
    {
        $tidy = tidy_parse_string($html, [], 'UTF8');

        if ($tidy === false) {
            return '(tidy_parse_string に失敗しました)';
        }

        tidy_clean_repair($tidy);

        $buffer = tidy_get_error_buffer($tidy);

        // false または空文字の場合に備えてフォールバック
        if ($buffer === false || trim($buffer) === '') {
            return '(エラーバッファが空です)';
        }

        return $buffer;
    }
}

$accessor = new SafeErrorBufferAccessor();

echo $accessor->get('<html><body><p>正常なHTML</p></body></html>') . PHP_EOL;
echo "---" . PHP_EOL;
echo $accessor->get('<html><body><img src="x.jpg"></body></html>') . PHP_EOL;

実行結果:

(エラーバッファが空です)
---
line 1 column 14 - Warning: <img> lacks "alt" attribute

tidy_get_error_buffer() の戻り値は string|false のため、false の場合のガードを入れておくと安全です。

例5:エラーバッファを HTML レポートとして整形する

<?php
class HtmlBufferReportGenerator
{
    public function generate(string $pageHtml): string
    {
        $tidy = tidy_parse_string($pageHtml, [
            'accessibility-check' => 1,
            'output-xhtml'        => true,
        ], 'UTF8');
        tidy_clean_repair($tidy);
        tidy_diagnose($tidy);

        $buffer = tidy_get_error_buffer($tidy);
        $lines  = $buffer ? array_filter(array_map('trim', explode("\n", $buffer))) : [];

        $rows = '';
        foreach ($lines as $line) {
            $class = 'info';
            if (stripos($line, 'Error:') !== false) {
                $class = 'error';
            } elseif (stripos($line, 'Warning:') !== false) {
                $class = 'warning';
            } elseif (stripos($line, 'Access:') !== false) {
                $class = 'access';
            }
            $rows .= sprintf(
                '<tr class="%s"><td>%s</td></tr>' . PHP_EOL,
                $class,
                htmlspecialchars($line)
            );
        }

        return <<<HTML
<style>
  table { border-collapse: collapse; width: 100%; }
  td { padding: 4px 8px; border: 1px solid #ccc; font-family: monospace; }
  .error   { background: #fee; }
  .warning { background: #ffe; }
  .access  { background: #eff; }
  .info    { background: #f5f5f5; color: #666; }
</style>
<table>
{$rows}
</table>
HTML;
    }
}

$gen    = new HtmlBufferReportGenerator();
$report = $gen->generate('<html><body><img src="x.jpg"><p>テスト</p></body></html>');
echo $report;

実行結果(HTMLとして出力されるためブラウザで確認):

<style>
  table { border-collapse: collapse; width: 100%; }
  ...
</style>
<table>
<tr class="warning"><td>line 1 column 14 - Warning: ...</td></tr>
<tr class="access"><td>line 1 column 14 - Access: ...</td></tr>
<tr class="info"><td>Info: Document content looks like HTML5</td></tr>
<tr class="info"><td>Tidy found 1 warnings and 0 errors!</td></tr>
</table>

例6:複数ページのバッファをまとめて出力する

<?php
class BulkBufferCollector
{
    private array $results = [];

    public function add(string $label, string $html): self
    {
        $tidy = tidy_parse_string($html, [
            'output-xhtml'        => true,
            'accessibility-check' => 1,
        ], 'UTF8');
        tidy_clean_repair($tidy);

        $buffer = tidy_get_error_buffer($tidy);

        $this->results[] = [
            'label'    => $label,
            'buffer'   => $buffer ?: '',
            'errors'   => tidy_error_count($tidy),
            'warnings' => tidy_warning_count($tidy),
            'access'   => tidy_access_count($tidy),
        ];

        return $this;
    }

    public function printSummary(): void
    {
        foreach ($this->results as $r) {
            $hasIssues = $r['errors'] + $r['warnings'] + $r['access'] > 0;
            printf(
                "[%s] %s (エラー:%d 警告:%d A11y:%d)\n",
                $hasIssues ? '✗' : '✓',
                $r['label'],
                $r['errors'],
                $r['warnings'],
                $r['access']
            );
            if ($hasIssues && $r['buffer']) {
                foreach (array_filter(explode("\n", trim($r['buffer']))) as $line) {
                    echo "      {$line}\n";
                }
            }
        }
    }
}

$collector = new BulkBufferCollector();
$collector
    ->add('トップ',   '<html><body><h1>ホーム</h1></body></html>')
    ->add('商品ページ', '<html><body><img src="item.jpg"><a href="#"></a></body></html>')
    ->add('会社概要',  '<html><body><p>会社情報</p></body></html>');

$collector->printSummary();

実行結果:

[✓] トップ (エラー:0 警告:0 A11y:0)
[✗] 商品ページ (エラー:0 警告:0 A11y:2)
      line 1 column 14 - Access: [1.1.1]: <img> missing 'alt' text.
      line 1 column 26 - Access: [13.1.1]: link text not meaningful.
[✓] 会社概要 (エラー:0 警告:0 A11y:0)

例7:手続き型と OOPプロパティを同一視する確認テスト

<?php
class ErrorBufferConsistencyChecker
{
    public function verify(string $html): void
    {
        $tidy = tidy_parse_string($html, ['output-xhtml' => true], 'UTF8');
        tidy_clean_repair($tidy);
        tidy_diagnose($tidy);

        $viaFunction = tidy_get_error_buffer($tidy); // 手続き型
        $viaProperty = $tidy->errorBuffer;            // OOPプロパティ

        $isIdentical = ($viaFunction === $viaProperty);

        echo "手続き型とプロパティの内容は同一: " . ($isIdentical ? 'はい' : 'いいえ') . PHP_EOL;
        echo PHP_EOL . $viaFunction . PHP_EOL;
    }
}

$checker = new ErrorBufferConsistencyChecker();
$checker->verify('<html><body><img src="a.jpg"><p>テスト</p></body></html>');

実行結果:

手続き型とプロパティの内容は同一: はい

line 1 column 14 - Warning: <img> lacks "alt" attribute
Info: Document content looks like HTML5
Tidy found 1 warnings and 0 errors!

tidy_get_error_buffer($tidy)$tidy->errorBuffer は同一の文字列を返すことが確認できます。

関連関数・プロパティとの比較

アクセス方法種別内容
tidy_get_error_buffer($tidy)手続き型関数エラーバッファ文字列を返す
$tidy->errorBufferOOPプロパティ同一内容のプロパティ(推奨)
tidy_error_count($tidy)手続き型関数エラー件数(int)のみ返す
tidy_warning_count($tidy)手続き型関数警告件数(int)のみ返す
tidy_access_count($tidy)手続き型関数アクセシビリティ警告件数のみ返す
tidy_diagnose($tidy)手続き型関数診断情報をバッファに追記する

カウント系関数が「件数(int)」を返すのに対し、tidy_get_error_buffer() は「ログの全文(string)」を返します。「何件か」を知りたいならカウント系、「具体的にどこが問題か」を知りたいならバッファ取得、という役割分担で使い分けましょう。

よくある落とし穴・注意点

  1. OOPスタイルはメソッドではなくプロパティ $tidy->getErrorBuffer() というメソッドは存在しません。OOPスタイルで使う場合は $tidy->errorBuffer とプロパティに直接アクセスします。括弧をつけて呼び出そうとするとエラーになります。
  2. tidy_diagnose() を呼ぶ前後で内容が変わる tidy_clean_repair() 後の時点でもエラーログは errorBuffer に入っていますが、tidy_diagnose() を呼ぶとさらに「Info:」から始まる文書統計情報が追記されます。診断情報も一緒に確認したい場合は必ず diagnose() の後にバッファを取得しましょう。
  3. バッファが空文字列になることがある HTMLが完全に正常で問題が何も検出されなかった場合、errorBuffer は空文字列("")になることがあります。tidy_get_error_buffer()false を返すケースも考慮し、=== false=== '' の両方をチェックするのが安全です。
  4. エラーバッファの書式はTidyバージョンによって異なる場合があるline N column M - Level: message」という書式はほぼ共通ですが、バージョンによって細部が異なる可能性があります。バッファをプログラムで解析する場合は、書式変更への耐性を持たせた正規表現を書くようにしましょう。
  5. バッファには改行でつながった複数行が含まれる バッファ全体はひとつの文字列ですが、その中に \n 区切りの複数行が含まれます。行ごとに処理するには explode("\n", $buffer) で分割してから扱います。

まとめ

ポイント内容
役割Tidyが蓄積したエラー・警告・診断ログ文字列を取得する
引数tidyオブジェクト
戻り値string(ログ文字列)または false
OOPスタイル$tidy->errorBuffer(プロパティ。メソッドではない)
カウント系との違い件数ではなく具体的なログ全文を返す
よく組み合わせる関数tidy_clean_repair(), tidy_diagnose(), tidy_error_count()
主な用途詳細ログの表示・記録、バッファの構造化パース、HTMLレポート生成
注意点OOPはプロパティアクセス、falseと空文字の両方を確認、diagnose()後に取得

tidy_get_error_buffer() は「件数」ではなく「中身」を取り出す関数です。カウント系関数で問題の存在を検知したら、このバッファで具体的な行・列・メッセージを確認する——という2段構えの使い方がTidy拡張の標準的なデバッグフローになります。

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