WordPress

【PHP】highlight.phpでプログラムコードをハイライト表示する

更新日:2023/02/10

Webページ上のプログラムコードはハイライト表示すると、見やすくなります。
ハイライト表示はプログラムコードを解析して、spanタグを挿入します。

この処理はサーバー側で行うケースと、ブラウザ上で行うケースがあります。
どちらもメリットデメリットがありますが、今回はhighlight.phpを使用してサーバー側で行う方法をお伝えします。

 

highlight.phpのインストール

highlight.phpのインストールはcomposerが必要です。
次のcomposerのホームページを見て、インストールしましょう。

要約

  • Windowsの場合:Windows Installerをダウンロードして実行します
  • その他:Command-line installationの4つのPHPコマンドを実行します

次に、composerを使ってhighlight.phpをインストールします。
次のコマンドを実行しましょう。

composer require scrivo/highlight.php
レンタルサーバー等でcomposerをインストールできない時は?

次のページからhighlight.phpのソースコードをダウンロードしてローカルで展開します。

https://github.com/scrivo/highlight.php/releases

そしてサーバーにアップロードします。

highlight.phpは依存関係でjsonmbstringが必要なので、クリアできていれば動作するはず…
保証はしません。

 

highlight.phpのcssファイルをダウンロード

次にスタイルファイルをダウンロードします。

次のリンク先のDownloadボタンを押します。

ダウンロードされたファイルを解凍して、stylesディレクトリをサーバーにアップロードします。

スタイルの適用イメージは、次のページで確認できます。

 

使い方

highlight.phpを読み込む

composerでインストールすると、composerをインストールしたディレクトリに/vendor/autoload.phpがあるので、読み込みます。

require ( dirname(__FILE__) . '/vendor/autoload.php');
use Highlight\Highlighter;
composerでインストールしていないときは?

アップロードしたhighlight.php内のAutoloader.phpを読み込みます。

require('Highlight/Autoloader.php`);
spl_autoload_register('\\Highlight\\Autoloader::load');

動作未確認です!

解析する

入力データは、言語の生のコードである必要があります。
Web表示を考慮してエスケープしてある場合は、デコードしておきます。

その後、highlightAuto()で解析します。

$input = 'function(){ /* 何らかのコード */ }';

$highlighter = new Highlighter();
$input = html_entity_decode($input,ENT_QUOTES | ENT_HTML5);
$highlighter->setAutodetectLanguages([  // 言語の自動判別候補をセット
              'html',
              'php',
              'css',
              'shell',
              'javascript',
              'perl'
]);

$result = $highlighter->highlightAuto($input);
$output = $result->value;  // 結果
$class = $result->language; // 言語
echo '<pre class="hljs '.$class.'">'.$output.'</pre>';

言語が確定しているときは、highlight()を使用することもできます。

$result = $highlighter->highlight( 'javascript' , $input);

cssを読み込む

headタグ内等で、スタイルを読み込むlinkタグを出力します。

<link rel="stylesheet" type="text/css" href="/css/night-owl.css">

 

aタグやspanタグを残す

Web表示したときに、コード内にaタグでリンクを挿入したり、ハイライトとは別にspan等で個別にスタイルを設定したいケースがあります。

しかし解析結果は次のように、タグがタグとして機能しないように各パーツがspanタグで囲まれます。

<span class="red">

<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"red"</span>&gt;</span>

その結果、タグがそのまま文字として表示されてしまいます。

ですが、無理やりaタグやspanタグを残してみます。

クラス名にhljsignoreがあるものは、そのまま残るようにします。
次のようなコードなら、2行目がそのままタグとして残ります。

<span class="red">hello!</span>
<span class="red hljsignore">hello!</span>
  1. 対象タグを適当な文字列に置き換える

    次のコードは、hljsignoreクラスを持っているaおよびspanタグを、変数$replace_tagで置き換えています。
    置き換え前のタグは、変数$tag_mapにセットしています。

    $igonore_class = 'hljsignore';
    $ignore_tags = 'a|span';
    $pattern = '#(?P<s><('.$ignore_tags.') .*?class=".*?'.$igonore_class.'.*?".*?>)(?P<b>.*?)(?P<e></(\2)>)#';
    
    $tag_map = array();
    $replace_tag = '/*____TAGFOLDER____*/';
    
    $input = preg_replace_callback( $pattern , function ($matches) use(&$tag_map,$replace_tag){
        $tag_map[] = $matches['s'];
        $tag_map[] = $matches['e'];
    
        return $replace_tag.$matches['b'].$replace_tag;
    }, $input );
    

    変数$replace_tagは、highlight.phpが分割しないような文字列を使用する必要があります。
    今回はコメント(/* */)を選択しています。

    言語によっては、他の文字列を使用する必要があるかもしれません。

  2. 解析する

    解析するの方法で、highlight.phpを実行します。

    hello!/*____TAGFOLDER____*/hello!/*____TAGFOLDER____*/
    
  3. 置き換えた文字列を元に戻す

    最初に置き換えた文字列を、変数$tag_mapの内容で置き換えます。

    置き換えた文字列の前後にspanタグが挿入されているケースを想定して、正規表現を組み立てています。

    $replace_tag2 = '/\*____TAGFOLDER____\*/';
    $pattern = '#<span.*?>'.$replace_tag2.'</span>|'.$replace_tag2.'#';
    $tag_count = count($tag_map);
    if( $tag_count > 0 ){
        $output = preg_replace_callback(  $pattern,function ($matches) use(&$tag_map){
                return array_shift($tag_map);
             }, $output );
    }
    

 

WordPressでのコード例

次のコードは、WordPressでコードをハイライトする例です。

クラス属性にhighligtを持っているpreタグを対象にしています。

require ( dirname(__FILE__) . '/highligt/vendor/autoload.php');
use Highlight\Highlighter;

add_filter('the_content', function($content){

    $tag = 'pre';
    $class = 'highligt';
    $pattern = '#(<'.$tag.' [^>]*?'.$class.'.*?>)(.+?)(<\/'.$tag.'>)#s';

    $content = preg_replace_callback($pattern, 
        function($matches){
            $input = $matches[2];

            $igonore_class = 'hljsignore';
            $ignore_tags = 'a|span';
            $pattern = '#(?P<s><('.$ignore_tags.') .*?class=".*?'.$igonore_class.'.*?".*?>)(?P<b>.*?)(?P<e></(\2)>)#';

            $tag_map = array();
            $replace_tag = '/*____TAGFOLDER____*/';
            $replace_tag2 = '/\*____TAGFOLDER____\*/';

            $input = preg_replace_callback( $pattern , function ($matches) use(&$tag_map,$replace_tag){
                $tag_map[] = $matches['s'];
                $tag_map[] = $matches['e'];

                return $replace_tag.$matches['b'].$replace_tag;
            }, $input );

            $highlighter = new Highlighter();
            $input = html_entity_decode($input,ENT_QUOTES | ENT_HTML5);
            $highlighter->setAutodetectLanguages([
                'html',
                'php',
                'css',
                'shell',
                'javascript',
                'perl'
            ]);

            $result = $highlighter->highlightAuto($input);
            $output = $result->value;

            $tag_count = count($tag_map);
            if( $tag_count > 0 ){
                $output = preg_replace_callback( '#<span.*?>'.$replace_tag2.'</span>|'.$replace_tag2.'#' , 
                    function ($matches) use(&$tag_map){
                        return array_shift($tag_map);
                    }, $output );
            }

            $html = '<div class="hljs '.$result->language.'">'.$matches[1].$output.$matches[3].'</div>';
        
            return $html;
        }, $content);

    return $content;
});

更新日:2023/02/10

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。