Экранирование кавычек в атрибутах шорткодов WordPress
Я пытаюсь разобраться, как экранировать кавычки (одинарные и двойные) в атрибутах шорткодов.
Проблема в том, что контент вводится пользователем и может содержать кавычки " и '. С двойными кавычками атрибут шорткода перестаёт работать правильно, например:
attibute="текст с "кавычками" прерывает атрибут..."
Вместо получения всей строки обработка останавливается на второй паре кавычек.
Я знаю, что можно использовать одинарные кавычки, но тогда возникает та же проблема, если пользователь введёт одинарные кавычки.
Я пробовал различные решения на PHP/WP, такие как esc_html, htmlspecialchars, htmlentities, но ни одно не сработало.
Возможно, я неправильно их настраиваю или применяю не в том месте.
Вот текущий код шорткода (сокращённая версия без экранирования):
function aps_person_schema_shortcode( $atts, $content = null) {
// Извлекаем атрибуты
extract( shortcode_atts( array(
'aps_person_description' => ''
), $atts
)
);
// Формируем HTML
$aps_person_return = '<div class="aps_person_container aps_container">';
$aps_person_return .= '<p class="aps_person_description" itemprop="description">' . $aps_person_description . '</p>';
$aps_person_return .= '</div>';
return $aps_person_return;
}
// Регистрируем шорткод
add_shortcode('aps_person', 'aps_person_schema_shortcode');
Я пробовал добавлять после extract что-то вроде:
$aps_person_description = esc_html($atts['aps_person_description']);
Но так как атрибут уже сломан (print_r показывает, что строка после кавычки разбивается на элементы массива для каждого слова), экранирование строки в этом месте не работает.
Пробовал до массива — тоже не работает, получаю Notice: Undefined index.
Итак, вопрос: как правильно санитизировать пользовательский ввод для данных атрибутов шорткода?

Оказывается, система шорткодов WordPress использует функцию shortcode_parse_atts($text);
для разбора записи шорткода, чтобы извлечь имена и значения атрибутов и сохранить их парами в массиве $atts
, который затем передается в функцию шорткода. Таким образом, в вашем случае добавление экранирующих действий в функцию шорткода, например:
$aps_person_description = esc_html($atts['aps_person_description']);
не сработает, потому что shortcode_parse_atts()
уже извлек имена и значения атрибутов и мог неправильно интерпретировать кавычки в значениях атрибутов.
При ближайшем рассмотрении функция shortcode_parse_atts()
использует такое регулярное выражение:
/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/
для распознавания имен и значений атрибутов в следующих форматах:
1. attr1="value1"
2. attr2='value2'
3. attr3=value3
4. "value4"
5. value5
Поэтому, если пользователь вставит кавычки в значения, это может легко привести к ошибке. Например:
[my_shortcode dog='Santa's Little Helper' /]
// $attr будет Array ( [0] => dog='Santa's [1] => Little [2] => Helper' )
// а должно быть Array ( [dog] => Santa's Little Helper )
Санитизация после разбора явно слишком запоздалая. А что насчет санитизации перед разбором? Придется обрабатывать всю запись, так как у нас еще нет значений атрибутов для работы. Но это тоже вызывает проблемы, например:
html_entities("[my_shortcode dog='Santa's Little Helper' /]", ENT_QUOTES);
// теперь запись выглядит как [my_shortcode dog='Santa's Little Helper' /]
// после разбора:
Array
(
[dog] => 'Santa's
[0] => Little
[1] => Helper'
)
Парадокс в том, что для корректного разбора парсером необходимо санитизировать значения атрибутов, но чтобы это сделать, сначала нужно запустить парсер и получить эти значения.
В заключение, я считаю, что разумным способом обеспечить корректную работу является требование к пользователям вводить атрибуты в правильном формате. Если пользователь введет плохо отформатированные атрибуты, это станет большой головной болью. Для справки, мой опыт подсказывает использовать $content
. В вашем случае я бы поместил aps_person_description
в $content
, это может выглядеть так:
[my_shortcode]текст здесь с "кавычками", которые могут прерывать атрибут...[/my_shortcode]
и кавычки будут меньше проблемой.

Нет никакого парадокса, это простая, но существенная ошибка в функции shortcode_parse_atts()
. Это просто неправильный способ разбора атрибутов в кавычках. Экранирующие последовательности необходимы - вы не найдёте ни одного языка программирования без них. Это должно было быть встроено с самого начала. Ваш пример можно было бы легко записать как [my_shortcode dog='Santa\'s Little Helper' /]
, если бы парсер имел представление об этом.

Кажется, вы работаете не с той стороны.
Попробуйте санировать пользовательский ввод, например, с помощью sanitize_text_field
, а не вывод шорткода.

Я столкнулся с похожей проблемой при выводе PHP-переменной в атрибут шорткода. В моем случае это было название термина таксономии, а не пользовательский ввод, поэтому не требовалось санитизации.
Использование htmlentities()
с флагом ENT_QUOTES
позволяет использовать строки, содержащие '
и "
, в качестве атрибутов шорткода. Однако это также преобразует другие символы в HTML-сущности, например, &
в &
, что в моем случае было нежелательно.
Чтобы преобразовывать только символы '
и "
в HTML-сущности, не затрагивая другие символы, такие как &
:
htmlspecialchars_decode( htmlentities($value_to_print, ENT_QUOTES), ENT_NOQUOTES )
htmlspecialchars_decode()
отменяет все преобразования, выполненные функцией htmlentities()
. Использование флага ENT_NOQUOTES
предотвращает обратное преобразование символов '
и "
.
