WordPress. Плагин для подсветки php-кода

07.07.2019

Теги: CMSPHPWeb-разработкаWordPressПлагин

Давайте создадим плагин, который позволит подсвечивать php-код в записях блога. Для подсветки используем нативную php-функцию highlight_string(). Добавлять php-код в запись можно будет с помощью шорткода [php]. Практической ценности плагин не представляет, написан исключительно с целью изучения WordPress.

Главная трудность при реализии плагина — это автоматические замены. WordPress заменяет одинарные и двойные кавычки на лапки и елочки. И заменяет двойные и одинарные переводы строк на теги <p> и <br/>.

Плагин для подсветки php-кода

Итак, создаем директорию highlight-php-code, а внутри нее — файл highlight-php-code.php:

<?php
/*
Plugin Name: Подсветка php-кода
Plugin URI: https://tokmakov.msk.ru
Description: Добавляет шорткод [php] для подсветки php-кода
Version: 1.0
Author: Евгений Токмаков
Author URI: https://tokmakov.msk.ru
*/

/*
 * Добавляем шорткод [php], который позволит вставлять в записи
 * блога php-код
 */
add_shortcode('php', function($atts, $content) {
    if (empty($content)) {
        return '';
    }
    $content = trim($content);
    /*
     * При сохранении записи блога, переводы строк записываются в базу данных
     * как \r\n. При выводе записи блога функция highlight_string() заменяет
     * символ \n на тег <br/>, а вот символ \r остается. И внутри тега <pre>
     * добавляет лишнюю пустую строку между строк подсвеченного php-кода. Так
     * что удаляем символ \r, чтобы этого избежать.
     */
    $content = str_replace("\r", '', $content);
    /*
     * При обработке шорткодов WP производит замены в $content:
     * 1. открывающих прямоугольных кавычек [ на &#91;
     * 2. закрывающих прямоугольных кавычкек ] на &#93;
     * Так что нам здесь надо произвести обратные замены.
     */
    $content = str_replace(['&#91;', '&#93;'], ['[', ']'], $content);
    $content = '<pre>' . highlight_string($content, true) . '</pre>';
    // убираем перевод строки, который добавляет highlight_string()
    $content = str_replace("\n", '', $content);
    return $content;
});

/*
 * Функция wpautop заменяет переводы строк на теги <p> и <br/>. И
 * это ломает код внутри шорткода [php]. Это происходит потому,
 * что функция wpautop выполняется раньше функции do_shortcode.
 * Поэтому сначала отключаем функцию wpautop для контента записи,
 * чтобы функция do_shortcode успела обработать шорткод [php], а
 * потом включаем ее снова.
 */
remove_filter('the_content', 'wpautop');
add_filter('the_content', 'wpautop', 99);

/*
 * Запрещаем функцию wptexturize внутри шорткода [php], которая
 * заменяет одинарные и двойные кавычки на лапки и елочки, чтобы
 * эта функция не ломала php-код.
 */
add_filter('no_texturize_shortcodes', function ($shortcodes) {
    $shortcodes[] = 'php';
    return $shortcodes;
});

Функция wpautop() заменяет двойной перенос строки на теги <p> и </p>, а одинарный — на тег <br/>. По умолчанию применяется к следующим фильтрам:

add_filter('term_description', 'wpautop');
add_filter('get_the_post_type_description', 'wpautop');
add_filter('the_content', 'wpautop');
add_filter('the_excerpt', 'wpautop');
add_filter('comment_text', 'wpautop', 30);
add_filter('widget_text_content', 'wpautop');
add_filter('the_excerpt_embed', 'wpautop');

Если нужно отключить этот фильтр у контента записи (the_content) или цитаты (the_excerpt), то добавляем в functions.php следующий код:

remove_filter('the_content', 'wpautop');
remove_filter('the_excerpt', 'wpautop');

Функция wptexturize() изменяет некоторые символы в тексте на более правильные, читаемые и визуально привлекательные. Текст внутри HTML блоков <pre>, <code>, <kbd>, <style>, <script>, <tt> и в шорткоде [code] пропускается.

Список всех фильтров, к которым по умолчанию применяется функция:

add_filter('comment_author',   'wptexturize');
add_filter('term_name',        'wptexturize');
add_filter('link_name',        'wptexturize');
add_filter('link_description', 'wptexturize');
add_filter('link_notes',       'wptexturize');
add_filter('bloginfo',         'wptexturize');
add_filter('wp_title',         'wptexturize');
add_filter('widget_title',     'wptexturize');

add_filter('single_post_title',    'wptexturize');
add_filter('single_cat_title',     'wptexturize');
add_filter('single_tag_title',     'wptexturize');
add_filter('single_month_title',   'wptexturize');
add_filter('nav_menu_attr_title',  'wptexturize');
add_filter('nav_menu_description', 'wptexturize');

add_filter('term_description',              'wptexturize');
add_filter('get_the_post_type_description', 'wptexturize');

add_filter('the_title',                  'wptexturize');
add_filter('the_content',                'wptexturize');
add_filter('the_excerpt',                'wptexturize');
add_filter('the_post_thumbnail_caption', 'wptexturize');
add_filter('comment_text',               'wptexturize');
add_filter('list_cats',                  'wptexturize');
add_filter('widget_text_content',        'wptexturize');
add_filter('the_excerpt_embed',          'wptexturize');

Если нужно указать html теги или шоткоды, где функция работать не должна, то можно использовать фильтры no_texturize_tags и no_texturize_shortcodes соответственно.

Альтернативная реализация подсветки

Без использования шорткода, просто с использованием тега <pre>:

add_filter('the_content', function($content) {
    $content = preg_replace_callback(
        '~(<pre[^>]*>)\s*(.*?)\s*(</pre>)~s',
        function($match){
            $code = $match[2];
            $code = str_replace("\r", '', $code);
            $code = $match[1] . highlight_string($code, true) . $match[3];
            // убираем перевод строки, который добавляет highlight_string
            $code = str_replace("\n", '', $code);
            return $code;
        },
        $content
    );
    return $content;
});

Столкнулся с неожиданным багом — перестал корректно работать плагин галереи. Это произошло потому, что в html-коде галереи WordPress заменил переводы строк на тег <br/>. В итоге получилось следующее:

<div class="gallery-images">
    <a href="..." class="swipebox" title="..." rel="..."><br />
        <img src="..." alt="..." /><br />
    </a><br />
    <a href="..." class="swipebox" title="..." rel="..."><br />
        <img src="..." alt="..." /><br />
    </a><br />
    <a href="..." class="swipebox" title="..." rel="..."><br />
        <img src="..." alt="..." /><br />
    </a><br />
</div>

В этом виноват вот этот фрагмент кода плагина подсветки php-кода:

remove_filter('the_content', 'wpautop');
add_filter('the_content', 'wpautop', 99);

Так что пришлось добавить в плагин галереи небольшой хак, который вырезает теги <br/> внутри html-кода галереи:

add_filter(
    'the_content',
    function($content) {
        $content = preg_replace_callback(
            '~(<div class="gallery-images">)(.*?)(</div>)~s',
            function($match){
                $html = $match[1].strip_tags($match[2],'<a><img>').$match[3];
                return $html;
            },
            $content
        );
        return $content;
    },
    100
);

Поиск: PHP • Web-разработка • WordPress • Плагин • Shortcode • Шорткод • highlight_string • PHP • Подсветка кода • wpautop • wptexturize

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.