WP_Rewrite

Кому как конечно, но я считаю, что это одна из самых непростых тем в WordPress. Я могу просто описать, что класс WP_Rewrite нужен для того-то, у него есть такие-то методы и свойства и привести пару примеров — всё, как в официальной документации WordPress. Но так я делать не стану, потому что в таком случае от статьи будет мало толку и никто в итоге так и не поймёт, как работать с этим классом.

Поэтому я начну с примеров. Думаю, что многие из этих примеров вам уже знакомы. Некоторые даже реализованы в виде плагинов.

И ещё кое-что, в посту подразумевается, что у вас есть некоторые базовые знания:

  • вы примерно знаете, что такое регулярные выражения и mod_rewrite,
  • знаете, куда какой код вставлять в теме/плагине,
  • вы примерно представляете себе, что такое классы и объекты PHP.

Пример 1. Как добавить .html для страниц в WordPress?

То есть страницы по умолчанию в WP не имеют никакого расширения и в настройках постоянных ссылок мы его не можем им добавить. Так как в итоге добавить для страниц .html, .htm, .pl, .phtml и так далее?

Открывайте свой functions.php, потому что работать мы будем в основном только в нём.

function true_add_html_on_pages() {
	// при работе с $wp_rewrite, как и с $wpdb, в первую очередь глобально определяем класс
	global $wp_rewrite;
 
	// метод $wp_rewrite->get_page_permastruct() возвращает нам структуру URL страниц, по умолчанию %pagename%
	// функцией strpos() мы делаем проверку, не присутствует ли уже расширение .html (%pagename%.html)
	if ( !strpos($wp_rewrite->get_page_permastruct(), '.html')){
		// если не присутствует, то добавляем его
		$wp_rewrite->page_structure = $wp_rewrite->page_structure . '.html';
		// метод flush_rules() применяет сделанные изменения
		$wp_rewrite->flush_rules();
	}
}
 
// вешаем на хук init
add_action('init', 'true_add_html_on_pages', -1);
 
// вроде бы всё ок, вот только WordPress добавляет слэши на конце и у нас получается %pagename%.html/ - сейчас мы это исправим
function true_remove_slash_on_pages( $url, $post_type){
	global $wp_rewrite;
 
	// $wp_rewrite->using_permalinks() возвращает true, если постоянные ссылки используются на сайте
	// $wp_rewrite->use_trailing_slashes равен true, если слэши добавляются на конце URL
	// нам нужно удалить слэши только с страниц, поэтому $post_type должен быть равен page
	if ($wp_rewrite->using_permalinks() && $wp_rewrite->use_trailing_slashes==true && $post_type == 'page'){
		// удаляем слэш
		return untrailingslashit( $url );
	} else {
		// возвращаем всё как есть
		return $url;
	}
}
 
// вешаем на фильтр user_trailingslashit
add_filter('user_trailingslashit', 'true_remove_slash_on_pages',70,2);

А теперь подробные комментарии к этому примеру.

  • Глобальный объект $wp_rewrite содержит в себе всю необходимую информацию о структуре URL в WordPress. О его методах и свойствах будет написано чуть ниже.
  • $post_type = 'page' в данном примере это тип постов — страницы.
  • untrailingslashit() это простейшая функция ядра WordPress, которая просто удаляет все слэши с конца строки.

Свойства объекта WP_Rewrite + примеры

Как я уже говорил, мы разбираться с WP_Rewrite мы будем поэтапно, и сейчас мы поглядим на его свойства.

$wp_rewrite->permalink_structure
Структура URL, которая определена на странице «Настройки > Постоянные ссылки», например /%postname%/.
$wp_rewrite->use_trailing_slashes
Будет ли WordPress автоматически добавлять слэши на конце ссылок. По умолчанию true — добавляются. Как сделать так, чтобы не добавлялись? Вы можете использовать фильтр user_trailingslashit из примера выше, а можете изменить значение напрямую в $wp_rewrite:

function true_no_trailingslashes() {
	global $wp_rewrite;
	$wp_rewrite->use_trailing_slashes = false;
	// даже не нужно обновлять правила через flush_rules()
}
add_action('init', 'true_no_trailingslashes', 1);
$wp_rewrite->rewritecode
Полный список (массив) тегов, доступных для использования в постоянных ссылках, например у меня он такой:

[rewritecode] => Array
        (
            [0] => %year% // год
            [1] => %monthnum% // номер месяца
            [2] => %day% // число
            [3] => %hour% // час
            [4] => %minute% // минуты
            [5] => %second% // секунды
            [6] => %postname% // ярлык поста
            [7] => %post_id% // ID поста
            [8] => %author% // автор поста (его nicename/логин)
            [9] => %pagename% // ярлык страницы
            [10] => %search% // поисковой запрос
            [11] => %category% // ярлык категории
            [12] => %post_tag% // ярлык метки
            [13] => %post_format% // название формата поста
            [14] => %product% // ярлык записи произвольного типа
       )
$wp_rewrite->rewritereplace
Назову этот параметр массивом шаблонов доступных значений для каждого значения rewritecode и приведу пример — возьмем rewritecode[0] — это год, значением может быть четырехзначное число, и правило для года rewritereplace[0] будет соответствующим ([0-9]{4}).
$wp_rewrite->queryreplace
Массив параметров URL, соответсвующих для каждого rewritecode, например для года year=.
$wp_rewrite->author_base, $wp_rewrite->pagination_base, $wp_rewrite->search_base, $wp_rewrite->feed_base
Префикс URL страниц соответственно: архивов автора (по умолчанию author), постраничной навигации (page), поиска (search), фидов (feed).

То есть напрмер URL страницы архивов автора http://адрес-сайта/author/misha/. Мы можем легко и просто его изменить, напрмиер на writer:

function true_writer_not_an_author() {
	global $wp_rewrite;
	$wp_rewrite->author_base = 'writer';
	$wp_rewrite->flush_rules(); // лучше применяйте изменения через Настройки > Постоянные ссылки
}
 
add_action('init', 'true_writer_not_an_author', 1);
$wp_rewrite->author_structure, $wp_rewrite->date_structure, $wp_rewrite->page_structure, $wp_rewrite->feed_structure, $wp_rewrite->comment_feed_structure, $wp_rewrite->search_structure
Полная структура URL соответственно: страниц архивов автора (по умолчанию — /author/%author%), архивов по дате (/%year%/%monthnum%/%day%, но может быть в другом порядке, в зависимости от настроек постоянных ссылок), страниц (%pagename%), фида комментария (comments/feed/%feed%), поиска (%search%).

Зависит от предыдущего параметра (base).

$wp_rewrite->root
Корень установки WordPress, добавляется ко всем структурам. По умолчанию index.php.
$wp_rewrite->front

То, что находится непосредственно перед структурой постоянных ссылок, напрмиер на скриншоте это /blog/:

свойство front в данном случае равно /blog/
$wp_rewrite->rewrite_rules
Все правила перезаписи URL для сайта. Значение этого параметра хранится в базе данных, его можно получить при помощи $rewrite_rules = get_option( 'rewrite_rules' );.

Сам этот массив выглядит примерно следующим образом (я заскринил только верхнюю его часть, так как он довольно большой).

массив правил перезаписи URL

Прямо в него можно добавлять и свои правила.

$wp_rewrite->non_wp_rules
Ассоциативный массив правил, которые не обрабатываются самим WordPress вообще, а идут прямиком в .htaccess.

function true_generate_rewrite_rules() {
	// не забываем обращаться к глобальной переменной $wp_rewrite
	global $wp_rewrite;
	// вот такой вот массив, правил в нем может быть сколько угодно, я добавил лишь два
	$non_wp_rules = array(
		'dir1/(.*)$' => 'dir1.php?param=$1',
		'dir2/(.*)$' => 'direct25.php?param=$1'
	);
	// объелиняем те $non_wp_rules, которые уже используются с нашими
	$wp_rewrite->non_wp_rules = $non_wp_rules + $wp_rewrite->non_wp_rules;
	// не забываем про flush_rules() - либо так, но лучше через Настройки -> Постоянные ссылки
	//$wp_rewrite->flush_rules();
}
 
// правильнее всего повесить функцию на специально предназначенный хук generate_rewrite_rules
add_action('generate_rewrite_rules', 'true_generate_rewrite_rules');

Данный код добавит в .htaccess следующие строки:

RewriteRule ^dir1/(.*)$ /dir1.php?param=$1 [QSA,L]
RewriteRule ^dir2/(.*)$ /direct25.php?param=$1 [QSA,L]

Теперь по порядку:

  • Массив $wp_rewrite->non_wp_rules — это правила, которые добавляются непосредственно в .htaccess под флагом [QSA,L]
  • Если на сайте мы перейдём по URL http://адрес-сайта/dir1/параметр, то, содержимым страницы будет http://адрес-сайта/dir1.php?param=параметр, при этом сам URL не изменится.

Пример 2. Постоянные ссылки произвольного типа записей в WordPress. Как удалить префикс типа записи из URL

За последнюю неделю или две очень многие люди связывались со мной (через сайт / через комментарии / и в вк) и спрашивали, как удалить префикс типа записи из URL, то есть сделать так, чтобы URL имели следующий вид http://адрес-сайта/ярлык-типа-записи.

Не знаю, с чего вдруг так резко это всем понадобилось, но буду рад помочь. Но имейте ввиду две вещи:

  1. Если URL типов записей будут иметь такой вид, то ярлыки обычных записей и страниц не должны совпадать с ярлыками записей данного типа!
  2. А ещё это чистой воды шаманство, так что могут быть баги, если обнаружите — пожалуйста пишите в комментариях к этому посту.

Шаг 1. Изменяем параметр rewrite регистрируемого типа записей

Для начала, во время непосредственно регистрации типа функцией register_post_type() рекомендую указать параметр rewrite равным false.

Шаг 2. Настраиваем структуру постоянных ссылок данного типа записей

Затем в код сайта отправляется следующее:

function true_post_type_rewrite() {
	global $wp_rewrite;
	// в данном случае мой тип записей был - Товары (product)
	// этот код позволит перезаписать урлы
	// параметры add_rewrite_tag('%название_тега%', '%маска_символов%', '%url_параметр%')
	$wp_rewrite->add_rewrite_tag("%product%", '([^/]+)', "product=");
	$wp_rewrite->add_permastruct('product', '%product%' );
}
 
add_action( 'init', 'true_post_type_rewrite');

После добавления этого кода не забудьте зайти в настройки постоянных ссылок и сохранить изменения.

Шаг 3. Исправляем ошибку 404

После установки кода из предыдущего примера у вас вряд ли что-то заработает — при переходе на страницу товара мы будем получать ошибку 404, якобы товар не найден.

Исправляется на самом деле легко:

function true_rewrite_conflicts( $request ) {
	if(!is_admin())
		$request['post_type'] = array('product', 'post', 'page'); // перечисляем типы записей с подобной структурой пермалинков
	return $request;
}
add_filter( 'request',  'true_rewrite_conflicts' )

Методы объекта WP_Rewrite

Как я уже писал выше, в этом посту подразумевается, что у вас уже есть базовые знания о классах, объектах, их методах и свойствах.

$wp_rewrite->using_permalinks()
Возвращает true, если на сайте включены постоянные ссылки, false — если не включены (то есть URL имеют вид ?p=14748.
$wp_rewrite->get_page_permastruct()
Возвращает структуру постоянных ссылок для страниц, по умолчанию %pagename%.
$wp_rewrite->get_date_permastruct()
Возвращает структуру постоянных ссылок для архивов по дате, по умолчанию /%year%/%monthnum%/%day%.
$wp_rewrite->get_category_permastruct()
Возвращает структуру постоянных ссылок для рубрик, по умолчанию /category/%category%.
$wp_rewrite->get_author_permastruct()
Возвращает структуру постоянных ссылок для страниц архивов автора, по умолчанию /author/%author%.
$wp_rewrite->flush_rules()
Применяет новые значения массива $wp_rewrite->rewrite_rules, сохраняет их в базу данных.
$wp_rewrite->add_rewrite_tag( $tag, $pattern, $query )
Функция добавляет переменную $tag в массив $wp_rewrite->rewritecode, $pattern — в $wp_rewrite->rewritereplace и $query в $wp_rewrite->queryreplace. Подробнее про каждый из этих массивов написано выше, в свойствах класса.
$wp_rewrite->add_permastruct( $name, $struct )
Позволяет добавить структуру постоянных ссылок, где $name — это название структуры, $struct — собственно структура.
$wp_rewrite->mod_rewrite_rules()
Возвращает список правил для .htaccess в виде строки.
$wp_rewrite->set_permalink_structure()
Про этот метод на моем блоге есть отдельный пост.

На самом деле про метод WP_Rewrite можно написать ешё столько же (и даже больше), но пожалуй я лучше разобью информацию на отдельные статьи. Если у вас есть вопросы или дополнения к посту — пожалуйста оставляйте их в комментариях.

Миша Рудрастых Разработчик WordPress WooCommerce

Миша Рудрастых

Впервые познакомился с WordPress в 2009 году, и после двух лет мучений с Joomla и самописными движками это был просто бальзам на душу. С 2014 года меня можно встретить на WordCamp — официальной конфе по WP в Москве, иногда там выступаю. Также в настоящее время веду курсы по WordPress в Epic Skills в Питере.

По теме