Qiita / Kobito 風MarkdownをPHPでうまくHTMLに出力するTips

Qiita の Markdown では、

```php:inc/hoge.php

function hoge(){
    echo "hoge";
}
```

と書くと、

inc/hoge.php ←ここは強調される
function hoge(){
    echo "hoge";
}

のように表示されます。

しかしPHPMarkdownパーサーでこれを上手く表現できるものが見当たりませんでした。そこで PHP Markdown Extra で上記を表現するクイックハックをやってみたら上手くいったので、ここに載せておきます。

いろんなライブラリの中からこれを選択した理由は、DokuwikiMarkdown Extraプラグイン
plugin:markdownextra [DokuWiki]
の内部で使っているものがこれだったからです。つまり、Qiita用に書いたMarkdownをそのままDokuwikiに貼り付けてきちんと表示されるようにする、というのが今回のミッションです。が、Dokuwikiを使っていなくてもPHPで上記を実現したいという人は、参考になると思いますので試してみて下さい。あくまでクイックハックですので元々実装されているコードブロックのclassやattrへの実装関係は完全に無視し、Qiita方言にのみ対応しています。

PHP Markdown Extra の改造

  • まずは、PHP Markdown Extra からClassic version の PHP Markdown Extra 1.2.8 をダウンロードします。DokuwikiMarkdown Extraプラグイン内で使用しているのは Classic version なのでLib版でなくこちらをダウンロードします。
  • markdown.php の 2921行目からのdoFencedCodeBlocksと_doFencedCodeBlocks_callbackを以下のように書き換えます。
function doFencedCodeBlocks($text) {
#
# Adding the fenced code block syntax to regular Markdown:
#
# ~~~
# Code block
# ~~~
#
  $less_than_tab = $this->tab_width;
  
  $text = preg_replace_callback('{
      (?:\n|\A)
      # 1: Opening marker
      (
        (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
      )
      [ ]*
      (?:
        \.?([-_a-zA-Z0-9]+) # 2: lang
      )?
      (:.*|) #3: filename
    
      [ ]* \n # Whitespace and newline following marker.
      
      # 4: Content
      (
        (?>
          (?!\1 [ ]* \n)  # Not a closing marker.
          .*\n+
        )+
      )
      
      # Closing marker.
      \1 [ ]* (?= \n )
    }xm',
    array(&$this, '_doFencedCodeBlocks_callback'), $text);

  return $text;
}
function _doFencedCodeBlocks_callback($matches) {
  $lang =& $matches[2];
  $filename  = substr($matches[3],1);
  if (trim($filename)!="") {
  $filename = "<div class=\"code-lang\">$filename</div>";
}
  $codeblock = $matches[4];
  $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
  $codeblock = preg_replace_callback('/^\n+/',
    array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);

  $codeblock  = "<div class=\"code-frame\" data-lang=\"$lang\">$filename<div class=\"highlight\"><pre>$codeblock</pre></div></div>";
  
  return "\n\n".$this->hashBlock($codeblock)."\n\n";
}
  • 変更点はこれだけです。

テスト

  • テストするには以下のようなphpファイルを作成し実行してみてください。事前にテストしたいMarkdownをsample.mdとして保存しておいてください。
<?php
require_once 'markdown.php';
$text = file_get_contents ('sample.md');
echo markdown($text)

Dokuwikiへの組み込み

Dokuwikiへの組み込みは以下の手順です。

以上です。プラグインマネージャで「更新」を行うと元の実装に戻ってしまうので注意して下さい。

その他のTIPS

Dokuwikiでmarkdownextraプラグインを使った箇所にスタイルを適用したい場合は、lib/plugins/markdownextra/syntax.phpの38行目あたり

    function handle($match, $state, $pos, Doku_Handler $handler) {
        switch ($state) {
            case DOKU_LEXER_ENTER :      return array($state, '');
            case DOKU_LEXER_UNMATCHED :  return array($state, '<div class="markdown">'.Markdown($match).'</div>'); //ここ
            case DOKU_LEXER_EXIT :       return array($state, '');
        }
        return array($state,'');
    }

にdivをつけてクラスを指定して下さい。あとはテーマのcssにdiv.markdownのスタイルを記述すればOKです。

ご質問、ご意見などあればお気軽にコメントどうぞ。