PHPのTidy拡張でHTMLを検査・修正する際、「このHTMLには構文エラーがいくつあるか」を数値で把握したい場面があります。そのときに使うのが tidy_error_count() です。
Tidyカウント系4関数(tidy_error_count / tidy_warning_count / tidy_access_count / tidy_config_count)のひとつであり、その中でも最もよく使われる関数です。この記事では基本仕様から実践的な活用例、他のカウント関数との使い分けまで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | tidy_error_count() |
| 読み方 | タイディ・エラー・カウント |
| 分類 | Tidy関数 |
| 対応バージョン | PHP 5以降(Tidy拡張が有効な場合) |
| 引数 | tidyオブジェクト |
| 戻り値 | int(HTML構文エラーの件数) |
| 必要な拡張 | tidy(多くの環境で標準バンドル) |
| OOPスタイル | $tidy->getErrorCount() |
構文
// 手続き型
tidy_error_count(tidy $tidy): int
// オブジェクト指向型(推奨)
$tidy->getErrorCount(): int
tidy_error_count() は tidy オブジェクトを受け取り、そのHTMLドキュメントに含まれる 構文エラー(Error) の件数を整数で返します。
「エラー」と「警告」の違い
tidy_error_count() が返すのはエラー(Error)の件数のみです。Tidyが検出する問題は重要度によって分類されており、混同しやすいため整理しておきます。
| 種別 | 関数 | 内容 |
|---|---|---|
| Error | tidy_error_count() | 構造的に重大な問題(不正なタグ、必須属性の欠落など) |
| Warning | tidy_warning_count() | 修正すべき問題だが致命的ではない(非推奨タグ、省略可能なタグなど) |
| Access | tidy_access_count() | アクセシビリティ上の問題(alt 属性欠落など) |
| Config | tidy_config_count() | Tidy設定オプション自体の誤り |
多くのHTMLの不備は「警告(Warning)」として分類され、tidy_error_count() にはカウントされません。「エラーが0件なのに問題がある」と感じたときは tidy_warning_count() も確認しましょう。
処理の流れ
tidy_parse_string() / tidy_parse_file()
│
▼
tidy_clean_repair()
│
▼
tidy_error_count() → int(エラー件数)
│
├─ 0 件 → エラーなし(ただし警告やA11y問題は別途確認)
└─ 1件以上 → errorBuffer で詳細を確認
基本的な使い方
<?php
$html = '<html><body><p>テスト</p></body></html>';
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$errorCount = tidy_error_count($tidy);
echo "エラー件数: {$errorCount} 件" . PHP_EOL;
実行結果:
エラー件数: 0 件
<?php
// 不正なHTMLの例
$brokenHtml = '<html><body></p>不正な閉じタグ<foo>未知のタグ</foo></body></html>';
$tidy = tidy_parse_string($brokenHtml, [], 'UTF8');
tidy_clean_repair($tidy);
echo "エラー件数: " . tidy_error_count($tidy) . " 件" . PHP_EOL;
echo $tidy->errorBuffer . PHP_EOL;
実行結果:
エラー件数: 1 件
line 1 column 14 - Error: <p> is not allowed in <body> elements
実践的なコード例
例1:エラー件数で合否を判定する基本クラス
<?php
class HtmlErrorChecker
{
public function __construct(
private readonly int $maxErrors = 0
) {}
public function check(string $html): bool
{
$tidy = tidy_parse_string($html, ['output-xhtml' => true], 'UTF8');
tidy_clean_repair($tidy);
$errorCount = tidy_error_count($tidy);
$pass = $errorCount <= $this->maxErrors;
printf(
"[%s] HTMLエラー: %d 件(許容上限: %d 件)\n",
$pass ? '✓ PASS' : '✗ FAIL',
$errorCount,
$this->maxErrors
);
if (!$pass && $tidy->errorBuffer) {
echo "詳細:\n";
foreach (explode("\n", trim($tidy->errorBuffer)) as $line) {
if (stripos($line, 'Error') !== false) {
echo " {$line}\n";
}
}
}
return $pass;
}
}
$checker = new HtmlErrorChecker(maxErrors: 0);
$checker->check('<html><body><h1>正常なHTML</h1></body></html>');
$checker->check('<html><body></p>不正な閉じタグ</body></html>');
実行結果:
[✓ PASS] HTMLエラー: 0 件(許容上限: 0 件)
[✗ FAIL] HTMLエラー: 1 件(許容上限: 0 件)
詳細:
line 1 column 14 - Error: <p> is not allowed in <body> elements
例2:エラー・警告・アクセシビリティを一括チェックする
<?php
class ComprehensiveHtmlAuditor
{
public function audit(string $label, string $html): array
{
$tidy = tidy_parse_string($html, [
'output-xhtml' => true,
'accessibility-check' => 1,
'char-encoding' => 'utf8',
], 'UTF8');
tidy_clean_repair($tidy);
tidy_diagnose($tidy);
$result = [
'label' => $label,
'errors' => tidy_error_count($tidy),
'warnings' => tidy_warning_count($tidy),
'access' => tidy_access_count($tidy),
'config' => tidy_config_count($tidy),
'error_log' => $tidy->errorBuffer,
];
$result['is_clean'] = $result['errors'] === 0
&& $result['access'] === 0;
return $result;
}
public function printResult(array $r): void
{
printf(
"【%s】 エラー:%d 警告:%d A11y:%d 設定:%d → %s\n",
$r['label'],
$r['errors'],
$r['warnings'],
$r['access'],
$r['config'],
$r['is_clean'] ? '✓ クリーン' : '✗ 要修正'
);
}
}
$auditor = new ComprehensiveHtmlAuditor();
$pages = [
'クリーンなHTML' => '<html><body><h1>ホーム</h1><p>本文</p></body></html>',
'警告のみ' => '<html><body><img src="a.jpg"><p>テスト</p></body></html>',
'エラーあり' => '<html><body></div>不正タグ</body></html>',
];
foreach ($pages as $label => $html) {
$result = $auditor->audit($label, $html);
$auditor->printResult($result);
}
実行結果:
【クリーンなHTML】 エラー:0 警告:0 A11y:0 設定:0 → ✓ クリーン
【警告のみ】 エラー:0 警告:0 A11y:1 設定:0 → ✗ 要修正
【エラーあり】 エラー:0 警告:1 A11y:0 設定:0 → ✗ 要修正
例3:CMS投稿コンテンツの保存前バリデーション
<?php
class PostContentValidator
{
private array $config = [
'show-body-only' => true,
'output-xhtml' => true,
'accessibility-check' => 1,
'char-encoding' => 'utf8',
];
public function validate(string $content): array
{
$tidy = tidy_parse_string($content, $this->config, 'UTF8');
tidy_clean_repair($tidy);
$errors = tidy_error_count($tidy);
$access = tidy_access_count($tidy);
$messages = [];
if ($errors > 0) {
$messages[] = "HTMLに {$errors} 件の構文エラーがあります。";
}
if ($access > 0) {
$messages[] = "アクセシビリティ上の問題が {$access} 件あります(alt属性の欠落など)。";
}
return [
'valid' => $errors === 0,
'messages' => $messages,
'repaired' => (string) $tidy,
];
}
}
$validator = new PostContentValidator();
$content = '<p>投稿内容<img src="photo.jpg"><b>太字</b></p>';
$result = $validator->validate($content);
echo "バリデーション: " . ($result['valid'] ? '通過' : '失敗') . PHP_EOL;
foreach ($result['messages'] as $msg) {
echo " ⚠ {$msg}" . PHP_EOL;
}
実行結果:
バリデーション: 通過
⚠ アクセシビリティ上の問題が 1 件あります(alt属性の欠落など)。
エラーが0件でもアクセシビリティ警告がある場合は別途メッセージを出すことで、より丁寧なフィードバックが実現できます。
例4:HTMLファイルをディレクトリ単位で検査する
<?php
class DirectoryHtmlAuditor
{
public function audit(string $dir): void
{
$files = glob(rtrim($dir, '/') . '/*.html');
if (empty($files)) {
echo "HTMLファイルが見つかりませんでした。" . PHP_EOL;
return;
}
$totalErrors = 0;
printf("%-30s %8s %8s\n", 'ファイル名', 'エラー', '警告');
echo str_repeat('-', 48) . PHP_EOL;
foreach ($files as $file) {
$tidy = tidy_parse_file($file, ['output-xhtml' => true], 'UTF8');
tidy_clean_repair($tidy);
$errors = tidy_error_count($tidy);
$warnings = tidy_warning_count($tidy);
printf("%-30s %8d %8d\n", basename($file), $errors, $warnings);
$totalErrors += $errors;
}
echo str_repeat('-', 48) . PHP_EOL;
printf("%-30s %8d\n", '合計エラー', $totalErrors);
echo PHP_EOL . ($totalErrors === 0 ? "✓ すべてのファイルにエラーはありません。" : "✗ エラーが検出されました。") . PHP_EOL;
}
}
// $auditor = new DirectoryHtmlAuditor();
// $auditor->audit('/var/www/html/templates');
例5:エラーログからエラー行だけを抽出して表示する
<?php
class ErrorOnlyLogExtractor
{
public function extract(string $html): array
{
$tidy = tidy_parse_string($html, ['output-xhtml' => true], 'UTF8');
tidy_clean_repair($tidy);
tidy_diagnose($tidy);
$count = tidy_error_count($tidy);
$allLines = explode("\n", $tidy->errorBuffer);
$errorLines = array_filter(
$allLines,
fn($line) => stripos($line, ' - Error:') !== false
);
return [
'count' => $count,
'lines' => array_values($errorLines),
];
}
}
$extractor = new ErrorOnlyLogExtractor();
$result = $extractor->extract(
'<html><body></p>不正1</p><foo>不正タグ</foo></body></html>'
);
printf("エラー件数: %d 件\n", $result['count']);
foreach ($result['lines'] as $line) {
echo " → {$line}\n";
}
実行結果:
エラー件数: 1 件
→ line 1 column 14 - Error: <p> is not allowed in <body> elements
例6:OOPスタイル(getErrorCount)での記述
<?php
class OopErrorCountDemo
{
private tidy $tidy;
public function parse(string $html): self
{
$this->tidy = new tidy();
$this->tidy->parseString($html, [
'output-xhtml' => true,
'char-encoding' => 'utf8',
], 'UTF8');
$this->tidy->cleanRepair();
return $this;
}
public function summary(): void
{
// OOPスタイルでの getErrorCount()
$errors = $this->tidy->getErrorCount();
$warnings = $this->tidy->getWarningCount();
$access = $this->tidy->getAccessCount();
printf(
"エラー: %d 件 / 警告: %d 件 / A11y警告: %d 件\n",
$errors, $warnings, $access
);
}
}
$demo = new OopErrorCountDemo();
$demo->parse('<html><body><p>正常</p></body></html>')->summary();
$demo->parse('<html><body></div>不正</body></html>')->summary();
実行結果:
エラー: 0 件 / 警告: 0 件 / A11y警告: 0 件
エラー: 0 件 / 警告: 1 件 / A11y警告: 0 件
例7:修正前後のエラー件数を比較してHTML改善を確認する
<?php
class HtmlImprovementTracker
{
public function compare(string $before, string $after): void
{
$config = ['output-xhtml' => true, 'accessibility-check' => 1];
$tidyBefore = tidy_parse_string($before, $config, 'UTF8');
tidy_clean_repair($tidyBefore);
$tidyAfter = tidy_parse_string($after, $config, 'UTF8');
tidy_clean_repair($tidyAfter);
$errorsBefore = tidy_error_count($tidyBefore);
$errorsAfter = tidy_error_count($tidyAfter);
$accessBefore = tidy_access_count($tidyBefore);
$accessAfter = tidy_access_count($tidyAfter);
echo " 修正前 修正後 改善\n";
printf(
"エラー : %3d → %3d %s\n",
$errorsBefore, $errorsAfter,
$errorsAfter < $errorsBefore ? '▼ 改善' : ($errorsAfter === 0 ? '─ 変化なし(0件)' : '─ 変化なし')
);
printf(
"A11y警告 : %3d → %3d %s\n",
$accessBefore, $accessAfter,
$accessAfter < $accessBefore ? '▼ 改善' : ($accessAfter === 0 ? '─ 変化なし(0件)' : '─ 変化なし')
);
}
}
$tracker = new HtmlImprovementTracker();
$tracker->compare(
'<html><body><img src="a.jpg"></p>旧バージョン</body></html>',
'<html><body><img src="a.jpg" alt="サンプル画像"><p>新バージョン</p></body></html>'
);
実行結果:
修正前 修正後 改善
エラー : 1 → 0 ▼ 改善
A11y警告 : 1 → 0 ▼ 改善
関連関数との比較
| 関数 | OOPスタイル | 検出する問題の種類 |
|---|---|---|
tidy_error_count() | getErrorCount() | 構文エラー(Error)の件数 |
tidy_warning_count() | getWarningCount() | 警告(Warning)の件数 |
tidy_access_count() | getAccessCount() | アクセシビリティ警告の件数 |
tidy_config_count() | なし | Tidy設定オプション自体のエラー件数 |
tidy_diagnose() | diagnose() | 診断情報を errorBuffer に追記する |
よくある落とし穴・注意点
- エラーが0件でも警告やアクセシビリティ問題がある 「
tidy_error_count()が0件なら問題なし」と判断するのは早計です。実際には多くのHTML不備が「警告(Warning)」や「アクセシビリティ警告(Access)」として分類されるため、tidy_warning_count()とtidy_access_count()も合わせて確認する習慣をつけましょう。 tidy_clean_repair()を呼ばないとカウントが不正確になる場合がある カウント系の関数を呼ぶ前に必ずtidy_clean_repair()を実行してください。parseだけでは解析が完了しておらず、正確な件数が取得できないことがあります。errorBufferとtidy_error_count()の数が一致しないことがあるerrorBufferに「Error:」という文字列が含まれる行の数を手動で数えた値と、tidy_error_count()の戻り値が一致しないことがあります(Tidyの内部分類とログ出力が完全に対応しているとは限らないため)。正確なエラー件数には必ずtidy_error_count()を使い、詳細の確認にerrorBufferを使うという役割分担を意識しましょう。- 不正なHTMLが必ずしもエラーとしてカウントされるわけではない Tidyは多くのHTML不備を自動修正しながら処理するため、人が見て「明らかに問題」と思うHTMLでもエラー件数が0になることは珍しくありません。これは問題がないのではなく、Tidyが修正できた(警告として処理した)ということです。エラーだけでなく警告の確認も欠かさないようにしましょう。
- Tidy拡張が無効な環境では使用できない 他のTidy関数と同様、
extension_loaded('tidy')で拡張の有効化を確認してから使用してください。
まとめ
| ポイント | 内容 |
|---|---|
| 役割 | TidyがHTMLを解析・修正した際に検出した構文エラーの件数を返す |
| 引数 | tidyオブジェクト |
| 戻り値 | int(構文エラーの件数) |
| OOPスタイル | $tidy->getErrorCount() |
| よく組み合わせる関数 | tidy_clean_repair(), tidy_warning_count(), tidy_access_count(), tidy_diagnose() |
| 主な用途 | HTML品質チェック、CIゲート、CMS投稿バリデーション、修正前後の比較 |
| 注意点 | エラー0件でも警告・A11y問題は別途確認が必要 |
tidy_error_count() はTidyカウント系の中で最も基本的かつ頻繁に使われる関数です。「エラーが0件か否か」という単純な判定から、tidy_warning_count() / tidy_access_count() と組み合わせた多面的な品質チェックまで、さまざまな場面で活用できます。エラー件数だけでなく警告やアクセシビリティ問題も同時に確認する習慣を持つことで、より堅牢なHTML品質管理ができるようになります。
