ACF: безопасный вывод полей в шаблоне WordPress
Как безопасно выводить ACF-поля в шаблоне WordPress: текст, ссылки, изображения, WYSIWYG и Repeater с esc_html(), esc_url(), esc_attr() и wp_kses_post().

Короткий ответ: ACF-поля нельзя выводить в шаблоне через простой echo get_field(), если значение может редактировать пользователь. Получите значение через get_field(), проверьте его тип и выведите через подходящую функцию WordPress: esc_html() для текста, esc_url() для ссылок, esc_attr() для атрибутов и wp_kses_post() для разрешенного HTML.
Если проще: ACF хранит данные, а шаблон решает, как показать эти данные посетителю. Проблема начинается там, где мы берем значение из админки и сразу печатаем его на странице. Для джуна это выглядит удобно, но в реальном проекте такой код может сломать верстку или открыть лишний HTML там, где должен быть обычный текст.
Главная ошибка в проектах на WordPress — использовать один способ вывода для всех типов полей. Текст, ссылка, изображение, WYSIWYG и Repeater возвращают разные структуры данных, поэтому безопасный шаблон должен учитывать тип поля и место вывода.

Что мы сделаем
Разберем безопасный вывод ACF на простых примерах: текст, ссылка, изображение, WYSIWYG и Repeater. В каждом блоке будет готовый фрагмент кода, место вставки и пояснение, почему используется именно такая функция.
Термин “экранирование” означает подготовку значения к выводу в HTML. Например, если редактор случайно вставит символ <, WordPress должен показать его как текст, а не принять за начало тега. Термин “sanitization” чаще относится к очистке перед сохранением или обработкой входящих данных. В этой статье основной фокус — именно вывод в шаблоне.
Почему нельзя выводить ACF без экранирования
ACF удобен тем, что редактор может управлять текстом, ссылками, изображениями и повторителями без правки кода. Но это же означает, что данные из полей попадают на публичную страницу. Если вывести их без проверки, в разметку может попасть лишний HTML, сломанная ссылка или опасный фрагмент.
Официальная документация ACF прямо указывает: значения, полученные через get_field(), нужно экранировать перед выводом. Начиная с ACF 6.2.6 у get_field() появился параметр $escape_html, но он не заменяет понимание контекста. URL все равно должен идти через esc_url(), атрибуты — через esc_attr(), а обычный текст — через esc_html().
Базовое правило: экранируйте при выводе
В WordPress принято экранировать данные максимально близко к месту вывода. Не стоит заранее превращать все значения в HTML-safe строки при сохранении в базу: одно и то же поле может использоваться в тексте страницы, в атрибуте, в ссылке или в JSON-конфигурации для JavaScript.
Пример для простого текстового поля:
Куда вставить: в PHP-шаблон темы, дочерней темы или template part, где должна появиться подпись страницы.
<?php
$subtitle = function_exists( 'get_field' ) ? get_field( 'subtitle' ) : '';
if ( $subtitle ) : ?>
<p class="page-subtitle"><?php echo esc_html( $subtitle ); ?></p>
<?php endif; ?>
Проверка function_exists( 'get_field' ) защищает шаблон от фатальной ошибки, если ACF отключили на тестовом или рабочем сайте. Это особенно важно для тем, которые должны переживать обновления и миграции.
Разбор кода: сначала мы проверяем, есть ли функция ACF. Потом получаем поле subtitle. Если значение не пустое, выводим его внутри <p>. Функция esc_html() говорит WordPress: “покажи это как текст, не как HTML”.
Как выводить текстовое поле
Для Text, Textarea, Number и похожих простых полей чаще всего подходит esc_html(). Она экранирует специальные символы и не позволяет значению стать HTML-разметкой.
Куда вставить: в место карточки, страницы или блока, где нужно вывести обычную текстовую заметку из ACF.
<?php
$note = function_exists( 'get_field' ) ? get_field( 'project_note' ) : '';
if ( '' !== (string) $note ) : ?>
<div class="project-note">
<?php echo esc_html( $note ); ?>
</div>
<?php endif; ?>
Если поле хранит число, все равно приведите его к ожидаемому типу. Например, для цены или количества используйте absint(), floatval() или собственную валидацию, а затем выводите в нужном формате.
Что должно получиться: если поле заполнено, посетитель видит текст. Если поле пустое, блок не выводится и в HTML не остается пустого контейнера.
Как выводить ссылку ACF
Поле Link в ACF обычно возвращает массив с ключами url, title и target. Для каждого значения нужен свой фильтр: URL идет через esc_url(), текст ссылки через esc_html(), target через ограниченный список допустимых значений.
Куда вставить: в шаблон блока с кнопкой или ссылкой, например в CTA, карточку услуги или секцию портфолио.
<?php
$link = function_exists( 'get_field' ) ? get_field( 'cta_link' ) : null;
if ( is_array( $link ) && ! empty( $link['url'] ) ) :
$url = $link['url'];
$title = ! empty( $link['title'] ) ? $link['title'] : 'Подробнее';
$target = '_blank' === ( $link['target'] ?? '' ) ? '_blank' : '_self';
$rel = '_blank' === $target ? 'noopener noreferrer' : '';
?>
<a class="project-cta"
href="<?php echo esc_url( $url ); ?>"
target="<?php echo esc_attr( $target ); ?>"
<?php echo $rel ? 'rel="' . esc_attr( $rel ) . '"' : ''; ?>>
<?php echo esc_html( $title ); ?>
</a>
<?php endif; ?>
Не используйте esc_attr() вместо esc_url() для href. URL требует отдельной обработки: WordPress отфильтрует недопустимые протоколы и корректно подготовит значение для HTML.
Разбор кода: is_array() проверяет, что ACF действительно вернул массив ссылки. esc_url() готовит адрес для href. esc_html() безопасно выводит текст кнопки. Для target мы не доверяем значению напрямую, а разрешаем только _blank или _self.
Как выводить изображение ACF
Для поля Image лучше выбрать формат возврата ID или Array. ID проще и надежнее: WordPress сам подставит srcset, размеры и alt из медиатеки.
Куда вставить: в шаблон изображения: hero-блок, карточку записи, карточку проекта или секцию с преимуществами.
<?php
$image_id = function_exists( 'get_field' ) ? absint( get_field( 'hero_image' ) ) : 0;
if ( $image_id ) {
echo wp_get_attachment_image(
$image_id,
'large',
false,
array(
'class' => 'hero-image',
'loading' => 'eager',
)
);
}
?>
Если поле возвращает массив, не выводите его части без проверки. Минимальный безопасный вариант:
<?php
$image = function_exists( 'get_field' ) ? get_field( 'card_image' ) : null;
if ( is_array( $image ) && ! empty( $image['ID'] ) ) {
echo wp_get_attachment_image(
absint( $image['ID'] ),
'medium',
false,
array( 'class' => 'card-image' )
);
}
?>
Не собирайте <img> вручную, если можно использовать wp_get_attachment_image(). Так меньше риск забыть alt, размеры, lazy loading или адаптивные версии.
Разбор кода: absint() превращает значение в положительное число. Это важно, потому что ID изображения должен быть числом. wp_get_attachment_image() берет картинку из медиатеки и сам собирает правильный <img> с размерами, srcset и атрибутами.
Как выводить WYSIWYG и HTML-поля
Если редактору действительно нужен форматированный текст, используйте wp_kses_post(). Эта функция оставляет HTML, разрешенный для контента записи, но убирает опасные теги и атрибуты.
Куда вставить: в шаблон текстового блока, где редактору разрешены абзацы, списки, ссылки и простое форматирование.
<?php
$content = function_exists( 'get_field' ) ? get_field( 'project_description' ) : '';
if ( $content ) : ?>
<div class="project-description">
<?php echo wp_kses_post( $content ); ?>
</div>
<?php endif; ?>
В ACF 6.2.6 и новее можно запросить HTML-safe значение четвертым параметром:
<?php
$content = function_exists( 'get_field' )
? get_field( 'project_description', false, true, true )
: '';
if ( $content ) {
echo $content; // Значение уже прошло ACF HTML escaping.
}
?>
Этот вариант полезен, когда нужно использовать field-specific escaping ACF. Но для ссылок, изображений и составных массивов все равно лучше разбирать структуру и экранировать каждую часть в своем контексте.
Важно для джуна: если поле должно быть обычной строкой, не используйте wp_kses_post() “на всякий случай”. Она разрешает часть HTML. Для названий, кнопок и коротких подписей обычно нужен esc_html().
Как выводить Repeater без пустой разметки
Repeater часто ломает верстку тем, что шаблон выводит контейнер даже при пустых строках. Сначала проверьте наличие строк, затем внутри цикла проверяйте обязательные подполе.
Куда вставить: в template part повторяющегося блока, например список преимуществ, этапы работы, FAQ или характеристики.
<?php if ( function_exists( 'have_rows' ) && have_rows( 'features' ) ) : ?>
<ul class="features">
<?php while ( have_rows( 'features' ) ) : the_row(); ?>
<?php
$title = get_sub_field( 'title' );
$text = get_sub_field( 'text' );
if ( ! $title && ! $text ) {
continue;
}
?>
<li class="features__item">
<?php if ( $title ) : ?>
<h3 class="features__title"><?php echo esc_html( $title ); ?></h3>
<?php endif; ?>
<?php if ( $text ) : ?>
<p class="features__text"><?php echo esc_html( $text ); ?></p>
<?php endif; ?>
</li>
<?php endwhile; ?>
</ul>
<?php endif; ?>
Для сложных повторителей удобно сначала собрать массив валидных строк, а уже потом выводить контейнер. Так вы не получите пустой <ul>, если все строки заполнены частично или отключены.
Разбор кода: have_rows() проверяет, есть ли строки Repeater. the_row() переключает текущую строку. get_sub_field() получает значение подполя внутри строки. Если и заголовок, и текст пустые, continue пропускает эту строку.
Куда вставлять код
Код вывода ACF-полей размещают в шаблонах темы, дочерней темы или в template parts. Если дорабатываете готовую тему, безопаснее создать дочернюю тему и переопределить нужный шаблон там. Подробный порядок описан в статье как создать дочернюю тему WordPress без потери изменений.
Если шаблон требует отдельные стили или скрипты, подключайте их через очередь WordPress. Не вставляйте <link> и <script> прямо в шаблон. Рабочий пример есть в инструкции как подключить CSS и JavaScript через wp_enqueue.
Если не работает
Используют the_field() в старом коде
В новых версиях ACF вывод через the_field() стал безопаснее, но в проектах с разными версиями плагина надежнее явно использовать get_field() и подходящую функцию экранирования. Так код остается понятным и предсказуемым. Для диагностики временно замените вывод на var_dump( get_field( 'field_name' ) ) на тестовой копии и посмотрите, какой тип данных реально возвращается.
Выводят массив как строку
Поля Link, Image, Gallery, Relationship и Repeater могут возвращать массивы или объекты. Перед выводом проверяйте тип через is_array(), is_object() или приводите ID через absint().
Разрешают HTML там, где нужен текст
Если поле должно быть названием карточки, кнопки или пункта списка, используйте esc_html(), а не wp_kses_post(). Разрешайте HTML только там, где он действительно нужен редактору.
Забывают про контекст атрибута
Текст внутри тега и текст внутри атрибута — разные контексты. Для alt, title, aria-label, class используйте esc_attr(). Для URL в href и src используйте esc_url().
Как проверить шаблон
- Откройте страницу с заполненными ACF-полями и проверьте, что нет PHP warning и пустых контейнеров.
- Посмотрите исходный код и убедитесь, что значения не попали в HTML как необработанные теги.
- Проверьте ссылки: внешний target
_blankдолжен получатьrel="noopener noreferrer". - Проверьте изображения: есть alt, размеры и
srcset. - Временно вставьте в тестовое поле символы
<>"'и убедитесь, что верстка не ломается. - Откройте страницу на мобильной ширине и проверьте длинные ссылки, подписи и карточки.
Если нужно привести шаблоны WordPress в порядок, безопасно вывести ACF-поля и не сломать существующую тему, можно отправить задачу на техническую поддержку WordPress.
Итог
Безопасный вывод ACF-полей строится не вокруг одной универсальной функции, а вокруг контекста. Текст выводите через esc_html(), атрибуты через esc_attr(), URL через esc_url(), разрешенный HTML через wp_kses_post(), а изображения — через wp_get_attachment_image(). Проверяйте наличие ACF-функций, тип возвращаемого значения и пустые состояния. Такой шаблон проще поддерживать, переносить и проверять после обновлений.
Официальная документация: ACF get_field(), ACF HTML Escaping, ACF Security Principles, WordPress Escaping Data, esc_url(), esc_html().


