Шорткоды WordPress: создание, параметры и вложенные шорткоды

Шорткоды WordPress — это макросы вида , которые заменяются на HTML при выводе записи. Они удобны для вставки динамического контента: форм обратной связи, таблиц цен, блоков с кодом, кнопок CTA. Разберём создание шорткодов от простых до вложенных, с параметрами и интеграцией в Gutenberg.

Создание простого шорткода

Функция add_shortcode() регистрирует шорткод. Первый аргумент — имя (только строчные буквы, цифры, дефис и подчёркивание), второй — callback-функция, которая возвращает HTML.

<?php
// functions.php — простой шорткод текущего года
add_shortcode( 'year', function() {
    return date( 'Y' );
});
// Использование: Копирайт &copy; [year]

// Шорткод с информацией о сайте
add_shortcode( 'site_info', function( $atts ) {
    $atts = shortcode_atts([
        'field' => 'name',
    ], $atts, 'site_info' );

    switch ( $atts['field'] ) {
        case 'name':
            return get_bloginfo( 'name' );
        case 'url':
            return home_url();
        case 'description':
            return get_bloginfo( 'description' );
        case 'posts_count':
            return wp_count_posts()->publish;
        default:
            return '';
    }
});
// Использование: Сайт [site_info field="name"] содержит [site_info field="posts_count"] статей

Callback шорткода должен возвращать строку через return, а не выводить её через echo. Если использовать echo, контент появится в начале страницы, а не в месте вставки шорткода.

Параметры (атрибуты) шорткода

Функция shortcode_atts() объединяет пользовательские атрибуты со значениями по умолчанию. Третий аргумент — имя шорткода — позволяет фильтровать атрибуты через хук shortcode_atts_{shortcode}.

<?php
// Шорткод кнопки с настраиваемыми параметрами
add_shortcode( 'button', function( $atts ) {
    $atts = shortcode_atts([
        'url'    => '#',
        'text'   => 'Подробнее',
        'color'  => 'blue',
        'size'   => 'medium',
        'target' => '_self',
        'class'  => '',
    ], $atts, 'button' );

    $classes = 'btn btn-' . esc_attr( $atts['color'] ) . ' btn-' . esc_attr( $atts['size'] );
    if ( $atts['class'] ) {
        $classes .= ' ' . esc_attr( $atts['class'] );
    }

    return sprintf(
        '<a href="%s" class="%s" target="%s">%s</a>',
        esc_url( $atts['url'] ),
        $classes,
        esc_attr( $atts['target'] ),
        esc_html( $atts['text'] )
    );
});
// Использование: [button url="https://example.com" text="Купить" color="green" size="large"]

// Шорткод вывода последних записей
add_shortcode( 'recent_posts', function( $atts ) {
    $atts = shortcode_atts([
        'count'    => 5,
        'category' => '',
        'orderby'  => 'date',
    ], $atts, 'recent_posts' );

    $args = [
        'post_type'      => 'post',
        'posts_per_page' => intval( $atts['count'] ),
        'post_status'    => 'publish',
        'orderby'        => sanitize_key( $atts['orderby'] ),
        'order'          => 'DESC',
    ];

    if ( $atts['category'] ) {
        $args['category_name'] = sanitize_text_field( $atts['category'] );
    }

    $query = new WP_Query( $args );

    if ( ! $query->have_posts() ) {
        return '<p>Записей не найдено.</p>';
    }

    $output = '<ul class="recent-posts-shortcode">';
    while ( $query->have_posts() ) {
        $query->the_post();
        $output .= sprintf(
            '<li><a href="%s">%s</a> <span class="post-date">%s</span></li>',
            get_permalink(),
            get_the_title(),
            get_the_date()
        );
    }
    $output .= '</ul>';
    wp_reset_postdata();

    return $output;
});
// Использование: [recent_posts count="3" category="wordpress" orderby="comment_count"]

Все атрибуты приходят как строки — не забывайте приводить типы: intval() для чисел, filter_var() для boolean. Экранируйте вывод через esc_attr(), esc_html(), esc_url() — шорткоды могут использоваться в произвольном контексте.

Вложенные (enclosing) шорткоды

Вложенный шорткод оборачивает контент: [shortcode]содержимое[/shortcode]. Callback получает контент вторым аргументом $content.

<?php
// Шорткод для блока с предупреждением
add_shortcode( 'alert', function( $atts, $content = '' ) {
    $atts = shortcode_atts([
        'type' => 'info', // info, warning, danger, success
    ], $atts, 'alert' );

    // do_shortcode() обрабатывает вложенные шорткоды внутри $content
    $inner = do_shortcode( $content );

    return sprintf(
        '<div class="alert alert-%s">%s</div>',
        esc_attr( $atts['type'] ),
        wpautop( $inner ) // автоматические абзацы
    );
});
// Использование:
// [alert type="warning"]
// Внимание! Этот код удалит все данные.
// [button url="/docs" text="Читать документацию"]
// [/alert]

// Шорткод спойлера (скрытый контент)
add_shortcode( 'spoiler', function( $atts, $content = '' ) {
    $atts = shortcode_atts([
        'title' => 'Показать',
    ], $atts, 'spoiler' );

    static $spoiler_id = 0;
    $spoiler_id++;

    $output  = '<div class="spoiler-wrap">';
    $output .= '<button class="spoiler-toggle" onclick="document.getElementById(\'spoiler-' . $spoiler_id . '\').classList.toggle(\'spoiler-open\')">';
    $output .= esc_html( $atts['title'] );
    $output .= '</button>';
    $output .= '<div id="spoiler-' . $spoiler_id . '" class="spoiler-content">';
    $output .= do_shortcode( $content );
    $output .= '</div></div>';

    return $output;
});
// Использование: [spoiler title="Показать ответ"]Ответ: 42[/spoiler]

Важные нюансы вложенных шорткодов:

  • WordPress не поддерживает вложенность одинаковых шорткодов: [alert][alert]...[/alert][/alert] не сработает
  • Если $content содержит другие шорткоды, вызывайте do_shortcode($content) для их обработки
  • Функция wpautop() преобразует переносы строк в теги <p> — полезно для многострочного контента

Подключение стилей и скриптов только при использовании шорткода

Не подключайте CSS и JS глобально, если они нужны только для шорткода. Используйте wp_enqueue_style() и wp_enqueue_script() прямо в callback — WordPress подключит их в футере.

<?php
add_shortcode( 'price_table', function( $atts ) {
    // CSS подключится только на страницах, где используется шорткод
    wp_enqueue_style(
        'price-table-css',
        get_template_directory_uri() . '/assets/css/price-table.css',
        [],
        '1.0'
    );

    wp_enqueue_script(
        'price-table-js',
        get_template_directory_uri() . '/assets/js/price-table.js',
        [ 'jquery' ],
        '1.0',
        true // в футер
    );

    $atts = shortcode_atts([
        'columns' => 3,
    ], $atts, 'price_table' );

    ob_start();
    include get_template_directory() . '/template-parts/price-table.php';
    return ob_get_clean();
});

Обратите внимание на приём с ob_start() / ob_get_clean() — он позволяет использовать PHP-шаблон с обычным HTML вместо конкатенации строк. Это намного удобнее для сложной вёрстки.

Интеграция шорткодов с Gutenberg

Начиная с WordPress 5.0, блочный редактор Gutenberg поддерживает шорткоды через блок «Шорткод». Но пользоваться им неудобно — нет предпросмотра и подсказок. Лучше создать собственный блок-обёртку:

<?php
// functions.php — регистрация блока для шорткода alert
add_action( 'init', function() {
    // Проверяем, что Gutenberg доступен
    if ( ! function_exists( 'register_block_type' ) ) {
        return;
    }

    wp_register_script(
        'alert-block-editor',
        get_template_directory_uri() . '/blocks/alert/editor.js',
        [ 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components' ],
        '1.0'
    );

    register_block_type( 'mytheme/alert', [
        'editor_script'   => 'alert-block-editor',
        'render_callback' => function( $attributes, $content ) {
            $type = $attributes['type'] ?? 'info';
            return sprintf(
                '<div class="alert alert-%s">%s</div>',
                esc_attr( $type ),
                wpautop( do_shortcode( $content ) )
            );
        },
        'attributes' => [
            'type' => [
                'type'    => 'string',
                'default' => 'info',
            ],
        ],
    ]);
});

Такой блок будет отображаться в визуальном редакторе с предпросмотром, а для вывода на фронте использует ту же логику, что и шорткод.

Типичные ошибки при создании шорткодов

  • echo вместо return — самая частая ошибка. Контент появляется вверху страницы, а не в месте вставки. Используйте ob_start() + ob_get_clean(), если нужно буферизировать вывод
  • Отсутствие экранирования — атрибуты шорткода могут содержать произвольный текст. Всегда экранируйте через esc_attr(), esc_html(), esc_url()
  • Одинаковые имена — если два плагина зарегистрируют шорткод с одним именем, сработает тот, что зарегистрирован последним. Используйте уникальные префиксы: [myprefix_button] вместо [button]
  • Тяжёлые запросы — шорткод выполняется при каждом просмотре страницы. Для сложных выборок используйте Transients API для кеширования
  • Забыли do_shortcode($content) — без этого вызова шорткоды внутри вложенного контента не обработаются

Шорткоды остаются удобным инструментом для вставки динамического контента, несмотря на появление Gutenberg-блоков. Они работают в классическом редакторе, виджетах (через add_filter('widget_text', 'do_shortcode')) и даже в PHP-шаблонах через do_shortcode('[your_shortcode]'). Главное — не злоупотребляйте ими: если логику можно вынести в шаблон темы или блок Gutenberg, это будет более производительно и поддерживаемо.