WordPress.com → Hostinger 移行後のコンテンツ修正メモ

ℹ️ この記事は Claude Code で書きました。

WordPress.com (Atomic) → Hostinger 移行手順メモで書いたインフラ・DB 移行が終わった後、実際に記事を読み進めると見つかる「WordPress.com 専用機能への暗黙の依存」と、その直し方をまとめる。いずれも移行直後は見た目上動いているように見えて、細部で壊れているタイプの問題。

前提

WordPress.com の Atomic サイトは、コアの WordPress に加えて JetpackCoBlocks、Site Helper (wpcomsh) など多数のプラグインが「常に有効な状態」で動いている。これらの一部は WordPress.com 基盤専用の実装 (自前ホスティングでは動かない) で、一部は単に「たまたま入っていた OSS プラグイン」でしかない。移行後に表示が崩れている箇所を見つけたら、まずどちらのタイプかを切り分ける必要がある。

1. コードブロックのシンタックスハイライトが無い

WordPress.com のコードブロックには自動でシンタックスハイライトが付くが、これは公開配布されている Jetpack プラグインには含まれていない WordPress.com 独自のエディタ拡張。自前ホスティングで同じ体験を得るには、別のプラグインを入れる必要がある。

代替候補:

  • Syntax-highlighting Code Block — 標準の「コード」ブロック (core/code) 自体を拡張するタイプ。highlight.php (highlight.js の PHP 移植) でサーバーサイドレンダリングする
  • Enlighter — 独自ブロックを追加するタイプ。テーマ選択 UI が充実
wp plugin install syntax-highlighting-code-block --activate
Code language: Bash (bash)

👀 見た目のカスタマイズ

このプラグインは highlight.js の default.css を固定で読み込む (プラグイン内に配色選択 UI は無い)。好みの配色にしたい場合は、追加 CSS で .hljs-* クラスを上書きする。GitHub 風の配色 (ghcolors/github テーマ) が候補になる。

はまりやすい点:

  • 実際のブロック要素は pre.wp-block-code で、中に code.hljs が入っている。CSS セレクタをこの構造に正確に合わせないと、書いたルールが一致せず何も変わらない
  • テーマ側が生の pre 要素に直接ダークな背景色を指定している場合、詳細度で負けて上書きされる。pre.wp-block-code のようにクラスを含めたセレクタで指定し直す
  • 行番号と本文の境界線は .hljs.shcb-line-numbers .shcb-loc::beforeborder-right が担当している

2. 過去記事のコードブロックが大文字小文字で壊れる

wp:code ブロックの language 属性に "JavaScript", "HTML", "CSS" のように 大文字始まり の値が入っていると、内部で使っている highlight.php が Unknown language の例外を投げ、プラグインがエラーを握りつぶして <pre><code> の外側ごと丸ごと消し、生テキストをそのまま出力してしまう。

wp eval '
require WP_PLUGIN_DIR . "/syntax-highlighting-code-block/vendor/autoload.php";
$hl = new \Highlight\Highlighter();
try { $hl->highlight("HTML", "test"); echo "OK\n"; }
catch (\Exception $e) { echo "ERROR: " . $e->getMessage() . "\n"; }
'
Code language: Bash (bash)

language は必ず小文字 (html, javascript, css 等) でなければならない。全記事の wp:code ブロックを洗い出し、大文字が混じっている値だけ小文字化する。

wp eval '
$ids = [/* 対象記事ID */];
$replacements = [
  "\"language\":\"HTML\""       => "\"language\":\"html\"",
  "\"language\":\"JavaScript\"" => "\"language\":\"javascript\"",
  "\"language\":\"CSS\""        => "\"language\":\"css\"",
];
foreach ($ids as $id) {
  $post = get_post($id);
  $new = strtr($post->post_content, $replacements);
  if ($new !== $post->post_content) {
    wp_update_post(["ID" => $id, "post_content" => $new]);
  }
}
'
Code language: Bash (bash)

自動判定 (language 未指定) に頼っている記事は、判定が微妙に外れることがある (例: Emacs Lisp のコードが php誤判定される)。表示は壊れないが、気になる場合は正しい言語スラッグを手動指定する。

3. 廃止プラグインのコードブロックを一括変換する

WordPress.com 上で昔使っていた Syntax Highlighter Evolved 由来のブロック (wp:syntaxhighlighter/code) は、プラグイン本体が無くても静的 HTML として保存されているため表示自体は壊れないが、ハイライトも行番号も付かない素の <pre> のまま。

まず該当記事数を確認する。

wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_status='publish' AND post_content LIKE '%wp:syntaxhighlighter/code%'"
Code language: Bash (bash)

parse_blocks/serialize_blocks でブロックタイプを core/code に変換し、language 属性・行番号表示を引き継ぐ。

$blocks = parse_blocks($post->post_content);
foreach ($blocks as &$block) {
  if ($block['blockName'] === 'syntaxhighlighter/code') {
    $lang = strtolower($block['attrs']['language'] ?? '');
    preg_match('/<pre class="wp-block-syntaxhighlighter-code[^"]*">(.*)<\/pre>/s', $block['innerHTML'], $m);
    $code = $m[1];
    $block['blockName'] = 'core/code';
    $block['attrs'] = $lang ? ['language' => $lang, 'showLineNumbers' => true] : ['showLineNumbers' => true];
    $new_html = '<pre class="wp-block-code"><code>' . $code . '</code></pre>';
    $block['innerHTML'] = $new_html;
    $block['innerContent'] = [$new_html];
  }
}
wp_update_post(['ID' => $id, 'post_content' => serialize_blocks($blocks)]);
Code language: PHP (php)

はまりやすい点:

  • 旧プラグインの class 属性に brush: bash; notranslate のような余計な文字列が付いていることがあり、正規表現を class="wp-block-syntaxhighlighter-code[^"]*" のように緩めておかないと一部の記事で抽出に失敗する
  • language の値に jscript のような非標準の表記が使われていることがある。highlight.php は認識しないので、javascript にエイリアスしておく

4. wp_update_post() がバックスラッシュを消す

PHP スクリプトで post_content にバックスラッシュを含む文字列 (LaTeX の \frac{}{} など) をセットして wp_update_post() で保存すると、保存時にバックスラッシュが消える

これは WordPress コアが $_POST 経由の入力を前提にしており、マジッククォート相当のエスケープが必要という古い設計を引きずっているため。API 経由で直接データを渡す場合は、明示的に wp_slash() を通す必要がある。

// NG: バックスラッシュが消える
wp_update_post(['ID' => $id, 'post_content' => $new_content]);

// OK
wp_update_post(wp_slash(['ID' => $id, 'post_content' => $new_content]));
Code language: Bash (bash)

一度バックスラッシュ無しで保存してしまった場合、元の文字列を「バックスラッシュを除去した形」に変換してから検索・置換し、wp_slash() を通して保存し直せば復旧できる。ただし短い置換パターン ((n-1) など) が別の長い置換対象の内部に偶然含まれていると、strtr() の一括置換で意図しない二重置換が起きることがあるので、置換後は必ず実データを確認する。

5. WordPress.com 専用の LaTeX 記法 ($latex ... $)

WordPress.com には $latex <数式>$ と書くと自動でレンダリングしてくれる独自ショートコードがある (Jetpack Beautiful Math 由来)。自前ホスティングではこの記法は認識されず、生テキストのまま表示される。

まず該当箇所を検索する。

wp db query "SELECT ID FROM wp_posts WHERE post_content LIKE '%\$latex%'"
Code language: Bash (bash)

一般的な MathJax/KaTeX 系プラグイン (今回は Simple MathJax) は \( ... \) / \[ ... \] 形式のみを認識する。$latex <数式>&s=<サイズ>&bg=<背景色>$ という WordPress.com 独自のパラメータ付き記法を、素の LaTeX + \( \) に変換する。

はまりやすい点:

  • WordPress.com の古いエディタ経由で保存された数式は、バックスラッシュ自体が最初から抜け落ちている ことがある (\fracfrac になっているなど)。これは移行作業由来ではなく、WordPress.com 側で元々失われていたデータ。文脈から復元できる場合は手動で補う (前後の数式や説明文から数式の意味を推測する)
  • 変換後の保存でも当然 4. の wp_slash() 問題 が起きるので注意

6. CoBlocks 依存ブロックの置き換え

WordPress.com 上で CoBlocks (GoDaddy 系の Gutenberg 拡張プラグイン) のブロックを使っていることがある。プラグイン本体が無いと編集画面で「認識されないブロック」警告が出る。

対象ブロックの洗い出し:

wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_status='publish' AND post_content LIKE '%coblocks%'"
Code language: Bash (bash)
preg_match_all('/<!-- (\/?wp:coblocks[a-zA-Z0-9\/_-]*)/', $post->post_content, $m);
Code language: Bash (bash)

🗡️ 区切り線 (coblocks/dynamic-separator)

中身が <hr class="wp-block-coblocks-dynamic-separator"/> という静的 HTML なので、プラグイン無しでも表示は壊れていない。WordPress コアの区切りブロック (core/separator) は is-style-dots という同名のスタイルクラスをネイティブでサポートしているため、ブロックタイプだけ差し替えれば移行できる。

🪗 アコーディオン (coblocks/accordion)

ブラウザネイティブの <details>/<summary> 要素で実装されているため、CSS の装飾を除けば プラグイン無しでも開閉機能自体は動作する。WordPress コアには専用の代替ブロックが無いので、render_block() で実際の描画結果をそのままキャプチャし、core/html (生 HTML ブロック) として固定化するのが手堅い。手作業でマークアップを組み直すより、既存のレンダリング結果をそのまま使う方が安全。

$rendered_html = render_block($block);
$block['blockName'] = 'core/html';
$block['attrs'] = [];
$block['innerHTML'] = $rendered_html;
$block['innerContent'] = [$rendered_html];
Code language: PHP (php)

LaTeX を含むアコーディオンの中身を変換する場合は、ここでも 4. の wp_slash() 問題 が起きるので注意。

7. スパム対策

WordPress.com には Akismet 相当のスパムフィルタが標準で付いてくるが、自前ホスティングでは別途導入が必要。

Akismet 自体は無料でも使えるが、個人・非商用サイト向けの Personal プラン ($0) が前提の規約になっており、サイトに広告を貼っている (= 商用利用とみなされる) 場合は有料の Business プランが必要になる

広告を貼っているサイトで無料に抑えたい場合は、商用利用の制限が無い代替プラグイン (Antispam Bee 等) を使う。判定方式は Akismet のようなクラウド機械学習ではなく、正規表現・ローカルスパムデータベース・投稿速度などのローカルなヒューリスティックの組み合わせ。精度は落ちるが実用上は十分機能する。

日本語オンリーのブログであれば、「特定の言語のみ許可」設定で日本語以外の言語のコメントを弾くのが効果的だが、プラグインによっては言語選択肢に日本語が用意されていないこともある。その場合は既定のフィルタ (正規表現・ローカルDB・BBCode検出等) のまま運用し、様子を見て国別フィルタの追加を検討する。