1. 関数概要
tidy_repair_file は、PHP の Tidy 拡張が提供する HTML/XHTML ファイルの読み込みから修復・整形までを一度に行い、結果を文字列で返す関数です。tidy_parse_file() → tidy_clean_repair() → tidy_get_output() の3ステップを1回の呼び出しにまとめたショートカット関数であり、tidy オブジェクトを介さず直接整形済み文字列を得られます。
| 項目 | 内容 |
|---|---|
| 関数名 | tidy_repair_file |
| 所属拡張 | Tidy |
| 戻り値の型 | string|false |
| 手続き型 / OOP | 手続き型のみ(OOP 版なし) |
| PHP バージョン | PHP 5 以降 |
| 公式ドキュメント | https://www.php.net/manual/ja/tidy.repairfile.php |
2. 構文
tidy_repair_file(
string $filename,
array|string|null $config = null,
?string $encoding = null,
bool $useIncludePath = false
): string|false
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
$filename | string | 修復するファイルのパスまたは URL |
$config | array|string|null | Tidy オプションの連想配列、または tidyrc 設定ファイルのパス。null でデフォルト設定 |
$encoding | string|null | 入出力エンコーディング(例: 'UTF8', 'latin1')。null でデフォルト(ascii) |
$useIncludePath | bool | true にすると PHP の include_path からもファイルを検索する |
戻り値
| 結果 | 戻り値 |
|---|---|
| 成功 | 修復・整形済みの HTML 文字列(string) |
| 失敗 | false |
3. tidy_repair_file vs 3ステップ処理の比較
| 観点 | tidy_repair_file() | 3ステップ処理 |
|---|---|---|
| コード量 | 1行 | 3行以上 |
| tidy オブジェクト | 不要 | 必要 |
| ステータス取得 | ❌ 不可 | ✅ tidy_get_status() |
| エラーバッファ取得 | ❌ 不可 | ✅ tidy_get_error_buffer() |
| DOM ツリー走査 | ❌ 不可 | ✅ tidy_get_root() 等 |
| 向いている用途 | 結果文字列だけ欲しい場合 | 詳細な検査・分析が必要な場合 |
4. 動作概念図
┌──────────────────────────────────────────────────────────┐
│ ファイルシステム / URL │
│ /var/www/html/page.html │
└─────────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ tidy_repair_file($filename, $config, $encoding) │
│ │
│ 内部で自動実行: │
│ ① tidy_parse_file() ファイル読み込み・解析 │
│ ② tidy_clean_repair() 修復・整形 │
│ ③ tidy_get_output() 文字列化 │
└─────────────────────┬────────────────────────────────────┘
│
┌───────────┴───────────┐
成功 │ │ 失敗
▼ ▼
整形済み HTML 文字列 false
(string)
5. 基本的な使い方
<?php
// テスト用の不正な HTML ファイルを作成
$tmpFile = tempnam(sys_get_temp_dir(), 'tidy_') . '.html';
file_put_contents($tmpFile, '<HTML><BODY><H1>タイトル<P>本文<br>テキスト</BODY>');
// ワンステップで修復・整形した文字列を取得
$result = tidy_repair_file($tmpFile, ['indent' => true, 'wrap' => 80], 'UTF8');
if ($result !== false) {
echo $result;
} else {
echo "修復に失敗しました。" . PHP_EOL;
}
unlink($tmpFile);
出力例:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>タイトル</h1>
<p>本文<br>
テキスト</p>
</body>
</html>
6. 実践的なコード例
例1: ファイルを修復して別ファイルに保存するクラス
<?php
class HtmlFileRepairer
{
public function __construct(
private readonly array $config = ['indent' => true, 'wrap' => 80]
) {}
public function repairToFile(string $inputPath, string $outputPath): bool
{
$result = tidy_repair_file($inputPath, $this->config, 'UTF8');
if ($result === false) {
echo "❌ 修復失敗: {$inputPath}" . PHP_EOL;
return false;
}
$bytes = file_put_contents($outputPath, $result);
if ($bytes === false) {
echo "❌ 書き込み失敗: {$outputPath}" . PHP_EOL;
return false;
}
echo "✅ 修復完了: {$outputPath} ({$bytes} bytes)" . PHP_EOL;
return true;
}
public function repairInPlace(string $filepath): bool
{
return $this->repairToFile($filepath, $filepath);
}
}
$inputFile = tempnam(sys_get_temp_dir(), 'tidy_in_') . '.html';
$outputFile = tempnam(sys_get_temp_dir(), 'tidy_out_') . '.html';
file_put_contents($inputFile, '<html><body><p>未閉じ<b>太字</body></html>');
$repairer = new HtmlFileRepairer();
$repairer->repairToFile($inputFile, $outputFile);
echo file_get_contents($outputFile);
unlink($inputFile);
unlink($outputFile);
出力例:
✅ 修復完了: /tmp/tidy_out_XXXX.html (212 bytes)
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>未閉じ<b>太字</b></p>
</body>
</html>
例2: ファイルの存在確認とエラーハンドリングを備えたクラス
<?php
class SafeHtmlFileRepairer
{
private string $lastError = '';
public function repair(
string $filepath,
array $config = ['indent' => true],
string $encoding = 'UTF8'
): string|false {
if (!file_exists($filepath)) {
$this->lastError = "ファイルが存在しません: {$filepath}";
return false;
}
if (!is_readable($filepath)) {
$this->lastError = "ファイルを読み取れません: {$filepath}";
return false;
}
$result = tidy_repair_file($filepath, $config, $encoding);
if ($result === false) {
$this->lastError = "Tidy による修復に失敗しました: {$filepath}";
return false;
}
return $result;
}
public function getLastError(): string
{
return $this->lastError;
}
}
$repairer = new SafeHtmlFileRepairer();
// 存在しないファイル
$result = $repairer->repair('/nonexistent/page.html');
if ($result === false) {
echo "❌ " . $repairer->getLastError() . PHP_EOL;
}
// 正常なファイル
$tmpFile = tempnam(sys_get_temp_dir(), 'tidy_') . '.html';
file_put_contents($tmpFile, '<html><body><p>テスト</p></body></html>');
$result = $repairer->repair($tmpFile);
if ($result !== false) {
echo "✅ 修復成功 (" . strlen($result) . " bytes)" . PHP_EOL;
echo $result;
}
unlink($tmpFile);
出力例:
❌ ファイルが存在しません: /nonexistent/page.html
✅ 修復成功 (183 bytes)
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>テスト</p>
</body>
</html>
例3: ディレクトリ内の HTML ファイルを一括修復するクラス
<?php
class HtmlDirectoryRepairer
{
private int $successCount = 0;
private int $failCount = 0;
public function __construct(
private readonly array $config = ['indent' => true, 'wrap' => 80]
) {}
public function repairAll(string $inputDir, string $outputDir): void
{
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
$files = glob($inputDir . '/*.html') ?: [];
echo "対象ファイル数: " . count($files) . " 件" . PHP_EOL . PHP_EOL;
foreach ($files as $inputPath) {
$basename = basename($inputPath);
$outputPath = $outputDir . DIRECTORY_SEPARATOR . $basename;
$result = tidy_repair_file($inputPath, $this->config, 'UTF8');
if ($result === false) {
echo "❌ 失敗: {$basename}" . PHP_EOL;
$this->failCount++;
continue;
}
file_put_contents($outputPath, $result);
printf("✅ %-20s → %d bytes%s", $basename, strlen($result), PHP_EOL);
$this->successCount++;
}
echo PHP_EOL . "完了 — 成功: {$this->successCount} / 失敗: {$this->failCount}" . PHP_EOL;
}
}
// テスト用ファイルを作成
$inDir = sys_get_temp_dir() . '/tidy_repair_in';
$outDir = sys_get_temp_dir() . '/tidy_repair_out';
@mkdir($inDir);
file_put_contents("{$inDir}/index.html", '<html><body><h1>トップ</h1><p>本文</body>');
file_put_contents("{$inDir}/about.html", '<HTML><BODY><H2>概要</H2><P>テキスト</BODY>');
file_put_contents("{$inDir}/contact.html", '<html><body><form><input type=text></form></body></html>');
$repairer = new HtmlDirectoryRepairer();
$repairer->repairAll($inDir, $outDir);
// クリーンアップ
array_map('unlink', glob("{$inDir}/*.html") ?: []);
array_map('unlink', glob("{$outDir}/*.html") ?: []);
rmdir($inDir);
rmdir($outDir);
出力例:
対象ファイル数: 3 件
✅ about.html → 198 bytes
✅ contact.html → 215 bytes
✅ index.html → 195 bytes
完了 — 成功: 3 / 失敗: 0
例4: 修復前後のバイト数を比較表示するクラス
<?php
class HtmlRepairDiffReporter
{
public function report(string $filepath, array $config = ['indent' => true]): void
{
if (!file_exists($filepath)) {
echo "❌ ファイルが存在しません: {$filepath}" . PHP_EOL;
return;
}
$original = file_get_contents($filepath);
$repaired = tidy_repair_file($filepath, $config, 'UTF8');
if ($repaired === false) {
echo "❌ 修復に失敗しました。" . PHP_EOL;
return;
}
$origLines = substr_count($original, "\n") + 1;
$repairedLines = substr_count($repaired, "\n") + 1;
echo "=== 修復前後の比較 ===" . PHP_EOL;
printf("%-12s: %6d bytes / %3d 行%s", "修復前", strlen($original), $origLines, PHP_EOL);
printf("%-12s: %6d bytes / %3d 行%s", "修復後", strlen($repaired), $repairedLines, PHP_EOL);
printf("%-12s: %+d bytes%s", "差分",
strlen($repaired) - strlen($original), PHP_EOL);
echo PHP_EOL . "--- 修復後 ---" . PHP_EOL;
echo $repaired;
}
}
$tmpFile = tempnam(sys_get_temp_dir(), 'tidy_') . '.html';
file_put_contents($tmpFile, '<html><body><p>段落<b>太字</body>');
$reporter = new HtmlRepairDiffReporter();
$reporter->report($tmpFile);
unlink($tmpFile);
出力例:
=== 修復前後の比較 ===
修復前 : 33 bytes / 1 行
修復後 : 212 bytes / 13 行
差分 : +179 bytes
--- 修復後 ---
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>段落<b>太字</b></p>
</body>
</html>
例5: XHTML 形式で出力するクラス
<?php
class XhtmlFileConverter
{
private array $config = [
'output-xhtml' => true,
'indent' => true,
'wrap' => 80,
'char-encoding' => 'utf8',
];
public function convert(string $inputPath, string $outputPath): bool
{
$result = tidy_repair_file($inputPath, $this->config, 'UTF8');
if ($result === false) {
echo "❌ 変換失敗: {$inputPath}" . PHP_EOL;
return false;
}
file_put_contents($outputPath, $result);
$isXhtml = str_contains($result, 'xmlns="http://www.w3.org/1999/xhtml"');
echo "✅ XHTML 変換: " . ($isXhtml ? '成功' : '未確認') . " → {$outputPath}" . PHP_EOL;
return true;
}
}
$inputFile = tempnam(sys_get_temp_dir(), 'tidy_in_') . '.html';
$outputFile = tempnam(sys_get_temp_dir(), 'tidy_out_') . '.xhtml';
file_put_contents($inputFile, '<html><body><br><img src="photo.jpg"></body></html>');
$converter = new XhtmlFileConverter();
$converter->convert($inputFile, $outputFile);
echo file_get_contents($outputFile);
unlink($inputFile);
unlink($outputFile);
出力例:
✅ XHTML 変換: 成功 → /tmp/tidy_out_XXXX.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></title>
</head>
<body>
<br />
<img src="photo.jpg" alt="" />
</body>
</html>
例6: tidyrc 設定ファイルを使って一括修復するクラス
<?php
class TidyRcBatchRepairer
{
private string $rcPath;
public function __construct(array $options)
{
$this->rcPath = tempnam(sys_get_temp_dir(), 'tidyrc_');
$lines = [];
foreach ($options as $key => $value) {
$val = is_bool($value) ? ($value ? 'yes' : 'no') : (string) $value;
$lines[] = "{$key}: {$val}";
}
file_put_contents($this->rcPath, implode("\n", $lines));
}
/**
* @param string[] $filepaths
* @return array<string, string|false>
*/
public function repairAll(array $filepaths): array
{
$results = [];
foreach ($filepaths as $path) {
// $config に設定ファイルパス(string)を渡す
$results[basename($path)] = tidy_repair_file($path, $this->rcPath, 'UTF8');
}
return $results;
}
public function __destruct()
{
if (file_exists($this->rcPath)) {
unlink($this->rcPath);
}
}
}
// テスト用ファイルを作成
$files = [];
foreach (['page1' => '<html><body><p>A</body>', 'page2' => '<HTML><BODY><P>B</BODY>'] as $name => $html) {
$path = sys_get_temp_dir() . "/{$name}.html";
file_put_contents($path, $html);
$files[] = $path;
}
$repairer = new TidyRcBatchRepairer([
'indent' => true,
'wrap' => 100,
'quiet' => true,
]);
$results = $repairer->repairAll($files);
foreach ($results as $name => $output) {
echo "=== {$name} ===" . PHP_EOL;
echo $output !== false ? $output : "❌ 失敗" . PHP_EOL;
}
foreach ($files as $f) { unlink($f); }
出力例:
=== page1.html ===
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>A</p>
</body>
</html>
=== page2.html ===
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>B</p>
</body>
</html>
例7: 修復結果を JSON レポートとして出力するクラス
<?php
class HtmlFileRepairReporter
{
/**
* @param array<string, string> $files キー: ラベル / 値: ファイルパス
* @return array<string, mixed>
*/
public function report(array $files, array $config = ['indent' => true]): array
{
$report = [];
foreach ($files as $label => $path) {
$originalSize = file_exists($path) ? filesize($path) : null;
$result = tidy_repair_file($path, $config, 'UTF8');
$report[$label] = [
'path' => $path,
'original_bytes'=> $originalSize,
'repaired_bytes'=> $result !== false ? strlen($result) : null,
'success' => $result !== false,
'size_diff' => $result !== false && $originalSize !== null
? strlen($result) - $originalSize
: null,
];
}
return $report;
}
}
// テスト用ファイルを作成
$tmpFiles = [];
$defs = [
'正常な HTML' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'不完全な HTML' => '<html><body><table><td>セル</body>',
'大文字タグ' => '<HTML><BODY><H1>見出し</H1><P>本文</P></BODY></HTML>',
];
foreach ($defs as $label => $html) {
$path = tempnam(sys_get_temp_dir(), 'tidy_') . '.html';
file_put_contents($path, $html);
$tmpFiles[$label] = $path;
}
$reporter = new HtmlFileRepairReporter();
$report = $reporter->report($tmpFiles);
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
foreach ($tmpFiles as $path) { unlink($path); }
出力例:
{
"正常な HTML": {
"path": "/tmp/tidyXXXX.html",
"original_bytes": 87,
"repaired_bytes": 183,
"success": true,
"size_diff": 96
},
"不完全な HTML": {
"path": "/tmp/tidyXXXX.html",
"original_bytes": 35,
"repaired_bytes": 219,
"success": true,
"size_diff": 184
},
"大文字タグ": {
"path": "/tmp/tidyXXXX.html",
"original_bytes": 54,
"repaired_bytes": 195,
"success": true,
"size_diff": 141
}
}
7. 関連関数との比較
| 関数名 | 入力 | 戻り値 | tidy オブジェクト |
|---|---|---|---|
tidy_repair_file() | ファイルパス / URL | string|false | 不要・生成されない |
tidy_repair_string() | HTML 文字列 | string|false | 不要・生成されない |
tidy_parse_file() | ファイルパス / URL | tidy|false | 生成される |
tidy_parse_string() | HTML 文字列 | tidy|false | 生成される |
tidy_clean_repair() | tidy オブジェクト | bool | 必要 |
tidy_get_output() | tidy オブジェクト | string | 必要 |
使い分けのポイント: 「修復済みの HTML 文字列だけ欲しい」なら
tidy_repair_file()、「ステータス・エラー・DOM ツリーも検査したい」ならtidy_parse_file()+ 3ステップ処理を選んでください。
8. よくある落とし穴と注意点
① ステータスやエラーバッファは取得できない
tidy_repair_file() は tidy オブジェクトを返さないため、tidy_get_status() や tidy_get_error_buffer() を呼び出せません。警告・エラーの詳細が必要な場合は tidy_parse_file() を使ってください。
// NG: tidy オブジェクトが存在しない
$result = tidy_repair_file('page.html', [], 'UTF8');
tidy_get_status($result); // $result は string|false なので使えない
// OK: 詳細が必要なら tidy_parse_file() を使う
$tidy = tidy_parse_file('page.html', [], 'UTF8');
tidy_clean_repair($tidy);
$status = tidy_get_status($tidy);
$output = tidy_get_output($tidy);
② ファイルの上書きは戻り値を file_put_contents() で書き込む
tidy_repair_file() は修復後の文字列を返すだけでファイルを自動上書きしません。元のファイルを修復済み内容に置き換えたい場合は file_put_contents() と組み合わせてください。
// 元ファイルをその場で修復・上書き
$repaired = tidy_repair_file($path, ['indent' => true], 'UTF8');
if ($repaired !== false) {
file_put_contents($path, $repaired);
}
③ エンコーディング名は Tidy 固有の表記を使う
'UTF-8' ではなく 'UTF8'、'ISO-8859-1' ではなく 'latin1' のように Tidy 固有の表記が必要です。
tidy_repair_file('page.html', [], 'UTF8'); // ✅ 正しい
tidy_repair_file('page.html', [], 'UTF-8'); // ⚠️ 認識されない場合がある
④ OOP 版は存在しない
tidy_repair_file() には OOP 版($tidy->repairFile() 相当)がありません。OOP スタイルで同等の処理を行う場合は $tidy->parseFile() + $tidy->cleanRepair() + $tidy->value を使ってください。
// OOP スタイルで同等の処理
$tidy = new tidy();
$tidy->parseFile('page.html', ['indent' => true], 'UTF8');
$tidy->cleanRepair();
$repaired = $tidy->value;
⑤ URL を渡す場合は allow_url_fopen が必要
$filename に URL を指定するには php.ini で allow_url_fopen = On が必要です。
9. まとめ
| 項目 | 内容 |
|---|---|
| 主な用途 | HTML ファイルをワンステップで修復・整形して文字列を得る |
| 戻り値 | string(修復済み HTML)/ false(失敗) |
| OOP 版 | なし |
| tidy オブジェクト | 生成されない(ステータス・エラー取得不可) |
| ファイル上書き | 自動では行わない(file_put_contents() で明示的に書き込む) |
| エンコーディング | 'UTF8', 'latin1' など Tidy 固有の表記を使う |
| 詳細な検査が必要なとき | tidy_parse_file() の3ステップ処理に切り替える |
tidy_repair_file() は「修復済みの HTML 文字列さえ得られればよい」場面でコードを大幅に簡略化できる便利なショートカット関数です。エラー検査や DOM 走査が不要なバッチ整形・ファイル変換処理に積極的に活用してください。詳細な解析が必要になった場合は tidy_parse_file() ベースの3ステップ処理へ移行する判断基準も覚えておくと便利です。
