1. 関数概要
tidy_warning_count は、PHP の Tidy 拡張が HTML/XHTML を解析した際に検出した警告(Warning)の件数を整数で返す関数です。tidy_get_status() が「警告があるかどうか」を判定するのに対し、tidy_warning_count() は「何件の警告があるか」という数量を把握できます。品質スコアの算出・しきい値チェック・レポート生成に活用できます。
| 項目 | 内容 |
|---|---|
| 関数名 | tidy_warning_count |
| 所属拡張 | Tidy |
| 戻り値の型 | int |
| 手続き型 / OOP | 手続き型(OOP 版: $tidy->warningCount()) |
| PHP バージョン | PHP 5 以降 |
| 公式ドキュメント | https://www.php.net/manual/ja/tidy.warningcount.php |
2. 構文
// 手続き型
tidy_warning_count(tidy $tidy): int
// オブジェクト指向型
$tidy->warningCount(): int
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
$tidy | tidy | tidy_parse_string() 等で生成した tidy オブジェクト |
戻り値
解析中に検出された警告の件数(int)。警告がない場合は 0 を返します。
3. tidy_warning_count / tidy_error_count / tidy_get_status の関係
| 関数 | 取得できる情報 | 戻り値 |
|---|---|---|
tidy_warning_count() | 警告の件数 | int |
tidy_error_count() | エラーの件数 | int |
tidy_access_count() | アクセシビリティ警告の件数 | int |
tidy_get_status() | 問題の有無を示すステータスコード(0/1/2) | int |
tidy_get_error_buffer() | 警告・エラーの詳細メッセージ | string|false |
使い分けのポイント: 「問題があるか」の二択判定は
tidy_get_status()、「何件あるか」の数量把握はtidy_warning_count()/tidy_error_count()、「どんな問題か」の詳細確認はtidy_get_error_buffer()を使います。
4. 動作概念図
┌──────────────────────────────────────────────────────────┐
│ HTML ソース(問題あり) │
│ <html><body><img src="a.jpg"> │
│ <table><td>データ</td></table> │
│ </body></html> │
└─────────────────────┬────────────────────────────────────┘
│ tidy_parse_string()
▼
┌──────────────────────────────────────────────────────────┐
│ Tidy 解析エンジン │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 内部カウンタ │ │
│ │ warnings : 2 ← img の alt 欠落, table summary │ │
│ │ errors : 0 │ │
│ │ access : 0 │ │
│ └──────────────────────────────────────────────────┘ │
└──────────┬────────────────────────┬───────────────────────┘
│ tidy_warning_count() │ tidy_get_status()
▼ ▼
2 (int) 1 (警告あり)
5. 基本的な使い方
<?php
// 警告が発生しやすい HTML
$html = '<html><body>
<img src="photo.jpg">
<table><td>データ</td></table>
</body></html>';
$tidy = tidy_parse_string($html, [], 'UTF8');
$warnings = tidy_warning_count($tidy);
$errors = tidy_error_count($tidy);
$status = tidy_get_status($tidy);
echo "警告件数 : {$warnings}" . PHP_EOL;
echo "エラー件数: {$errors}" . PHP_EOL;
echo "ステータス: {$status}" . PHP_EOL;
if ($warnings > 0) {
echo PHP_EOL . "--- 詳細 ---" . PHP_EOL;
echo tidy_get_error_buffer($tidy) . PHP_EOL;
}
出力例:
警告件数 : 2
エラー件数: 0
ステータス: 1
--- 詳細 ---
line 2 column 3 - Warning: <img> lacks "alt" attribute
line 3 column 3 - Warning: <table> lacks "summary" attribute
6. 実践的なコード例
例1: 警告・エラーをまとめてレポートするクラス
<?php
class HtmlQualityReporter
{
private tidy $tidy;
private int $warnings;
private int $errors;
private int $access;
private string $errorBuffer;
public function __construct(string $html, array $config = [])
{
$this->tidy = tidy_parse_string($html, $config, 'UTF8');
$this->warnings = tidy_warning_count($this->tidy);
$this->errors = tidy_error_count($this->tidy);
$this->access = tidy_access_count($this->tidy);
$this->errorBuffer = tidy_get_error_buffer($this->tidy) ?: '';
}
public function getWarningCount(): int { return $this->warnings; }
public function getErrorCount(): int { return $this->errors; }
public function getAccessCount(): int { return $this->access; }
public function report(): void
{
$status = tidy_get_status($this->tidy);
$icon = match($status) { 0 => '✅', 1 => '⚠️ ', 2 => '❌', default => '?' };
echo "=== HTML 品質レポート ===" . PHP_EOL;
echo "{$icon} ステータス : {$status}" . PHP_EOL;
echo " 警告件数 : {$this->warnings}" . PHP_EOL;
echo " エラー件数 : {$this->errors}" . PHP_EOL;
echo " アクセシビリティ警告 : {$this->access}" . PHP_EOL;
if ($this->errorBuffer !== '') {
echo PHP_EOL . "--- 詳細メッセージ ---" . PHP_EOL;
echo $this->errorBuffer . PHP_EOL;
}
}
}
$html = '<html><body>
<img src="photo.jpg">
<table><td>セル</td></table>
<p>本文<b>太字</b></p>
</body></html>';
$reporter = new HtmlQualityReporter($html);
$reporter->report();
出力例:
=== HTML 品質レポート ===
⚠️ ステータス : 1
警告件数 : 2
エラー件数 : 0
アクセシビリティ警告 : 0
--- 詳細メッセージ ---
line 2 column 3 - Warning: <img> lacks "alt" attribute
line 3 column 3 - Warning: <table> lacks "summary" attribute
例2: OOP スタイルで warningCount() を使う
<?php
class TidyOopWarningChecker
{
private tidy $tidy;
public function __construct(string $html, array $config = [])
{
$this->tidy = new tidy();
$this->tidy->parseString($html, $config, 'UTF8');
}
public function getWarningCount(): int
{
return $this->tidy->warningCount();
}
public function getErrorCount(): int
{
return $this->tidy->errorCount();
}
public function describe(): void
{
$w = $this->getWarningCount();
$e = $this->getErrorCount();
echo "warningCount(): {$w}" . PHP_EOL;
echo "errorCount() : {$e}" . PHP_EOL;
echo "判定: " . match(true) {
$e > 0 => "❌ エラーあり({$e} 件)",
$w > 0 => "⚠️ 警告あり({$w} 件)",
default => "✅ 問題なし",
} . PHP_EOL;
}
}
$sources = [
'正常' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'警告あり' => '<html><body><img src="a.jpg"><table><td>x</td></table></body></html>',
'エラーあり'=> '<html><body><table><td><div>NG</div></td></body></html>',
];
foreach ($sources as $label => $html) {
echo "--- {$label} ---" . PHP_EOL;
(new TidyOopWarningChecker($html))->describe();
echo PHP_EOL;
}
出力例:
--- 正常 ---
warningCount(): 0
errorCount() : 0
判定: ✅ 問題なし
--- 警告あり ---
warningCount(): 2
errorCount() : 0
判定: ⚠️ 警告あり(2 件)
--- エラーあり ---
warningCount(): 1
errorCount() : 1
判定: ❌ エラーあり(1 件)
例3: 警告件数にしきい値を設けて合否判定するクラス
<?php
class HtmlWarningThresholdChecker
{
public function __construct(
private readonly int $maxWarnings = 0,
private readonly int $maxErrors = 0
) {}
public function check(string $label, string $html): bool
{
$tidy = tidy_parse_string($html, [], 'UTF8');
$warnings = tidy_warning_count($tidy);
$errors = tidy_error_count($tidy);
$pass = ($warnings <= $this->maxWarnings && $errors <= $this->maxErrors);
$icon = $pass ? '✅ PASS' : '❌ FAIL';
printf(
" %s %-18s warnings=%d/%d errors=%d/%d%s",
$icon,
$label,
$warnings, $this->maxWarnings,
$errors, $this->maxErrors,
PHP_EOL
);
return $pass;
}
public function checkAll(array $pages): array
{
echo "=== しきい値チェック(警告≦{$this->maxWarnings} / エラー≦{$this->maxErrors})===" . PHP_EOL;
$results = [];
foreach ($pages as $label => $html) {
$results[$label] = $this->check($label, $html);
}
$passed = count(array_filter($results));
$total = count($results);
echo PHP_EOL . "結果: {$passed}/{$total} 件 PASS" . PHP_EOL;
return $results;
}
}
$checker = new HtmlWarningThresholdChecker(maxWarnings: 1, maxErrors: 0);
$checker->checkAll([
'clean.html' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'one-warn.html' => '<html><body><img src="a.jpg"></body></html>',
'two-warn.html' => '<html><body><img src="a.jpg"><table><td>x</td></table></body></html>',
'error.html' => '<html><body><table><td><p>NG</td></table></body></html>',
]);
出力例:
=== しきい値チェック(警告≦1 / エラー≦0)===
✅ PASS clean.html warnings=0/1 errors=0/0
✅ PASS one-warn.html warnings=1/1 errors=0/0
❌ FAIL two-warn.html warnings=2/1 errors=0/0
❌ FAIL error.html warnings=0/1 errors=1/0
結果: 2/4 件 PASS
例4: 複数ページを一括検証して集計レポートを生成するクラス
<?php
class HtmlBatchQualityChecker
{
/** @var array<string, array{warnings: int, errors: int, access: int, status: int}> */
private array $results = [];
public function addAll(array $pages): self
{
foreach ($pages as $name => $html) {
$tidy = tidy_parse_string($html, [], 'UTF8');
$this->results[$name] = [
'warnings' => tidy_warning_count($tidy),
'errors' => tidy_error_count($tidy),
'access' => tidy_access_count($tidy),
'status' => tidy_get_status($tidy),
];
}
return $this;
}
public function printReport(): void
{
echo "=== 一括品質レポート ===" . PHP_EOL;
printf(" %-20s %7s %7s %7s %7s%s", "ファイル名", "warn", "error", "access", "status", PHP_EOL);
echo " " . str_repeat('-', 52) . PHP_EOL;
$totals = ['warnings' => 0, 'errors' => 0, 'access' => 0];
foreach ($this->results as $name => $r) {
$icon = match($r['status']) { 0 => '✅', 1 => '⚠️ ', 2 => '❌', default => '?' };
printf(
" %s %-18s %7d %7d %7d %7d%s",
$icon, $name,
$r['warnings'], $r['errors'], $r['access'], $r['status'],
PHP_EOL
);
$totals['warnings'] += $r['warnings'];
$totals['errors'] += $r['errors'];
$totals['access'] += $r['access'];
}
echo " " . str_repeat('-', 52) . PHP_EOL;
printf(
" %-20s %7d %7d %7d%s",
"合計",
$totals['warnings'],
$totals['errors'],
$totals['access'],
PHP_EOL
);
}
}
$checker = new HtmlBatchQualityChecker();
$checker->addAll([
'index.html' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'about.html' => '<html><body><img src="a.jpg"><p>About</p></body></html>',
'product.html' => '<html><body><table><td>A</td><td>B</td></table></body></html>',
'broken.html' => '<html><body><table><td><div>NG</div></td></body></html>',
])->printReport();
出力例:
=== 一括品質レポート ===
ファイル名 warn error access status
----------------------------------------------------
✅ index.html 0 0 0 0
⚠️ about.html 1 0 0 1
⚠️ product.html 2 0 0 1
❌ broken.html 1 1 0 2
----------------------------------------------------
合計 4 1 0
例5: 警告件数で品質スコアを算出するクラス
<?php
class HtmlQualityScorer
{
private int $warnings;
private int $errors;
private int $access;
public function __construct(string $html)
{
$tidy = tidy_parse_string($html, [], 'UTF8');
$this->warnings = tidy_warning_count($tidy);
$this->errors = tidy_error_count($tidy);
$this->access = tidy_access_count($tidy);
}
public function getScore(): int
{
// 100点から警告×3点・エラー×10点・アクセシビリティ×5点を減点
$score = 100
- ($this->warnings * 3)
- ($this->errors * 10)
- ($this->access * 5);
return max(0, $score);
}
public function getGrade(): string
{
return match(true) {
$this->getScore() >= 90 => 'A',
$this->getScore() >= 70 => 'B',
$this->getScore() >= 50 => 'C',
$this->getScore() >= 30 => 'D',
default => 'F',
};
}
public function report(): void
{
$score = $this->getScore();
$grade = $this->getGrade();
echo "=== 品質スコア ===" . PHP_EOL;
echo "警告 : {$this->warnings} 件 (-" . ($this->warnings * 3) . "点)" . PHP_EOL;
echo "エラー : {$this->errors} 件 (-" . ($this->errors * 10) . "点)" . PHP_EOL;
echo "アクセシビリティ : {$this->access} 件 (-" . ($this->access * 5) . "点)" . PHP_EOL;
echo "スコア : {$score} / 100" . PHP_EOL;
echo "グレード : {$grade}" . PHP_EOL;
}
}
$pages = [
'高品質ページ' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'軽微な問題' => '<html><body><img src="a.jpg"><table><td>x</td></table></body></html>',
'深刻な問題' => '<html><body><table><td><div>NG</div></td><td><div>NG2</div></td></body></html>',
];
foreach ($pages as $label => $html) {
echo PHP_EOL . "【{$label}】" . PHP_EOL;
(new HtmlQualityScorer($html))->report();
}
出力例:
【高品質ページ】
=== 品質スコア ===
警告 : 0 件 (-0点)
エラー : 0 件 (-0点)
アクセシビリティ : 0 件 (-0点)
スコア : 100 / 100
グレード : A
【軽微な問題】
=== 品質スコア ===
警告 : 2 件 (-6点)
エラー : 0 件 (-0点)
アクセシビリティ : 0 件 (-0点)
スコア : 94 / 100
グレード : A
【深刻な問題】
=== 品質スコア ===
警告 : 1 件 (-3点)
エラー : 2 件 (-20点)
アクセシビリティ : 0 件 (-0点)
スコア : 77 / 100
グレード : B
例6: 警告件数の推移をログで記録するクラス
<?php
class HtmlWarningTrendLogger
{
public function __construct(private readonly string $logFile) {}
public function log(string $label, string $html): void
{
$tidy = tidy_parse_string($html, [], 'UTF8');
$warnings = tidy_warning_count($tidy);
$errors = tidy_error_count($tidy);
$status = tidy_get_status($tidy);
$statusLabel = match($status) { 0 => 'OK', 1 => 'WARN', 2 => 'ERROR', default => 'UNKNOWN' };
$line = sprintf(
"[%s] %-20s | status=%-5s | warnings=%d | errors=%d\n",
date('Y-m-d H:i:s'),
$label,
$statusLabel,
$warnings,
$errors
);
file_put_contents($this->logFile, $line, FILE_APPEND);
echo $line;
}
public function summary(): void
{
if (!file_exists($this->logFile)) {
echo "ログファイルが存在しません。" . PHP_EOL;
return;
}
$lines = file($this->logFile, FILE_IGNORE_NEW_LINES) ?: [];
$totalWarn = 0;
$totalError = 0;
foreach ($lines as $line) {
if (preg_match('/warnings=(\d+)/', $line, $m)) {
$totalWarn += (int) $m[1];
}
if (preg_match('/errors=(\d+)/', $line, $m)) {
$totalError += (int) $m[1];
}
}
echo PHP_EOL . "=== ログサマリー ===" . PHP_EOL;
echo "記録件数 : " . count($lines) . " 件" . PHP_EOL;
echo "累計警告件数 : {$totalWarn}" . PHP_EOL;
echo "累計エラー件数 : {$totalError}" . PHP_EOL;
}
}
$logFile = sys_get_temp_dir() . '/tidy_warning_trend.log';
@unlink($logFile);
$logger = new HtmlWarningTrendLogger($logFile);
$logger->log('page-A', '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>');
$logger->log('page-B', '<html><body><img src="a.jpg"></body></html>');
$logger->log('page-C', '<html><body><img src="a.jpg"><table><td>x</td></table></body></html>');
$logger->summary();
unlink($logFile);
出力例:
[2026-07-29 12:00:00] page-A | status=OK | warnings=0 | errors=0
[2026-07-29 12:00:00] page-B | status=WARN | warnings=1 | errors=0
[2026-07-29 12:00:00] page-C | status=WARN | warnings=2 | errors=0
=== ログサマリー ===
記録件数 : 3 件
累計警告件数 : 3
累計エラー件数 : 0
例7: 警告・エラー・アクセシビリティを JSON レポートで出力するクラス
<?php
class HtmlQualityJsonReporter
{
/**
* @param array<string, string> $pages
* @return array<string, mixed>
*/
public function report(array $pages): array
{
$report = [];
$summary = ['total_warnings' => 0, 'total_errors' => 0, 'total_access' => 0, 'pages' => 0];
foreach ($pages as $name => $html) {
$tidy = tidy_parse_string($html, [], 'UTF8');
$warnings = tidy_warning_count($tidy);
$errors = tidy_error_count($tidy);
$access = tidy_access_count($tidy);
$status = tidy_get_status($tidy);
$report[$name] = [
'status' => $status,
'status_label' => match($status) { 0 => 'OK', 1 => 'WARN', 2 => 'ERROR', default => 'UNKNOWN' },
'warnings' => $warnings,
'errors' => $errors,
'access' => $access,
'error_buffer' => tidy_get_error_buffer($tidy) ?: null,
];
$summary['total_warnings'] += $warnings;
$summary['total_errors'] += $errors;
$summary['total_access'] += $access;
$summary['pages']++;
}
return [
'generated_at' => date('c'),
'summary' => $summary,
'pages' => $report,
];
}
}
$reporter = new HtmlQualityJsonReporter();
$result = $reporter->report([
'index.html' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'about.html' => '<html><body><img src="a.jpg"></body></html>',
'broken.html' => '<html><body><table><td><div>NG</div></td></body></html>',
]);
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
出力例:
{
"generated_at": "2026-07-29T12:00:00+09:00",
"summary": {
"total_warnings": 2,
"total_errors": 1,
"total_access": 0,
"pages": 3
},
"pages": {
"index.html": {
"status": 0,
"status_label": "OK",
"warnings": 0,
"errors": 0,
"access": 0,
"error_buffer": null
},
"about.html": {
"status": 1,
"status_label": "WARN",
"warnings": 1,
"errors": 0,
"access": 0,
"error_buffer": "line 1 column 14 - Warning: <img> lacks \"alt\" attribute"
},
"broken.html": {
"status": 2,
"status_label": "ERROR",
"warnings": 1,
"errors": 1,
"access": 0,
"error_buffer": "..."
}
}
}
7. 関連関数との比較
| 関数名 | 用途 | 戻り値 |
|---|---|---|
tidy_warning_count() | 警告の件数を取得 | int |
tidy_error_count() | エラーの件数を取得 | int |
tidy_access_count() | アクセシビリティ警告の件数を取得 | int |
tidy_get_status() | 問題有無のステータスコード(0/1/2)を取得 | int |
tidy_get_error_buffer() | 警告・エラーの詳細メッセージを取得 | string|false |
tidy_parse_string() | HTML 文字列を解析して tidy オブジェクト生成 | tidy|false |
8. よくある落とし穴と注意点
① tidy_clean_repair() 後は件数が変化することがある
tidy_clean_repair() を呼ぶと内部状態が更新され、警告・エラー件数が変わる場合があります。件数の確認は tidy_parse_string() 直後、tidy_clean_repair() の前に行うのが安全です。
$tidy = tidy_parse_string($html, [], 'UTF8');
$warnings = tidy_warning_count($tidy); // ← ここで取得(clean_repair 前)
tidy_clean_repair($tidy);
// clean_repair 後の件数は変わっている可能性がある
② 警告 0 件でも品質が完全に保証されるわけではない
Tidy が検出しない問題(セマンティクスの誤り・SEO 上の問題・独自仕様のエラー等)は件数に含まれません。あくまで Tidy の構文チェック範囲内の指標として捉えてください。
③ tidy_get_status() との使い分けを意識する
「問題があるかどうかだけ知りたい」なら tidy_get_status() のほうがシンプルです。件数そのものが必要な場合(しきい値チェック・スコア計算等)に tidy_warning_count() を使います。
// 有無だけ判定
if (tidy_get_status($tidy) > 0) { /* 問題あり */ }
// 件数が必要な場合
$count = tidy_warning_count($tidy);
if ($count > 5) { /* 警告が多すぎる */ }
④ アクセシビリティ警告は別カウンタ
tidy_warning_count() にはアクセシビリティ警告は含まれません。アクセシビリティの問題も把握したい場合は tidy_access_count() を別途呼び出してください。
9. まとめ
| 項目 | 内容 |
|---|---|
| 主な用途 | Tidy が検出した警告の件数を整数で取得する |
| 戻り値 | int(0 以上の整数、警告なしは 0) |
| OOP 版 | $tidy->warningCount() |
| エラー件数 | tidy_error_count() で別途取得 |
| アクセシビリティ件数 | tidy_access_count() で別途取得 |
| 詳細メッセージ | tidy_get_error_buffer() で取得 |
| 取得タイミング | tidy_clean_repair() の前が安全 |
| よく使う組み合わせ | tidy_error_count(), tidy_access_count(), tidy_get_status(), tidy_get_error_buffer() |
tidy_warning_count() は HTML バリデーションの品質を数値で定量化するための関数です。tidy_get_status() による有無の判定に加え、件数ベースのしきい値チェック・スコア算出・トレンドログ記録といった高度な品質管理の仕組みを構築する際に活用してください。
