1. 関数概要
tidy_parse_string は、PHP の Tidy 拡張が提供する HTML/XHTML/XML 文字列を直接受け取って解析する関数です。動的に生成した HTML・データベースから取得した HTML・API レスポンスのマークアップなど、ファイルを介さずにメモリ上の文字列を即座に Tidy で処理できます。Tidy を使う上で最も頻繁に呼び出す中心的な関数です。
| 項目 | 内容 |
|---|---|
| 関数名 | tidy_parse_string |
| 所属拡張 | Tidy |
| 戻り値の型 | tidy|false |
| 手続き型 / OOP | 手続き型(OOP 版: $tidy->parseString()) |
| PHP バージョン | PHP 5 以降 |
| 公式ドキュメント | https://www.php.net/manual/ja/tidy.parsestring.php |
2. 構文
// 手続き型
tidy_parse_string(
string $string,
array|string|null $config = null,
?string $encoding = null
): tidy|false
// オブジェクト指向型
$tidy->parseString(
string $string,
array|string|null $config = null,
?string $encoding = null
): bool
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
$string | string | 解析する HTML/XHTML/XML 文字列 |
$config | array|string|null | Tidy オプションの連想配列、または tidyrc 設定ファイルのパス。null でデフォルト設定 |
$encoding | string|null | 入出力エンコーディング(例: 'UTF8', 'latin1')。null でデフォルト(ascii) |
戻り値
| 呼び出し方 | 成功 | 失敗 |
|---|---|---|
| 手続き型 | tidy オブジェクト | false |
| OOP 型 | true | false |
3. 主なオプション一覧
$config に渡せる代表的な Tidy オプションです。
| オプション名 | 型 | デフォルト | 説明 |
|---|---|---|---|
indent | bool | false | インデントを付ける |
indent-spaces | int | 2 | インデントのスペース数 |
wrap | int | 68 | 折り返し幅(文字数)。0 で折り返しなし |
output-xhtml | bool | false | XHTML 形式で出力する |
output-xml | bool | false | XML 形式で出力する |
doctype | string | 'auto' | DOCTYPE の種類('html5', 'strict', 'loose', 'omit' 等) |
char-encoding | string | 'ascii' | 入出力文字エンコーディング |
drop-empty-elements | bool | true | 空要素を削除する |
clean | bool | false | 冗長なスタイルを整理する |
show-warnings | bool | true | 警告を表示する |
quiet | bool | false | 情報メッセージを抑制する |
4. 動作概念図
┌──────────────────────────────────────────────────────────┐
│ 入力: HTML 文字列(メモリ上) │
│ "<html><body><P>未閉じ<br></body>" │
└─────────────────────┬────────────────────────────────────┘
│ tidy_parse_string($string, $config, $encoding)
▼
┌──────────────────────────────────────────────────────────┐
│ Tidy 解析エンジン │
│ ・エンコード変換 ・DOCTYPE 検出 │
│ ・構文エラー収集 ・内部ツリー構築 │
└─────────────────────┬────────────────────────────────────┘
│
┌───────────┴───────────┐
成功 │ │ 失敗
▼ ▼
tidy オブジェクト false
│
▼ tidy_clean_repair()
修復・整形済み tidy
│
▼ tidy_get_output()
整形済み HTML 文字列(string)
5. 基本的な使い方
<?php
$html = '<html><body><P>未閉じタグ<br>テキスト</body>';
$tidy = tidy_parse_string($html, ['indent' => true, 'wrap' => 80], 'UTF8');
if ($tidy !== false) {
tidy_clean_repair($tidy);
echo tidy_get_output($tidy);
}
出力例:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>未閉じタグ<br>
テキスト</p>
</body>
</html>
6. 実践的なコード例
例1: HTML を整形して返すシンプルなクラス
<?php
class HtmlTidyFormatter
{
private array $config;
public function __construct(array $config = ['indent' => true, 'wrap' => 80])
{
$this->config = $config;
}
public function format(string $html): string|false
{
$tidy = tidy_parse_string($html, $this->config, 'UTF8');
if ($tidy === false) {
return false;
}
tidy_clean_repair($tidy);
return tidy_get_output($tidy);
}
}
$formatter = new HtmlTidyFormatter();
$dirty = '<HTML><BODY><H1>タイトル<P>本文<br>テキスト</P></BODY>';
$clean = $formatter->format($dirty);
echo $clean ?? '整形失敗';
出力例:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>タイトル</h1>
<p>本文<br>
テキスト</p>
</body>
</html>
例2: OOP スタイルで parseString() を使う
<?php
class TidyOopStringParser
{
private tidy $tidy;
public function __construct()
{
$this->tidy = new tidy();
}
public function parse(string $html, array $config = [], string $encoding = 'UTF8'): bool
{
$result = $this->tidy->parseString($html, $config, $encoding);
if ($result) {
$this->tidy->cleanRepair();
}
return $result;
}
public function getOutput(): string { return $this->tidy->value; }
public function getStatus(): int { return $this->tidy->getStatus(); }
public function isXhtml(): bool { return $this->tidy->isXhtml(); }
public function isXml(): bool { return $this->tidy->isXml(); }
}
$parser = new TidyOopStringParser();
$html = '<!DOCTYPE html><html><head><title>OOP テスト</title></head><body><p>本文</p></body></html>';
if ($parser->parse($html, ['indent' => true])) {
echo "ステータス: " . $parser->getStatus() . PHP_EOL;
echo "XHTML: " . var_export($parser->isXhtml(), true) . PHP_EOL;
echo $parser->getOutput();
}
出力例:
ステータス: 0
XHTML: false
<!DOCTYPE html>
<html>
<head>
<title>OOP テスト</title>
</head>
<body>
<p>本文</p>
</body>
</html>
例3: バリデーションと整形を分離するクラス
<?php
class HtmlValidator
{
private tidy $tidy;
private int $status;
private string $errorBuffer;
public function validate(string $html): self
{
$this->tidy = tidy_parse_string($html, [], 'UTF8');
$this->status = tidy_get_status($this->tidy);
$this->errorBuffer = tidy_get_error_buffer($this->tidy) ?: '';
return $this;
}
public function isValid(): bool { return $this->status === 0; }
public function hasWarning(): bool { return $this->status === 1; }
public function hasError(): bool { return $this->status === 2; }
public function repair(array $config = ['indent' => true]): string
{
$tidy = tidy_parse_string(
tidy_get_output($this->tidy), // 解析済み文字列を再利用
$config,
'UTF8'
);
tidy_clean_repair($tidy);
return tidy_get_output($tidy);
}
public function report(): void
{
$label = match($this->status) {
0 => '✅ 正常', 1 => '⚠️ 警告あり', 2 => '❌ エラーあり', default => '不明',
};
echo "ステータス: {$label}" . PHP_EOL;
if ($this->errorBuffer !== '') {
echo "詳細:" . PHP_EOL . $this->errorBuffer . PHP_EOL;
}
}
}
$validator = new HtmlValidator();
$validator->validate('<html><body><p>未閉じ<b>太字</body></html>');
$validator->report();
echo PHP_EOL . "=== 修復結果 ===" . PHP_EOL;
echo $validator->repair();
出力例:
ステータス: ⚠️ 警告あり
詳細:
line 1 column 26 - Warning: missing </b> before </body>
=== 修復結果 ===
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>未閉じ<b>太字</b></p>
</body>
</html>
例4: 複数の HTML を一括処理して結果を配列で返すクラス
<?php
class BulkHtmlProcessor
{
private array $config;
public function __construct(array $config = ['indent' => true, 'wrap' => 80])
{
$this->config = $config;
}
/**
* @param array<string, string> $pages
* @return array<string, array{status: int, output: string, error: string}>
*/
public function processAll(array $pages): array
{
$results = [];
foreach ($pages as $name => $html) {
$tidy = tidy_parse_string($html, $this->config, 'UTF8');
if ($tidy === false) {
$results[$name] = ['status' => -1, 'output' => '', 'error' => 'parse failed'];
continue;
}
tidy_clean_repair($tidy);
$results[$name] = [
'status' => tidy_get_status($tidy),
'output' => tidy_get_output($tidy),
'error' => tidy_get_error_buffer($tidy) ?: '',
];
}
return $results;
}
public function printSummary(array $results): void
{
echo "=== 一括処理サマリー ===" . PHP_EOL;
foreach ($results as $name => $r) {
$icon = match($r['status']) { 0 => '✅', 1 => '⚠️ ', 2 => '❌', default => '?' };
printf(" %s %-15s status=%d %d bytes%s",
$icon, $name, $r['status'], strlen($r['output']), PHP_EOL);
}
}
}
$processor = new BulkHtmlProcessor();
$pages = [
'index' => '<!DOCTYPE html><html><head><title>T</title></head><body><p>OK</p></body></html>',
'about' => '<html><body><p>警告あり<br></body></html>',
'contact' => '<HTML><BODY><TABLE><TD>セル</TD></TABLE></BODY></HTML>',
];
$results = $processor->processAll($pages);
$processor->printSummary($results);
出力例:
=== 一括処理サマリー ===
✅ index status=0 183 bytes
⚠️ about status=1 196 bytes
✅ contact status=0 207 bytes
例5: 動的生成 HTML をサニタイズして安全に出力するクラス
<?php
class SafeHtmlSanitizer
{
private readonly array $config;
public function __construct()
{
$this->config = [
'output-xhtml' => false,
'indent' => true,
'wrap' => 0, // 折り返しなし
'drop-empty-elements' => true,
'clean' => true, // 冗長スタイルを除去
'show-warnings' => false,
'quiet' => true,
];
}
public function sanitize(string $userInput): string
{
// ユーザー入力を一度エスケープしてから tidy に通す
$html = '<div>' . $userInput . '</div>';
$tidy = tidy_parse_string($html, $this->config, 'UTF8');
if ($tidy === false) {
return htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
}
tidy_clean_repair($tidy);
$output = tidy_get_output($tidy);
// body 内の内容だけを抽出
if (preg_match('/<body[^>]*>(.*?)<\/body>/si', $output, $m)) {
return trim($m[1]);
}
return $output;
}
}
$sanitizer = new SafeHtmlSanitizer();
$userInput = '<p onclick="alert(1)">テキスト</p><script>evil()</script><b>太字</b>';
echo $sanitizer->sanitize($userInput);
出力例:
<div>
<p>テキスト</p>
<b>太字</b>
</div>
例6: エンコーディングを変換しながら整形するクラス
<?php
class EncodingAwareFormatter
{
/**
* @param string $html 入力 HTML 文字列
* @param string $inputEnc Tidy エンコーディング名(例: 'latin1', 'UTF8', 'shiftjis')
* @param array $config 追加オプション
*/
public function format(string $html, string $inputEnc = 'UTF8', array $config = []): string
{
$mergedConfig = array_merge(
['indent' => true, 'wrap' => 80, 'char-encoding' => strtolower($inputEnc)],
$config
);
$tidy = tidy_parse_string($html, $mergedConfig, $inputEnc);
if ($tidy === false) {
return $html; // フォールバック: 元の文字列を返す
}
tidy_clean_repair($tidy);
// 現在のエンコーディング設定を確認
$enc = tidy_getopt($tidy, 'char-encoding');
echo "使用エンコーディング: {$enc}" . PHP_EOL;
return tidy_get_output($tidy);
}
}
$formatter = new EncodingAwareFormatter();
$html = '<html><body><p>UTF-8 テキスト:日本語サンプル</p></body></html>';
echo $formatter->format($html, 'UTF8');
出力例:
使用エンコーディング: utf8
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>UTF-8 テキスト:日本語サンプル</p>
</body>
</html>
例7: 解析結果を統合レポートとして返すクラス
<?php
readonly class TidyStringReport
{
public function __construct(
public string $output,
public int $status,
public string $statusLabel,
public bool $isXhtml,
public bool $isXml,
public int $htmlVersion,
public string $errorBuffer,
public string $release,
public int $outputBytes,
) {}
}
class TidyStringAnalyzer
{
public function analyze(string $html, array $config = []): TidyStringReport
{
$merged = array_merge(['indent' => true, 'wrap' => 80], $config);
$tidy = tidy_parse_string($html, $merged, 'UTF8');
if ($tidy === false) {
throw new \RuntimeException('tidy_parse_string() が失敗しました。');
}
tidy_clean_repair($tidy);
$status = tidy_get_status($tidy);
$output = tidy_get_output($tidy);
return new TidyStringReport(
output: $output,
status: $status,
statusLabel: match($status) { 0 => 'OK', 1 => 'WARN', 2 => 'ERROR', default => 'UNKNOWN' },
isXhtml: tidy_is_xhtml($tidy),
isXml: tidy_is_xml($tidy),
htmlVersion: tidy_get_html_ver($tidy),
errorBuffer: tidy_get_error_buffer($tidy) ?: '',
release: tidy_get_release(),
outputBytes: strlen($output),
);
}
public function printReport(TidyStringReport $r): void
{
echo "=== Tidy 解析レポート ===" . PHP_EOL;
echo "ステータス : {$r->status} ({$r->statusLabel})" . PHP_EOL;
echo "isXhtml() : " . var_export($r->isXhtml, true) . PHP_EOL;
echo "isXml() : " . var_export($r->isXml, true) . PHP_EOL;
echo "HTML バージョン: " . ($r->htmlVersion ?: 'HTML5/不明') . PHP_EOL;
echo "出力バイト数 : {$r->outputBytes} bytes" . PHP_EOL;
echo "libtidy : {$r->release}" . PHP_EOL;
echo "エラーバッファ : " . ($r->errorBuffer ?: '(なし)') . PHP_EOL;
echo PHP_EOL . "--- 整形済み出力 ---" . PHP_EOL;
echo $r->output;
}
}
$analyzer = new TidyStringAnalyzer();
$report = $analyzer->analyze(
'<html><body><p>統合テスト<br></p></body></html>',
['output-xhtml' => false]
);
$analyzer->printReport($report);
出力例:
=== Tidy 解析レポート ===
ステータス : 0 (OK)
isXhtml() : false
isXml() : false
HTML バージョン: HTML5/不明
出力バイト数 : 196 bytes
libtidy : 1st August 2023
エラーバッファ : (なし)
--- 整形済み出力 ---
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>統合テスト<br></p>
</body>
</html>
7. 関連関数との比較
| 関数名 | 入力 | 用途 | 戻り値 |
|---|---|---|---|
tidy_parse_string() | HTML 文字列 | 動的生成 HTML・API レスポンス等の解析 | tidy|false |
tidy_parse_file() | ファイルパス / URL | ファイルを直接読み込んで解析 | tidy|false |
tidy_clean_repair() | tidy オブジェクト | 解析済みドキュメントの修復・整形 | bool |
tidy_get_output() | tidy オブジェクト | 整形済み HTML を文字列で取得 | string |
tidy_get_status() | tidy オブジェクト | 解析ステータスを取得 | int |
tidy_get_error_buffer() | tidy オブジェクト | エラーバッファを取得 | string|false |
8. よくある落とし穴と注意点
① エンコーディングを指定しないと日本語が文字化けする
$encoding を省略するとデフォルトの ascii が使われ、マルチバイト文字が壊れることがあります。日本語を扱う場合は必ず 'UTF8' を指定してください。
// NG: エンコーディング未指定 → 日本語が文字化けする恐れ
$tidy = tidy_parse_string($html, ['indent' => true]);
// OK: UTF8 を明示
$tidy = tidy_parse_string($html, ['indent' => true], 'UTF8');
② tidy_clean_repair() を忘れると未修復のまま出力される
tidy_parse_string() は解析のみを行い、修復は行いません。tidy_get_output() の前に必ず tidy_clean_repair() を呼び出してください。
// NG: clean_repair なしで出力(未修復)
$tidy = tidy_parse_string($html, [], 'UTF8');
echo tidy_get_output($tidy); // 修復されていない可能性がある
// OK: clean_repair 後に出力
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
echo tidy_get_output($tidy);
③ OOP 版の戻り値は bool(tidy オブジェクトではない)
手続き型は tidy|false を返しますが、OOP 版 $tidy->parseString() の戻り値は bool です。解析結果は $tidy インスタンス自体に格納されます。
// 手続き型: 戻り値が tidy オブジェクト
$tidy = tidy_parse_string($html, [], 'UTF8');
// OOP 型: 戻り値は bool、$tidy 自身が結果を保持
$tidy = new tidy();
$ok = $tidy->parseString($html, [], 'UTF8'); // bool
if ($ok) { $tidy->cleanRepair(); }
④ エンコーディング名は Tidy 固有の表記を使う
PHP の mb_ 系関数とは異なる表記です。'UTF-8' ではなく 'UTF8'、'ISO-8859-1' ではなく 'latin1' のように指定します。
tidy_parse_string($html, [], 'UTF8'); // ✅ 正しい
tidy_parse_string($html, [], 'UTF-8'); // ⚠️ 認識されない場合がある
tidy_parse_string($html, [], 'latin1'); // ✅ 正しい
⑤ $config に空配列と null の違い
$config = [](空配列)は「オプションなし=すべてデフォルト値」を意味し、$config = null も同様です。ただし明示的に [] を渡す書き方のほうがコードの意図が明確になります。
9. まとめ
| 項目 | 内容 |
|---|---|
| 主な用途 | HTML/XHTML/XML 文字列を Tidy で解析する |
| 戻り値(手続き型) | tidy オブジェクト(成功)/ false(失敗) |
| 戻り値(OOP 型) | bool(成功/失敗) |
tidy_parse_file() との違い | 文字列を受け取る・URL 非対応・include_path 非対応 |
| エンコーディング | 'UTF8', 'latin1' など Tidy 固有の表記を使う |
| 必須の後続処理 | tidy_clean_repair() → tidy_get_output() |
| 基本3ステップ | tidy_parse_string() → tidy_clean_repair() → tidy_get_output() |
tidy_parse_string() は Tidy を使う際に最初に呼び出す最重要関数です。tidy_parse_string() → tidy_clean_repair() → tidy_get_output() の3ステップを基本パターンとして確実に習得し、バリデーション・一括処理・統合レポート生成など多様な用途に応用してください。
