Вопрос или проблема
Я пытаюсь настроить WordPress 5.9, чтобы разрешить числовые ярлыки для дочерних страниц. Взяв ответ 2015 года на подобный вопрос, я использовал обновленный код wp_unique_post_slug
, удалил || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug )
и добавил его в качестве следующего хука в functions.php
моей темы. Но по какой-то причине я получаю критическую ошибку: “Исчерпан допустимый объем памяти”. Что я могу сделать, чтобы разрешить числовые ярлыки для дочерних страниц?
add_filter( 'wp_unique_post_slug', function ( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ), true )
|| ( 'inherit' === $post_status && 'revision' === $post_type ) || 'user_request' === $post_type
) {
return $slug;
}
$override_slug = apply_filters( 'pre_wp_unique_post_slug', null, $slug, $post_ID, $post_status, $post_type, $post_parent );
if ( null !== $override_slug ) {
return $override_slug;
}
global $wpdb, $wp_rewrite;
$original_slug = $slug;
$feeds = $wp_rewrite->feeds;
if ( ! is_array( $feeds ) ) {
$feeds = array();
}
if ( 'attachment' === $post_type ) {
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );
$is_bad_attachment_slug = apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug', false, $slug );
if ( $post_name_check
|| in_array( $slug, $feeds, true ) || 'embed' === $slug
|| $is_bad_attachment_slug
) {
$suffix = 2;
do {
$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID ) );
$suffix++;
} while ( $post_name_check );
$slug = $alt_post_name;
}
} elseif ( is_post_type_hierarchical( $post_type ) ) {
if ( 'nav_menu_item' === $post_type ) {
return $slug;
}
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) );
$is_bad_hierarchical_slug = apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent );
if ( $post_name_check
|| in_array( $slug, $feeds, true ) || 'embed' === $slug
|| $is_bad_hierarchical_slug
) {
$suffix = 2;
do {
$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) );
$suffix++;
} while ( $post_name_check );
$slug = $alt_post_name;
}
} else {
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
$post = get_post( $post_ID );
$conflicts_with_date_archive = false;
if ( 'post' === $post_type && ( ! $post || $post->post_name !== $slug ) && preg_match( '/^[0-9]+$/', $slug ) ) {
$slug_num = (int) $slug;
if ( $slug_num ) {
$permastructs = array_values( array_filter( explode( "https://wordpress.stackexchange.com/", get_option( 'permalink_structure' ) ) ) );
$postname_index = array_search( '%postname%', $permastructs, true );
if ( 0 === $postname_index ||
( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && 13 > $slug_num ) ||
( $postname_index && '%monthnum%' === $permastructs[ $postname_index - 1 ] && 32 > $slug_num )
) {
$conflicts_with_date_archive = true;
}
}
}
$is_bad_flat_slug = apply_filters( 'wp_unique_post_slug_is_bad_flat_slug', false, $slug, $post_type );
if ( $post_name_check
|| in_array( $slug, $feeds, true ) || 'embed' === $slug
|| $conflicts_with_date_archive
|| $is_bad_flat_slug
) {
$suffix = 2;
do {
$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) );
$suffix++;
} while ( $post_name_check );
$slug = $alt_post_name;
}
}
return apply_filters( 'wp_unique_post_slug', $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug );
}, 10, 6 );
Обновленный ответ — @Quasimodo подтвердил, что его интересуют только записи типа page
и что числовые ярлыки — это четырехзначные годы, например 2019
.
Что я могу сделать, чтобы разрешить числовые ярлыки для дочерних страниц?
Я бы использовал pre_wp_unique_post_slug
хук, который выполняется перед wp_unique_post_slug
, что означает фильтрацию ярлыка до того, как будет сгенерирован уникальный (WordPress), и поэтому наш callback должен быть способен генерировать уникальный ярлык, если это необходимо.
Вот пример, опробованный и работающий с WordPress v5.9.3:
add_filter( 'pre_wp_unique_post_slug', 'my_pre_wp_unique_post_slug', 10, 6 );
function my_pre_wp_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type, $post_parent ) {
$allowed_post_status = array( 'publish', 'private' );
if ( preg_match( '/^\d{4}$/', $slug ) && $post_parent &&
'page' === $post_type && in_array( $post_status, $allowed_post_status )
) {
return my_unique_page_slug( $slug, $post_ID, $post_type );
}
return $override_slug;
}
function my_unique_page_slug( $slug, $post_ID, $post_type="page" ) {
global $wpdb;
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
if ( $post_name_check ) {
$suffix = 2;
do {
$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) );
$suffix++;
} while ( $post_name_check );
$slug = $alt_post_name;
}
return $slug;
}
Кстати: просто чтобы вы знали, wp_unique_post_slug
хук можно использовать, но ваш callback должен будет возвращать или работать со значением $original_slug
(последний параметр из хука) вместо $slug
(первый параметр из хука).
Таким образом, приведенный выше пример теперь позволяет нам использовать число в качестве ярлыка для дочерних страниц (тип записи page
), однако нам все еще нужно убедиться, что URL/постоянная ссылка работает, как ожидалось — потому что по умолчанию example.com/page-slug/<number>
рассматривается как запрос на разбиение на страницы, и WordPress попытается загрузить стр. 2, 3 и т.д. страницы с ярлыком page-slug
.
- Это также объясняет, почему WordPress по умолчанию запрещает использовать число в качестве ярлыка (записи).
Таким образом, шаг 2 для вас будет таким — добавьте пользовательское правило перезаписи, которое гарантирует, что постоянная ссылка example.com/parent-slug/<number>
загружает страницу с ярлыком <number>
и является дочерней к parent-slug
(или какой-либо другой правильный/фактический ярлык):
add_action( 'init', 'my_add_page_rewrite_rules' );
function my_add_page_rewrite_rules() {
add_rewrite_rule(
'^(.+/\d{4})(?:/(\d+))?/?$',
'index.php?pagename=$matches[1]&page=$matches[2]',
'top' );
}
Или вы можете использовать page_rewrite_rules
хук:
add_filter( 'page_rewrite_rules', 'my_page_rewrite_rules' );
function my_page_rewrite_rules( $page_rewrite ) {
return array(
'^(.+/\d{4})(?:/(\d+))?/?$' => 'index.php?pagename=$matches[1]&page=$matches[2]',
) + $page_rewrite;
}
И помните, есть шаг 3 — обновить правила перезаписи, просто посетив wp-admin
→ Настройки → Постоянные ссылки.
Что касается этого:
Но по какой-то причине я получаю критическую ошибку: “Исчерпан допустимый объем памяти”.
Это потому, что, как сказал @birgire в своем комментарии, «вы создаете бесконечный цикл, повторно применяя фильтр wp_unique_post_slug
в конце вашего обработки».
Таким образом, каждый раз, когда был вызван wp_unique_post_slug()
(например, wp_insert_post()
),
apply_filters()
(вwp_unique_post_slug()
) вызывал вашу обработку- Ваша обработка вызывала
apply_filters()
apply_filters()
(в вашей обработке) вызывал вашу обработку- Шаги 2 и 3 повторялись снова и снова…
И в итоге PHP выдал критическую ошибку, так как ваш код потреблял слишком много памяти.. :/
Я пробовал это и различные плагины, которые заявляют, что делают то же самое, но не знаю, изменилось ли поведение WP6.x. Мне не удается заставить какие-либо из этих решений работать – они все еще добавляют “-2” к каждому четырехзначному ярлыку, который я пытаюсь использовать.
Если @Quasimodo (https://wordpress.stackexchange.com/users/220387/quasimodo) все еще здесь, могли бы вы проверить, работает ли это на новых версиях WP, пожалуйста?
С уважением, Мадс
Ответ или решение
Для настройки числовых слагов для дочерних страниц в WordPress 5.9 необходимо выполнить несколько шагов. Вопрос сводится к тому, как добавить числовые слаги для дочерних страниц, и при этом избежать критической ошибки "Allowed memory size exhausted". Рассмотрим этот вопрос подробно, применяя подход TEA (теория, пример, применение).
Теория
WordPress изначально не поддерживает использование чисел в качестве слагов страниц, поскольку это может конфликтовать с системами пагинации и другими встроенными механизмами маршрутизации URL. Однако, используя возможности фильтров и хуков WordPress, такие как pre_wp_unique_post_slug
, мы можем переписать механизм формирования уникальных слагов и добавить необходимые правила для корректной работы числовых URL.
Проблема, с которой вы столкнулись, заключается в том, что происходит бесконечная рекурсия при применении фильтра wp_unique_post_slug
внутри вашей пользовательской функции, что в конечном итоге приводит к превышению лимита памяти.
Пример
-
Использование фильтра
pre_wp_unique_post_slug
: Это позволяет перехватывать процесс генерации слага до момента, когда он становится уникальным. Важно, чтобы ваша функция могла генерировать уникальный слаг, если это необходимо. -
Добавление Rewrite Rules: Чтобы числовые слаги корректно обрабатывались как часть URL, необходимо добавить пользовательские правила перезаписи (
rewrite rules
). Это гарантирует, что WordPress обрабатывает числовой слаг как часть маршрута к странице, а не как параметр пагинации. -
Избегание рекурсии: Важно убедиться, что ваш код не вызывает функции или фильтры рекурсивно, что вызывает превышение лимита памяти.
Применение
Шаг 1: Настройка вашего functions.php
Добавьте следующее в файл functions.php
вашей темы:
add_filter('pre_wp_unique_post_slug', 'handle_numerical_slug_for_pages', 10, 5);
function handle_numerical_slug_for_pages($override_slug, $slug, $post_ID, $post_status, $post_type, $post_parent) {
if ('page' === $post_type && preg_match('/^\d{4}$/', $slug) && $post_parent) {
return generate_unique_numeric_slug($slug, $post_ID, $post_type);
}
return $override_slug;
}
function generate_unique_numeric_slug($slug, $post_ID, $post_type = 'page') {
global $wpdb;
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN (%s, 'attachment') AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var($wpdb->prepare($check_sql, $slug, $post_type, $post_ID));
if ($post_name_check) {
$suffix = 2;
do {
$alt_post_name = $slug . '-' . $suffix;
$post_name_check = $wpdb->get_var($wpdb->prepare($check_sql, $alt_post_name, $post_type, $post_ID));
$suffix++;
} while ($post_name_check);
$slug = $alt_post_name;
}
return $slug;
}
Шаг 2: Добавление правила переписи (Rewrite Rule)
Чтобы внедрить поддержку числовых слагов в URL, добавьте следующее:
add_action('init', 'add_custom_page_rewrite_rules');
function add_custom_page_rewrite_rules() {
add_rewrite_rule(
'^(.+/\d{4})/?$',
'index.php?pagename=$matches[1]',
'top'
);
}
Шаг 3: Обновление пермалинков
Не забудьте очистить правила переписи URL. Для этого посетите панель администратора WordPress и перейдите в "Настройки" > "Постоянные ссылки". Просто сохраните изменения на этой странице, чтобы обновить правила переписи.
Заключение
Использование числовых слагов может быть полезно для организации контента, особенно если вам приходится иметь дело с данными, представляющими год или другие числовые значения. Предоставленный подход позволяет обойти ограничения WordPress и использовать числовые слаги для дочерних страниц безопасно и эффективно. Убедитесь, что ваш код не вызывает нежелательные рекурсивные процессы, и проверяйте все изменения на тестовой среде, прежде чем применять их на рабочем сайте.