Или, как один простой вопрос неожиданно превратился в микро-расследование.
Познакомиться с новым сервисом или инструментом разработки можно многими путями, от рассказа знакомого до оплаченного разработчиком рекламного блока. Впечатлившись предлагаемыми возможностями, вы, конечно же, захотите протестировать новинку, прежде чем встраивать ее в ваши привычные процессы. Тут-то и может подстерегать сюрприз.
Опуская неинтересную предысторию, нынешнее утро застало меня на сайте Context7 — MCP-сервера, обещающего всегда актуальную документацию для огромного количества фреймворков, языков и библиотек.

Впечатляет и вдохновляет. Введенное “wordpress” в поле поиска показало вполне приемлемую картину обновлений. Обратите внимание на третью строку, где мы видим, собственно, официальный репозиторий WordPress (точнее, его зеркало с subversion).
Руки уже потянулись к клавиатуре для установки, как вдруг внимание привлек скромный пункт “Try live” в меню. Что ж, а почему бы и нет? Открываем чат и задаем простейший вопрос: “Последний известный релиз WordPress”.
ИИ последовательно просмотрел несколько пакетов документации (причем /wordpress/wordpress среди них не было) и… сообщил, что последняя известная ему версия WordPress - это 6.7 “Rollins”, вышедшая в ноябре 2024. Эй, а как же “up-to-date” и “version-specific”?! Впрочем, в чате заботливо рассказывают, как узнать версию самостоятельно, но, согласитесь, это несколько не то.
Что ж, возможно, вопрос и в самом деле был некорректен. Попробуем конкретизировать. Спросим об основных отличиях между 6.9 и 7.0, там как раз было одно маленькое, но очень важное изменение, детали которого потребовались мне в проекте. Спрашиваем.
Хм… Кажется, и этот вопрос останется без ответа. Хотя, казалось бы, именно на подобные вопросы должна отвечать “version-specific” документация. Теперь мне уже стало интересно: продолжим конкретизировать.
Что ж, ИИ прав: вопрос был с подвохом, такой функции в WordPress действительно нет, это метод класса. Ложноположительного срабатывания я не получила. Спросим сразу об интересующем нас filter.
Хорошо, мы получили информацию о filter. Единственный момент, который настораживает: доступность фильтра указана с версии 5.6. Впрочем, вернемся к этому немного позже, сначала хорошо бы уточнить информацию о методе, где применяется этот filter. Здесь начинается самое интересное, привожу конкретный код, показанный в ответе:
public function get_page_cache_headers(): array {
$cache_hit_callback = static function ( $header_value ) {
return 1 === preg_match( '/(^| |,)HIT(,| |$)/i', $header_value );
};
$cache_headers = array(
// Standard HTTP caching headers.
'cache-control' => static function ( $header_value ) {
return (bool) preg_match( '/max-age=[1-9]/', $header_value );
},
'expires' => static function ( $header_value ) {
return strtotime( $header_value ) > time();
},
'age' => static function ( $header_value ) {
return is_numeric( $header_value ) && $header_value > 0;
},
'last-modified' => null,
'etag' => null,
'via' => null,
// Vendor/custom headers (ported from AMP plugin → Performance Lab → core).
'x-cache-enabled' => static function ( $header_value ) {
return ( 'true' === strtolower( $header_value ) );
},
'x-cache-disabled' => static function ( $header_value ) {
return ( 'on' !== strtolower( $header_value ) );
},
// Cloudflare.
'cf-cache-status' => $cache_hit_callback,
// Fastly.
'x-cache' => $cache_hit_callback,
// LiteSpeed.
'x-litespeed-cache' => $cache_hit_callback,
);
return $cache_headers;
}
На первый взгляд, выглядит достоверно. Но только на первый. Вернемся к основному вопросу — об отличиях версий 6.9 и 7.0 и вспомним про декларируемую доступность фильтра с версии 5.6. Зайдем в релизы репозитория /wordpress/wordpress на Github и выберем сравнение двух веток: 6.9.7 и 7.0.4. Нас интересует коммит, затрагивающий метод get_page_cache_headers. Для наглядности приведем код метода целиком, включая аннотации:
/**
* Returns a list of headers and its verification callback to verify if page cache is enabled or not.
*
* Note: key is header name and value could be callable function to verify header value.
* Empty value mean existence of header detect page cache is enabled.
*
* @since 6.1.0
*
* @return array List of client caching headers and their (optional) verification callbacks.
*/
public function get_page_cache_headers() {
$cache_hit_callback = static function ( $header_value ) {
return str_contains( strtolower( $header_value ), 'hit' );
};
$cache_headers = array(
'cache-control' => static function ( $header_value ) {
return (bool) preg_match( '/max-age=[1-9]/', $header_value );
},
'expires' => static function ( $header_value ) {
return strtotime( $header_value ) > time();
},
'age' => static function ( $header_value ) {
return is_numeric( $header_value ) && $header_value > 0;
},
'last-modified' => '',
'etag' => '',
'x-cache-enabled' => static function ( $header_value ) {
return 'true' === strtolower( $header_value );
},
'x-cache-disabled' => static function ( $header_value ) {
return ( 'on' !== strtolower( $header_value ) );
},
'x-srcache-store-status' => $cache_hit_callback,
'x-srcache-fetch-status' => $cache_hit_callback,
// Generic caching proxies (Nginx, Varnish, etc.)
'x-cache' => $cache_hit_callback,
'x-cache-status' => $cache_hit_callback,
'x-litespeed-cache' => $cache_hit_callback,
'x-proxy-cache' => $cache_hit_callback,
'via' => '',
// Cloudflare
'cf-cache-status' => $cache_hit_callback,
);
/**
* Filters the list of cache headers supported by core.
*
* @since 6.1.0
*
* @param array $cache_headers Array of supported cache headers.
*/
return apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers );
}
Кое-что удивляет, не правда ли? @since в аннотациях указывают на доступность и фильтра, и функции с версии 6.1.0, а вовсе не 5.6. Также определение $cache_hit_callback выглядит намного проще показанного в Context7 и массив $cache_headers отличается как по группировке, так и по составу. Давайте-ка скачаем архив релиза 5.6.19!
Поиск по содержимому файлов релиза 5.6.19 показывает отсутствие и метода get_page_cache_headers, и фильтра site_status_page_cache_supported_cache_headers где бы то ни было в коде. Идем в репозиторий, находим файл wp-admin/includes/class-wp-health.php и смотрим его историю редактирования. Находим коммит, добавивший функцию get_page_cache_headers. Убеждаемся, что никакого preg_match( '/(^| |,)HIT(,| |$)/i', $header_value ) там нет.
Немного позже находим и коммит, где false !== strpos( ... ) заменяется на str_contains() и далее никаких изменений с этим блоком кода не происходит.
Проделав аналогичные манипуляции с кодом, можно также убедиться, что и написание массива заголовков в любой версии реального файла отличается от показанного в чате Context7. Более того, ИИ не знает о наличии в этом массиве таких заголовков как x-srcache-store-status, x-srcache-fetch-status и x-proxy-cache, а вместо заголовка x-cache-status предлагает использовать x-cache-enabled и x-cache-disabled.
Можно возвразить, что это очень узкоспециализированный вопрос, крайне редко используемые данные и так далее. Однако люди создают не только темы, но и плагины для экосистемы WordPress, в том числе расширяющие штатный функционал. И кэш-решения занимают отнюдь не последнее место по популярности.
Не оспариваю полезность Context7, для этого у меня слишком мало опыта работы с ИИ. Но, надеюсь, получилось подсветить в статье потенциальные проблемные места этого решения, как минимум для WordPress-разработчиков.
Автор: Alhana
