Где, Когда и Как Правильно Сбрасывать Правила Перезаписи в Рамках Плагина?
У меня возникла странная проблема с некорректным сбросом правил перезаписи.
Я пробовал использовать flush_rewrite_rules();
и flush_rewrite_rules(true);
.
Я также пробовал глобализировать $wp_rewrite
используя $wp_rewrite->flush_rules();
и $wp_rewrite->flush_rules(true);
Ни один из этих методов не очищает правила перезаписи корректно. Эти вызовы действительно сбрасывают правила перезаписи при вызове. Откуда я это знаю? Используя решение для отладки сброса правил перезаписи.
В настоящее время у меня правила перезаписи сбрасываются при активации и деактивации плагина. С этим проблем нет.
У меня есть страница настроек администрирования плагина для пользователей. Некоторые настройки изменяют структуру постоянных ссылок, поэтому правила перезаписи должны быть сброшены при "Сохранении настроек" на странице администрирования плагина. (Использует стандартный update_option();
для сохранения настроек.)
Хочу отметить, что в зависимости от указанных настроек, создаются пользовательские типы записей, соответствующие указанным пользователем настройкам. Поэтому правила перезаписи должны быть сброшены сразу после сохранения настроек. Именно здесь всё работает некорректно.
Вышеуказанное решение для отладки правил перезаписи, предоставленное @toscho
, показывает, что сбрасывается множество правил перезаписи. Однако при посещении отдельной записи пользовательского типа или даже архива пользовательского типа, каждый возвращает ошибки 404.
Пользовательский тип записи зарегистрирован правильно и корректно. Я точно знаю, что проблема не в этом.
Сразу после сохранения настроек страницы администрирования плагина. Пользовательские типы записей создаются, структура постоянных ссылок корректируется, и выполняется попытка сброса всех правил перезаписи.
Пользовательские типы записей затем загружаются всегда и загружаются в init
как обычно.
По какой-то причине правила перезаписи не сбрасываются должным образом, потому что, как я сказал ранее, посещение отдельных записей или разделов архива пользовательского типа возвращает ошибки 404.
Теперь странная часть: если я просто захожу на страницу настроек постоянных ссылок в админке, а затем возвращаюсь на фронтенд для просмотра отдельных записей или разделов архива пользовательского типа, они волшебным образом работают как ожидалось.
Что делает эта страница настроек постоянных ссылок в админке, чего не делаю я, что позволяет правилам перезаписи сбрасываться правильно, а у меня нет?
То есть, как временное решение, я перенаправляю пользователя на страницу настроек постоянных ссылок после сохранения страницы настроек плагина, но это не идеальное решение. Я бы предпочел, чтобы правила перезаписи просто правильно сбрасывались в коде моего плагина.
Есть ли определенный момент в WordPress, когда сброс правил перезаписи просто перестает сбрасывать ВСЕ правила?
admin_menu
- Страница настроек плагина добавляется в администрирование WordPress.
add_options_page()
- Страница настроек плагина добавляется в меню Настройки.
Страница настроек отображается в callback-функции для add_options_page()
. Здесь же обрабатывается $_POST
для обновления настроек плагина и сброса правил перезаписи.
Поскольку это уже длинный вопрос, я готов предоставить блоки кода (если это поможет) по внешней ссылке, чтобы помочь получить правильный ответ.

Лучшее место для сброса правил перезаписи — это активация/деактивация плагина.
function myplugin_activate() {
// регистрируем таксономии/типы записей здесь
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'myplugin_activate' );
function myplugin_deactivate() {
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );
Заранее извиняюсь, я не до конца разобрался в вашем вопросе, так что это довольно шаблонный ответ.

Спасибо за предложение, но я в курсе. Проблема не в активации/деактивации плагина. Она связана с тем, что пользователи изменяют настройки уже активного плагина, что приводит к изменению правил перезаписи и требует их сброса.

Трудно сказать, что не так, не видя ваш код. Но после сохранения некоторых настроек целесообразно использовать хук admin_init
, как показано ниже, чтобы сбросить правила перезаписи.
Код:
add_action('admin_init', 'wpse_123401_plugin_settings_flush_rewrite');
function wpse_123401_plugin_settings_flush_rewrite() {
if ( get_option('plugin_settings_have_changed') == true ) {
flush_rewrite_rules();
update_option('plugin_settings_have_changed', false);
}
}
Вам нужно установить опцию где-то на странице настроек или, точнее, в процессе сохранения настроек. Делать это без опции — плохая практика, так как вы не хотите сбрасывать правила каждый раз.
Примечание: не тестировалось

Или можно использовать transient, возможно? Но определенно +1 за то, чтобы не сбрасывать правила при каждом admin_init.

Конечно, ты прав насчет transient, думаю я выбрал *_option()
из-за страницы настроек. @helgatheviking

Это было очень полезно. У меня была похожая ситуация, как у Майкла. В итоге я использовал предложение helga о transient и установил его в функции валидации настроек. В результате мне пришлось установить transient в false после сброса, иначе он продолжал сбрасываться при каждой загрузке админки, пока transient не истекал. Так что функционально использование option или transient было одинаковым. Наверное, transient может быть удобнее, чтобы немного очистить таблицу опций. Но это уже мелочи.

У меня был файл класса типов записей, который отвечал за чтение настроек плагина и создание необходимых пользовательских типов записей на основе указанных пользователем параметров.
Этот файл класса типов записей загружался на хуке init
.
Я предполагал, что мне нужно просто обновить настройки плагина, а затем сбросить правила перезаписи. Поскольку класс типов записей уже загружался на основе настроек плагина. Но с административными страницами ситуация иная — они загружаются ПОСЛЕ хука init
.
Типы записей фактически не регистрировались, потому что настройки ещё не были установлены. Класс регистрации типов записей завершался досрочно без регистрации каких-либо типов.
Решение оказалось следующим:
- Обновить настройки плагина.
- Загрузить файл класса типов записей ТОЛЬКО один раз в этом месте, чтобы создать новые правила перезаписи.
- Сбросить правила перезаписи.
(Ранее... шаг 2 отсутствовал — Как упоминалось выше...)
Теперь типы записей будут загружаться на хуке init
с уже указанными настройками, что позволит создавать типы записей и связывать их с соответствующими правилами перезаписи.
По какой-то причине мне пришлось добавить JavaScript-вызов для перенаправления на текущую страницу после выполнения трёх шагов.
Также мне потребовалось добавить вызов flush_rewrite_rules();
на странице настроек плагина в админ-панели.
Таким образом, для гарантированного сброса всего:
Шаг 1) Перейти на страницу настроек плагина в админ-панели — Первоначальный сброс.
Шаг 2) Обновить настройки плагина — Второй сброс.
Шаг 3) Перенаправление на страницу настроек плагина вызывает... Третий и окончательный сброс (аналогично первоначальному — выполняется автоматически при посещении страницы настроек)
Не могу сказать, что это практичное решение, но у меня оно сработало. Очень странная проблема, скорее всего связанная с архитектурой моего кода.

У меня была точно такая же проблема. В моем плагине есть типы записей, которые создаются динамически. Поэтому их нельзя зарегистрировать через register_post_type()
в статическом методе во время activation_hook
, и они ещё не активны, когда выполняется flush_rewrite_rules()
в этом хуке (что обычно является рекомендуемым способом сброса правил перезаписи).
Самое чистое решение, которое я в итоге придумал, — сбрасывать правила перезаписи после регистрации типов записей, но только если это действительно необходимо (поскольку операция медленная). В моем случае у меня несколько пользовательских типов записей, наследующихся от одного базового класса, поэтому было желательно реализовать код для сброса именно там.
Необходимость сброса можно определить, проверив результат get_option( 'rewrite_rules' )
:
class MyPostTypeClass {
public final function register_as_custom_post_type() {
... // выполните здесь всю настройку вашего типа записи
$args = array(
... // заполните остальные аргументы по своему усмотрению
'rewrite' => array('slug' => 'slug-of-your-post-type')
);
register_post_type('post-type-name-of-your-post-type', $args );
$rewrite_rules_must_be_fluhed = true;
foreach( get_option( 'rewrite_rules' ) as $key => $rule)
if(strpos($key, $args['rewrite']['slug'] ) === 0)
{
$rewrite_rules_must_be_fluhed = false;
break;
}
if($rewrite_rules_must_be_fluhed)
flush_rewrite_rules(true);
}
}
Недостатки:
- Частично зависит от того, какие именно правила перезаписи генерирует WP во время
register_post_type()
. - Проверка необходимости сброса при каждой загрузке страницы также создает некоторую нагрузку.
Преимущества:
- Полностью инкапсулировано в классе, представляющем тип записи.
- Сбрасывает правила перезаписи только при реальной необходимости.
Используйте это только если вы не можете зарегистрировать ваш тип записи в статической функции, которая может вызываться как во время init
, так и во время activation_hook
!
Зависимость от вида сгенерированных правил можно уменьшить, заменив проверку if(strpos($key, $args['rewrite']['slug'] ) === 0)
на более сложную, например, регулярное выражение.

@tazo-todua это также сработало у меня при использовании мультисайта.
add_action( 'wpmu_new_blog', 'set_my_permalink_structure', 11, 2 );
function set_my_permalink_structure( $blog_id ) {
switch_to_blog( $blog_id );
global $wp_rewrite;
$wp_rewrite->set_permalink_structure( '/%postname%/' );
$wp_rewrite->flush_rules();
$wp_rewrite->init();
restore_current_blog();
}

Если нужно выполнить flush_rewrite_rules при активации или деактивации ЛЮБОГО плагина
add_action('init', function(){
// Получаем пути всех установленных плагинов
$pluginsInstalledPaths = glob(WP_PLUGIN_DIR . '/*', GLOB_ONLYDIR);
foreach ($pluginsInstalledPaths as $pluginPath) {
// Получаем все PHP-файлы в корневой директории плагина
$files = glob($pluginPath . '/*.php');
foreach ($files as $file) {
$contents = file_get_contents($file);
// Если файл содержит "Plugin Name" - это главный файл плагина
if (preg_match('/\/\*\*[^\/]*?Plugin Name:/', $contents)) {
add_action('activate_' . plugin_basename($file), function(){
flush_rewrite_rules();
});
add_action('deactivate_' . plugin_basename($file), function(){
flush_rewrite_rules();
});
}
}
}
});
