レ点腫瘍学ノート

Toppukiwikiカスタマイズ箇所2026

PukiwikiにMarkdownをlibディレクトリに触れずに実装した

pukiwiki markdown AI

4b3622e9ab.png

ぼくが運営しているPukiWikiサイトでは、以前からMarkdown記法でページを書けるように改造を加えてきました。その成果はpukiwiki154_mdとしてGitHubで公開しています。ただ、この方式はPukiWikiのlibフォルダ内のファイルを直接書き換えるものだったため、本体のバージョンが上がるたびに改造をやり直さなければならない欠点がありました。今回、pluginフォルダへのファイル追加・差し替えだけでMarkdown対応を完結させる方式に作り直し、pukiwiki154_md2としてv0.1を公開しました。

GitHub - m0370/pukiwiki154_md2: PukiWiki 1.5.4 Markdown対応プラグイン(pluginフォルダ完結型・lib/skin無改造)
PukiWiki 1.5.4 Markdown対応プラグイン(pluginフォルダ完結型・lib/skin無改造) - m0370/pukiwiki154_md2
https://github.com/m0370/pukiwiki154_md2

なぜ作り直すことにしたのか

従来のpukiwiki154_mdでは、lib/convert_html.phpをはじめとして4ファイルに手を入れていました。動くものはできましたし、実際に運用もできています。しかしPukiWiki 1.5.5が出たら、改造箇所を一つずつ思い出しながら新しいlibに同じ手術を施す作業が待っています。さらに、lib改造版で書きためたページデータは素のPukiWikiへ持っていくと表示が崩れ、「データが改造版に縛られている」状態も気になっていました。Wikiというのは何年も使い続けるものなので、特定の改造版に依存した状態はいつか精算したいと思っていました。

pluginフォルダだけで実現できた仕組み

新しい方式の土台になっているのは、PukiWikiのページ表示がplugin/read.inc.phpという標準プラグインで実装されているという構造です。lib/pukiwiki.phpには次のような分岐があります。

if (isset($retvars['body']) && $retvars['body'] != '') {
    $body = & $retvars['body'];   // プラグインが返したHTMLをそのまま採用
} else {
    $body = convert_html(get_source($base));  // 通常のPukiWiki変換
}

標準のread.inc.phpは空のbodyを返すため通常はconvert_html()による変換に進みますが、空でないHTMLを返せば本体はそれをそのまま表示します。ページのソースに#mdがあればMarkdown変換したHTMLを返し、なければ空を返す——read.inc.phpへの12行の追加だけで、libに一切触れずにページ描画を切り替えられます。編集画面のプレビューも同様に、edit.inc.phpが内部でconvert_html()を呼んでいるためpluginフォルダ内で対応が完結します。Markdown変換エンジン本体は新規のmd.inc.phpに集約し、パーサーのleague/commonmarkはplugin/markdown_parser/として同梱しました。

実装で詰まったポイント

保存時の罠が一番大きな問題でした。PukiWikiはページを保存するとき、make_str_rules()という関数で本文を自動整形します。代表的なのが*で始まる行の末尾へのアンカー自動付与で、PukiWiki記法では*は見出しなので便利な機能です。しかしMarkdownでは* 項目はリストです。何も対策しないと、Markdownのリストを保存するたびに行末へアンカーが付いて本文が壊れていきます。#mdのあるページを保存するときだけ$str_rules$fixed_heading_anchorを一時的に無効化することで回避しました。PukiWiki記法ページの保存は従来通り動作することも確認しています。

改行の扱いも調整しました。CommonMark仕様では行末にスペースを2つ置かないと改行が反映されませんが、Wikiのようにさっと書く場面では行末スペースはまず忘れます。league/commonmarkの設定で、単純な改行がそのまま<br>になるようにしています。

Markdownの中で使えるもの

GitHub Flavored Markdown(テーブル、打ち消し線、タスクリスト)に加え、次のものがMarkdownページ内で使えます。

設定(任意)

pukiwiki.ini.phpに設定を追記することで挙動を変更できます(追記しなくてもデフォルトで動作します)。主なものは$use_markdown_cache(変換結果キャッシュ、デフォルト有効)、$default_md(新規ページ作成時に#mdを自動挿入、デフォルト有効)、$markdown_support_hash_plugin(#plugin記法を許可、デフォルト無効)です。キャッシュが有効なとき、!pcomment46のようにページ本文を変えずに表示だけが変わるプラグインは、キャッシュ期間中は更新が反映されません。そういったプラグインを多用するページでは$use_markdown_cache = 0を検討してください。詳しくはGitHubのREADME.mdをご覧ください。

後方互換性を検証した

今回の方式で一番確認したかったのは後方互換性です。プラグインのない素のPukiWiki 1.5.4を別ポートに立てて、#mdのページデータをそのまま持ち込んで表示させてみました。結果はHTTP 200でエラーは一切出ません。#mdの行が文字としてそのまま表示され、本文はPukiWiki記法として解釈された状態になります。書式は失われますが内容は読めます。逆に言えば、このプラグインを撤去してもWikiは壊れず、データを別の素のPukiWikiへ引っ越してもエラーは発生しません。lib/・skin/・pukiwiki.ini.phpはPukiWiki 1.5.4公式とdiff差分ゼロなので、改造版への依存から抜け出せた感覚があります。

制限と留意点

現在のv0.1には注意点が2点あります。まず、!commentなどのコメント系プラグイン経由で保存が行われると、edit.inc.phpの整形抑止が適用されないため、Markdownの*始まり行の末尾にアンカーが書き込まれる場合があります。通常は表示時に除去されるので見た目には現れませんが、フェンスコードブロック内の行は除去対象外です。コメント系プラグインを多用するページでは気になる場合があるので、その際は該当プラグインにも同様の抑止処理を入れる必要があります。次に、#contentsの引数(depth指定等)は現在無視され、常に全階層の目次が出力されます。生成されるアンカーはPukiWiki標準の[#xxxx]ではなくcommonmarkのパーマリンクID(例: #content-概要)になるため、他ページから特定見出しへリンクする際はIDを確認してください。

開発の進め方について

今回の開発はAnthropic社のClaude Code Fable 5と一緒に進めました。最初にぼくが提案したのはページ全体を#md{{ }}で囲う方式で、Fable 5はその実現可能性を検証したうえで、read.inc.phpの差し替えで済ませる方が筋がよいという代案を出してきました。保存時のアンカー付与でMarkdownが壊れる問題も、実装前の調査段階で指摘されています。仕様の判断(フラグ名の変更、既存データとの互換性など)はやはり運用してきた人間にしかできませんが、設計検討にAIを使うのはコードを書かせるのと同じくらい価値があると感じました。半日ほどでv0.1の公開まで辿り着けたのは、その恩恵が大きいと思っています。

インストールはpluginフォルダの中身をコピーするだけです。プラグイン一式とインストール手順はpukiwiki154_md2に置いてあります。まずはぼく自身のWikiで運用して、v0.1の粗を洗い出すところからです。

GitHub - m0370/pukiwiki154_md2: PukiWiki 1.5.4 Markdown対応プラグイン(pluginフォルダ完結型・lib/skin無改造)
PukiWiki 1.5.4 Markdown対応プラグイン(pluginフォルダ完結型・lib/skin無改造) - m0370/pukiwiki154_md2
https://github.com/m0370/pukiwiki154_md2

この記事に対するコメント

このページには、まだコメントはありません。

お名前:

更新日:2026-06-11 閲覧数:46 views.