WordPress как на ладони
Шаблоны, плагины и темы для настоящих поклонников Elementor. От TemplateMonster.com wordpress jino

WP_Rewrite{} WP 1.5

Класс ядра, который разбирает запрашиваемый URL (ЧПУ) и создает из него понятный для WordPress "внутренний" URL. Этот класс используется как API для создания ЧПУ (человеко-понятных ссылок).

С помощью этого класса WordPress понимает какую страницу нужно показать: страницу записи, категории, архива и т.д. Например, мы запрашиваем страницу http://site.ru/mypage WP_Rewrite разбирает этот ЧПУ и переделывает его в вид: http://site.ru/index.php?pagename=mypage, а дальше WordPress работает с параметрами запроса. Параметры запроса фильтруются через "белый список" и остаются там только разрешенные. Нужно это для защиты. Получить значение любой переменной запроса можно с помощью функции get_query_var().

Работа класса WP_Rewrite тесно связана с классом WP{}. WP_Rewrite разбирает и делает понятным для WordPress текущий URL (ЧПУ), а WP создает и обрабатывает "уже понятный URL" и на основе результата устанавливает глобальные переменные, и выдает нужную страницу сайта.

С помощью класса WP_Rewrite можно научить WordPress понимать ЧПУ любого рода. Также, можно научить пропускать указанные URL на уровне файла .htaccess (см. метод WP_Rewrite::add_external_rule($regex, $query)).

Этот класс работает только для внешней части сайта (фронтенда). В админке ЧПУ не используются.

Правила перезаписи ЧПУ в понятный URL сохраняются в базе данных, в таблице wp_options, опция rewrite_rules. Опция обновляется при обновлении «постоянных ссылок» в админке WordPress или при сбросе правил с помощью flush_rewrite_rules().

Является основой для: remove_permastruct(), add_permastruct(), remove_rewrite_tag()
Хуки из класса:
Возвращает

Экземпляр класса.

Свойства класса

$permalink_structure(строка)
Структура ЧПУ для постов.
По умолчанию: null
$use_trailing_slashes(логический)
Нужно ли добавлять замыкающий слэш на конце ссылок?
По умолчанию: null
$author_base(строка)
База для ЧПУ страницы авторов: example.com/$author_base/authorname
По умолчанию: 'author'
$author_structure(строка)
Структура ЧПУ для архива авторов.
По умолчанию: null
$date_structure(строка)
Структура ЧПУ для архива даты.
По умолчанию: null
$page_structure(строка)
Структура ЧПУ для постоянных страниц.
По умолчанию: null
$search_base(строка)
База для ЧПУ страницы поиска: example.com/$search_base/query
По умолчанию: 'search'
$search_structure(строка)
Структура ЧПУ для страницы поиска.
По умолчанию: null
$comments_base(строка)
База для ЧПУ комментария.
По умолчанию: 'comments'
$pagination_base(строка)
База для ЧПУ пагинации.
По умолчанию: 'page'
$comments_pagination_base(строка)
База для ЧПУ пагинации комментария.
По умолчанию: 'comment-page'
$feed_base(строка)
База для ЧПУ фида.
По умолчанию: 'feed'
$comment_feed_structure(строка)
Структура ЧПУ для URL фида.
По умолчанию: null
$feed_structure(строка)
Структура ЧПУ для URL под запрос (request) фида.
По умолчанию: null
$front(строка)
Префикс структуры ЧПУ. Если структура ЧПУ выглядит так: /ID-%post_id%, тогда префикс будет равен /ID-. А если так: /%year%/%postname%/, то префикс = /.
Подробнее смотрите: WP_Rewrite::init()
По умолчанию: null
$root(строка)
Префикс для всех структур ЧПУ.
Если используется структура PATHINFO или index, то этот параметр равен значению свойства WP_Rewrite::$index с замыкающим слэшем на конце.
Смотрите: WP_Rewrite::init()
Смотрите: WP_Rewrite::using_index_permalinks()
По умолчанию: ''
$index(строка)
Название файла, на который должны передаваться все запросы.
По умолчанию: 'index.php'
$matches(строка)
Название переменной которая будет использоваться в регулярных выражениях в строке результирующего запроса, пр.: index.php?pagename=$matches[1].
По умолчанию: ''
$extra_rules(массив)
Дополнительные правила перезаписи, добавленные в класс из вне.
Они не создаются классом автоматически. Подробнее см.: add_rewrite_rule().
По умолчанию: array()
$rules(массив)
Правила перезаписи URL на основе которых будет получен текущий запрос или сделан редирект. Это, можно сказать, основное свойство в котором хранятся все основные правила перезаписи URL.
По умолчанию: null
$extra_rules_top(массив)
Дополнительные правила перезаписи, которые будут выполняться в самом начале, т.е. раньше правил из свойств: $extra_rules и $rules.
Они не создаются классом автоматически. Подробнее см.: add_rewrite_rule().
По умолчанию: array()
$non_wp_rules(массив)
Правила которые не перенаправляют (не смотрят) на файл index.php.
Эти правила записываются в порцию правил mod_rewrite в .htaccess. Для добавления таких правил используйте метод WP_Rewrite::add_external_rule($regex, $query).
По умолчанию: array()
$extra_permastructs(массив)
Дополнительные структуры ЧПУ, например к ним относятся ЧПУ рубрик, которые добавляются с помощью функции add_permastruct().
По умолчанию: array()
$use_verbose_rules(логический)
Если установить в true, то все правила перезаписи будут записываться как правила mod_rewrite в файле .htaccess.
По умолчанию функция отключена, включение этой опции добавить много правил в .htaccess.
Смотрите также: WP_Rewrite::mod_rewrite_rules()
По умолчанию: false
$endpoints(массив)
Конечные точки наподобие /trackback/ для типов ЧПУ (например для постов и страниц), добавляются функцией add_rewrite_endpoint( $name, $places ).
По умолчанию:
$use_verbose_page_rules(логический)

Похожа ли структура ЧПУ постов на структуру постоянных страниц?

Если первый тег в ЧПУ постов может быть похож на название страницы, например ЧПУ начинается с %postname% или %author%, то может быть путаница, в таких случаях нужно установить этот флаг в true, для дополнительной проверки типа записи.

До версии 3.3. этот флаг выставлял ЧПУ страниц вверх всех правил, чтобы они проверялись первыми. А теперь этот флаг говорит WP::parse_request() проверять URL на схожесть со структурой страниц и если это так, то перед принятием ЧПУ проверяется тип записи - действительно ли это страница.
По умолчанию: true

$rewritecode(массив)

Теги перезаписи, которые могут быть использованы в структуре ЧПУ. Добавляются с помощью функции add_rewrite_tag().

Эти теги переводятся в соответствующие части регулярного выражения, которые указываются в свойстве $rewritereplace, а затем части регулярного выражения переписываются в соответствующую часть запроса, которая находится в свойстве $queryreplace.

По умолчанию:

array(
	'%year%',
	'%monthnum%',
	'%day%',
	'%hour%',
	'%minute%',
	'%second%',
	'%postname%',
	'%post_id%',
	'%author%',
	'%pagename%',
	'%search%'
)
$rewritereplace(массив)

Части регулярного выражения, которыми будет заменен тег из свойства $rewritecode.

По умолчанию:

array(
	'([0-9]{4})',
	'([0-9]{1,2})',
	'([0-9]{1,2})',
	'([0-9]{1,2})',
	'([0-9]{1,2})',
	'([0-9]{1,2})',
	'([^/]+)',
	'([0-9]+)',
	'([^/]+)',
	'([^/]+?)',
	'(.+)'
)
$queryreplace(массив)

Параметры запроса, значение которого будет установлено из вхождения части регулярного выражения из свойства $rewritereplace.

По умолчанию:

array(
	'year=',
	'monthnum=',
	'day=',
	'hour=',
	'minute=',
	'second=',
	'name=',
	'p=',
	'author_name=',
	'pagename=',
	's='
)
$feeds(массив)
Поддерживаемые фиды.
По умолчанию: array( 'feed', 'rdf', 'rss', 'rss2', 'atom' )

Методы класса

__construct()
Конструктор - вызывает метод init().
add_endpoint( $name, $places, $query_var = true )
Добавляет конечную точку, например /trackback/. Добавляются дополнительные правила перезаписи ЧПУ с указанной конечной точкой. Смотрите описание функции add_rewrite_endpoint().
add_external_rule( $regex, $query )
Добавляет правила перезаписи, которое не относится к WordPress к файлу index.php.
add_permastruct( $name, $struct, $args = array() )
Позволяет добавить дополнительную структуру постоянных ссылок для создания ЧПУ. Смотрите описание функции add_permastruct().
add_rewrite_tag( $tag, $regex, $query )
Добавляет/обновляет тег запроса (например '%pagetype%') который можно использовать в ЧПУ. Смотрите описание функции add_rewrite_tag().
add_rule( $regex, $query, $after = 'bottom' )
Добавляет дополнительное правило перезаписи URL (ЧПУ) во внутренний url wordpress с параметрами запроса. Создает дополнительное правило. Смотрите описание функции add_rewrite_rule().
flush_rules( $hard = true )
Обновляет правила перезаписи ссылок (ЧПУ) в базе данных и кэше. Смотрите описание функции flush_rewrite_rules().
generate_rewrite_rule( $permalink_structure, $walk_dirs = false )
Создает правила на основе указанной ЧПУ структуры. Правила создаются только для директорий указаны в ЧПУ (параметр walk_dirs из функции add_permastruct()). Это урезанный вызов метода generate_rewrite_rules()
generate_rewrite_rules( $permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true )
Создает правила перезаписи на основе указанной ЧПУ структуры.
get_author_permastruct()
Получает структуру ЧПУ для страницы автора.
get_category_permastruct()
Получает структуру ЧПУ для страницы рубрик.
get_comment_feed_permastruct()
Получает структуру ЧПУ для страницы фида комментариев.
get_date_permastruc()
Получает структуру ЧПУ для страницы даты: год, месяц и день.
get_year_permastruct()
Получает структуру ЧПУ для страницы года: дата без дня и месяца.
get_month_permastruct()
Получает структуру ЧПУ для страницы месяца: дата без дня.
get_day_permastruct()
Получает структуру ЧПУ для страницы дня. Идентично get_date_permastruc().
get_extra_permastruct( $name )
Получает произвольную структуру ЧПУ указанную в параметре $name.
get_feed_permastruct()
Получает структуру ЧПУ для страницы фида.
get_page_permastruct()
Получает структуру ЧПУ для постоянной страницы.
get_search_permastruct()
Получает структуру ЧПУ для страницы поиска.
get_tag_permastruct()
Получает структуру ЧПУ для страницы меток.
iis7_url_rewrite_rules()
Получает правила перезаписи в формате IIS7 для записи в файл web.config.
init()
Инициализация всего класса, устанавливает свойства объекта.
mod_rewrite_rules()
Получает правила перезаписи предназначенные для записи в файл .htaccess.
page_rewrite_rules()
Получает все правила перезаписи для постоянных страниц.
page_uri_index()

Получает пути URL всех постоянных страниц и их вложений. Вернет такой массив:

Array (
	[0] => Array (
			[about/portfolio] => 3644
			[about] => 7
		)

	[1] => Array (
			[about/photo1] => 824
			[about/photo2] => 5484
		)
)
preg_index( $number )
Индексы для вхождений используемых в функциях preg_*().
remove_permastruct( $name )
Удаляет структуру ЧПУ по названию. См. remove_permastruct()
remove_rewrite_tag( $tag )
Удаляет тег перезаписи. См. remove_rewrite_tag()
rewrite_rules()
Создает правила перезаписи и соответствующие им запросы на основе структур ЧПУ. Создаваемые правила сохраняются в опцию get_option( 'rewrite_rules' ) при вызове метода wp_rewrite_rules().
set_category_base( $category_base)
Устанавливает базу категорий для URL категорий.
set_permalink_structure( $permalink_structure )
Устанавливает основную структуру ЧПУ для сайта.
set_tag_base( $tag_base )
Устанавливает базу меток для URL меток.
using_index_permalinks()
Определяет используются ли ЧПУ для index.php и модуль перезаписи не активирован.
using_mod_rewrite_permalinks()
Определяет используются ли ЧПУ для mod_rewrite и модуль перезаписи не активирован.
using_permalinks()
Определяет используются ли ЧПУ.
wp_rewrite_rules()
Получает все правила перезаписи которые возвращает метод rewrite_rules(). И обновляет опцию get_option( 'rewrite_rules' ).

Функции-обертки для класса WP_Rewrite

Для удобного использования WP_Rewrite в wordpress существуют функции-обертки:

  • add_rewrite_rule() - Добавляет правила для ЧПУ. Создает дополнительные правила перезаписи УРЛ.

  • add_rewrite_tag() - Добавляет переменную запроса в WordPress. ?pagetype=mypage, здесь pagetype — переменная запроса.

  • remove_rewrite_tag() - Удаляет существующий тег запроса, например %postname%.

  • flush_rewrite_rules() - Обновляет правила перезаписи ЧПУ: удаляет имеющиеся, генерирует и записывает новые.

  • add_permastruct() - Позволяет добавить дополнительную структуру постоянных ссылок для создания ЧПУ.

  • remove_permastruct() - Удаляет ранее добавленную структуру ЧПУ (постоянных ссылок).

Хуки класса WP_Rewrite для плагинов

Так как ЧПУ - это часть системы и функционала сайта, WordPress позволяет внедряться в процесс генерации правил перезаписи URL. Метод rewrite_rules() содержит 9 фильтров:

  • post_rewrite_rules - изменяет правила перезаписи URL постов. Передает: $post_rewrite
  • date_rewrite_rules - изменяет правила перезаписи URL дат. Передает: $date_rewrite
  • {$permastructname}_rewrite_rules - изменяет правила перезаписи любой структуры ЧПУ, название указывается в $permastructname. Передает: $rules
  • search_rewrite_rules - правила перезаписи URL поиска.
  • comments_rewrite_rules - правила перезаписи URL фида последних комментариев: /comments/feed/.
  • author_rewrite_rules - правила перезаписи URL авторов.
  • page_rewrite_rules - правила перезаписи URL постоянных страниц.
  • root_rewrite_rules - правила перезаписи URL корня сайта.
  • generate_rewrite_rules - это событие срабатывает после того, как все правила созданы. Передает весь объект WP_Rewrite по ссылке.
  • rewrite_rules_array - изменяет все правила перезаписи в одном массиве. Фильтр срабатывает в самом конце.

  • mod_rewrite_rules - изменяет все правила, аналог фильтра generate_rewrite_rules только для правил файла .htaccess.

Примеры

#1 Как выглядит глобальная переменная $wp_rewrite

WP_Rewrite Object
(
	[permalink_structure] => /id_%post_id%/%postname%.html
	[use_trailing_slashes] => 
	[author_base] => author
	[page_structure] => %pagename%
	[search_base] => search
	[comments_base] => comments
	[pagination_base] => page
	[comments_pagination_base] => comment-page
	[feed_base] => feed
	[front] => /id_
	[root] => 
	[index] => index.php
	[matches] => 
	[rules] => Array
		(
			[(filecode)(/.*)$] => index.php?pagename=$matches[1]&file_path=$matches[2]
			[(sitemap)/([^/]*)/?] => index.php?pagename=$matches[1]&pagetype=$matches[2]
			[article/?$] => index.php?post_type=article
			[article/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_type=article&feed=$matches[1]
			[article/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_type=article&feed=$matches[1]
			[article/page/([0-9]{1,})/?$] => index.php?post_type=article&paged=$matches[1]
			[cat/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?category_name=$matches[1]&feed=$matches[2]
			[cat/(.+?)/(feed|rdf|rss|rss2|atom)/?$] => index.php?category_name=$matches[1]&feed=$matches[2]
			[cat/(.+?)/embed/?$] => index.php?category_name=$matches[1]&embed=true
			[cat/(.+?)/page/?([0-9]{1,})/?$] => index.php?category_name=$matches[1]&paged=$matches[2]
			[cat/(.+?)/?$] => index.php?category_name=$matches[1]
			[met/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?tag=$matches[1]&feed=$matches[2]
			[met/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?tag=$matches[1]&feed=$matches[2]
			[met/([^/]+)/embed/?$] => index.php?tag=$matches[1]&embed=true
			[met/([^/]+)/page/?([0-9]{1,})/?$] => index.php?tag=$matches[1]&paged=$matches[2]
			[met/([^/]+)/?$] => index.php?tag=$matches[1]
			[id_type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_format=$matches[1]&feed=$matches[2]
			[id_type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_format=$matches[1]&feed=$matches[2]
			[id_type/([^/]+)/embed/?$] => index.php?post_format=$matches[1]&embed=true
			[id_type/([^/]+)/page/?([0-9]{1,})/?$] => index.php?post_format=$matches[1]&paged=$matches[2]
			[id_type/([^/]+)/?$] => index.php?post_format=$matches[1]
			[robots\.txt$] => index.php?robots=1
			[.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$] => index.php?feed=old
			[.*wp-app\.php(/.*)?$] => index.php?error=403
			[.*wp-register.php$] => index.php?register=true
			[feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]
			[(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]
			[ embed/?$] => index.php?&embed=true
			[page/?([0-9]{1,})/?$] => index.php?&paged=$matches[1]
			[comments/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]&withcomments=1
			[comments/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]&withcomments=1
			[comments/embed/?$] => index.php?&embed=true
			[search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?s=$matches[1]&feed=$matches[2]
			[search/(.+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?s=$matches[1]&feed=$matches[2]
			[search/(.+)/embed/?$] => index.php?s=$matches[1]&embed=true
			[search/(.+)/page/?([0-9]{1,})/?$] => index.php?s=$matches[1]&paged=$matches[2]
			[search/(.+)/?$] => index.php?s=$matches[1]
			[id_author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?author_name=$matches[1]&feed=$matches[2]
			[id_author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?author_name=$matches[1]&feed=$matches[2]
			[id_author/([^/]+)/embed/?$] => index.php?author_name=$matches[1]&embed=true
			[id_author/([^/]+)/page/?([0-9]{1,})/?$] => index.php?author_name=$matches[1]&paged=$matches[2]
			[id_author/([^/]+)/?$] => index.php?author_name=$matches[1]
			[id_date/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
			[id_date/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
			[id_date/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true
			[id_date/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
			[id_date/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
			[id_date/([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
			[id_date/([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
			[id_date/([0-9]{4})/([0-9]{1,2})/embed/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&embed=true
			[id_date/([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
			[id_date/([0-9]{4})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]
			[id_date/([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
			[id_date/([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
			[id_date/([0-9]{4})/embed/?$] => index.php?year=$matches[1]&embed=true
			[id_date/([0-9]{4})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&paged=$matches[2]
			[id_date/([0-9]{4})/?$] => index.php?year=$matches[1]
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[id_[0-9]+/[^/]+.html/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[id_([0-9]+)/([^/]+).html/embed/?$] => index.php?p=$matches[1]&name=$matches[2]&embed=true
			[id_([0-9]+)/([^/]+).html/trackback/?$] => index.php?p=$matches[1]&name=$matches[2]&tb=1
			[id_([0-9]+)/([^/]+).html/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?p=$matches[1]&name=$matches[2]&feed=$matches[3]
			[id_([0-9]+)/([^/]+).html/(feed|rdf|rss|rss2|atom)/?$] => index.php?p=$matches[1]&name=$matches[2]&feed=$matches[3]
			[id_([0-9]+)/([^/]+).html/page/?([0-9]{1,})/?$] => index.php?p=$matches[1]&name=$matches[2]&paged=$matches[3]
			[id_([0-9]+)/([^/]+).html/comment-page-([0-9]{1,})/?$] => index.php?p=$matches[1]&name=$matches[2]&cpage=$matches[3]
			[id_([0-9]+)/([^/]+).html(?:/([0-9]+))?/?$] => index.php?p=$matches[1]&name=$matches[2]&page=$matches[3]
			[id_[0-9]+/[^/]+.html/([^/]+)/?$] => index.php?attachment=$matches[1]
			[id_[0-9]+/[^/]+.html/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[id_[0-9]+/[^/]+.html/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/[^/]+.html/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/[^/]+.html/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[id_[0-9]+/[^/]+.html/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[id_[0-9]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
			[id_[0-9]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[id_[0-9]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[id_[0-9]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[id_([0-9]+)/embed/?$] => index.php?p=$matches[1]&embed=true
			[id_([0-9]+)/trackback/?$] => index.php?p=$matches[1]&tb=1
			[id_([0-9]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?p=$matches[1]&feed=$matches[2]
			[id_([0-9]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?p=$matches[1]&feed=$matches[2]
			[id_([0-9]+)/page/?([0-9]{1,})/?$] => index.php?p=$matches[1]&paged=$matches[2]
			[id_([0-9]+)/comment-page-([0-9]{1,})/?$] => index.php?p=$matches[1]&cpage=$matches[2]
			[id_([0-9]+)(?:/([0-9]+))?/?$] => index.php?p=$matches[1]&page=$matches[2]
			[id_[0-9]+/([^/]+)/?$] => index.php?attachment=$matches[1]
			[id_[0-9]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[id_[0-9]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[id_[0-9]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[id_[0-9]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[.?.+?/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
			[.?.+?/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[.?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[.?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[.?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[.?.+?/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[(.?.+?)/embed/?$] => index.php?pagename=$matches[1]&embed=true
			[(.?.+?)/trackback/?$] => index.php?pagename=$matches[1]&tb=1
			[(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?pagename=$matches[1]&feed=$matches[2]
			[(.?.+?)/(feed|rdf|rss|rss2|atom)/?$] => index.php?pagename=$matches[1]&feed=$matches[2]
			[(.?.+?)/page/?([0-9]{1,})/?$] => index.php?pagename=$matches[1]&paged=$matches[2]
			[(.?.+?)/comment-page-([0-9]{1,})/?$] => index.php?pagename=$matches[1]&cpage=$matches[2]
			[(.?.+?)(?:/([0-9]+))?/?$] => index.php?pagename=$matches[1]&page=$matches[2]
		)

	[extra_rules] => Array ()

	[extra_rules_top] => Array (
			[(filecode)(/.*)$] => index.php?pagename=$matches[1]&file_path=$matches[2]
		)

	[non_wp_rules] => Array ()

	[extra_permastructs] => Array (
			[category] => Array (
					[with_front] => 
					[ep_mask] => 512
					[paged] => 1
					[feed] => 1
					[forcomments] => 
					[walk_dirs] => 1
					[endpoints] => 1
					[struct] => cat/%category%
				)

			[post_tag] => Array (
					[with_front] => 
					[ep_mask] => 1024
					[paged] => 1
					[feed] => 1
					[forcomments] => 
					[walk_dirs] => 1
					[endpoints] => 1
					[struct] => met/%post_tag%
				)

			[article] => Array (
					[with_front] => 
					[ep_mask] => 1
					[paged] => 1
					[feed] => 1
					[forcomments] => 
					[walk_dirs] => 1
					[endpoints] => 1
					[struct] => article/%article%
				)
		)

	[endpoints] => Array ()

	[use_verbose_rules] => 
	[use_verbose_page_rules] => 
	[rewritecode] => Array
		(
			[0] => %year%
			[1] => %monthnum%
			[2] => %day%
			[3] => %hour%
			[4] => %minute%
			[5] => %second%
			[6] => %postname%
			[7] => %post_id%
			[8] => %author%
			[9] => %pagename%
			[10] => %search%
			[11] => %category%
			[12] => %post_tag%
			[13] => %post_format%
			[14] => %article%
		)

	[rewritereplace] => Array
		(
			[0] => ([0-9]{4})
			[1] => ([0-9]{1,2})
			[2] => ([0-9]{1,2})
			[3] => ([0-9]{1,2})
			[4] => ([0-9]{1,2})
			[5] => ([0-9]{1,2})
			[6] => ([^/]+)
			[7] => ([0-9]+)
			[8] => ([^/]+)
			[9] => ([^/]+?)
			[10] => (.+)
			[11] => (.+?)
			[12] => ([^/]+)
			[13] => ([^/]+)
			[14] => ([^/]+)
		)

	[queryreplace] => Array
		(
			[0] => year=
			[1] => monthnum=
			[2] => day=
			[3] => hour=
			[4] => minute=
			[5] => second=
			[6] => name=
			[7] => p=
			[8] => author_name=
			[9] => pagename=
			[10] => s=
			[11] => category_name=
			[12] => tag=
			[13] => post_format=
			[14] => article=
		)

	[feeds] => Array
		(
			[0] => feed
			[1] => rdf
			[2] => rss
			[3] => rss2
			[4] => atom
		)

)

#2 ЧПУ для дополнительного параметра постоянной страницы

Пример перезаписи ЧПУ вида: http://mysite/project/1 в http://mysite/index.php?pagename=project&id=1:

add_filter('rewrite_rules_array', 'my_insert_rewrite_rules');
add_filter('query_vars', 'my_insert_query_vars');
add_action('wp_loaded', 'my_flush_rules');

// flush_rules() если нашего правила еще нет в правилах
function my_flush_rules(){
	$rules = get_option( 'rewrite_rules' );

	if ( ! isset( $rules['(project)/(\d*)$'] ) ) {
		global $wp_rewrite;
		$wp_rewrite->flush_rules();
	}
}

// Добавляем новое правило
function my_insert_rewrite_rules( $rules ){
	$newrules = array(
		'(project)/(\d*)$' => 'index.php?pagename=$matches[1]&id=$matches[2]'
	);
	return $newrules + $rules;
}

// Добавим переменную запроса id в переменные, чтобы WP знал о ней
function my_insert_query_vars( $vars ){
	array_push($vars, 'id');
	return $vars;
}

Имейте ввиду, что flush_rules() очень требовательная к ресурсам функция, поэтому нельзя запускать её каждый раз при генерации страницы. Лучше всего запускать её один раз при активации плагина через функцию register_activation_hook().

Также flush_rules() всегда можно запустить один раз, просто зайдя в настройки постоянных ссылок в админ-панели WordPress и сохранив настройки без внесения каких-либо изменений.

#3 Пример редиректа со страницы http://site.ru/feed.xml на http://site.ru/feed/rss2

add_filter( 'generate_rewrite_rules', 'feed_dir_rewrite' );
function feed_dir_rewrite( $wp_rewrite ) {
	$feed_rules = array(
		'index.rdf' => 'index.php?feed=rdf',
		'index.xml' => 'index.php?feed=rss2',
		'(.+).xml'  => 'index.php?feed=' . $wp_rewrite->preg_index(1)
	);

	$wp_rewrite->rules = $feed_rules + $wp_rewrite->rules;
	return $wp_rewrite->rules;
}

Обратите внимание, что переменная передается по ссылке и передает весь класс WP_Rewrite, а не только на правила перезаписи rules.

Подключать такие правила нужно до того, как WordPress сделает что либо с правилами перезаписи. А это значит до хука init. Также имейте ввиду, что такое включение правил будет работать на всем сайте, как если бы вы добавили их в файл .htaccess.

#4 Правила перезаписи не для WordPress а для .htaccess

$wp_rewrite->non_wp_rules = array( 'Pattern' => 'Substitution' );

print_r( $wp_rewrite->mod_rewrite_rules() );

/* Выведет:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^Pattern /Substitution [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

*/

#5 Добавим .html для постоянных страниц WordPress

add_action('init', 'html_page_permalink', -1);
function html_page_permalink(){
	global $wp_rewrite; // открываем доступ к глобальной переменной с классом WP_Rewrite

	// если еще нет '.html' в структуре страниц, добавляем
	if( ! strpos($wp_rewrite->get_page_permastruct(), '.html') ){
		$wp_rewrite->page_structure = $wp_rewrite->page_structure . '.html';
	}
}

// Удалим слэш на конце, если нужно. Если слэш есть в структуре ЧПУ, то он будет добавляться и к постоянным страницам.
add_filter('user_trailingslashit', 'no_page_slash', 70, 2);
function no_page_slash( $string, $type ){
   global $wp_rewrite;

	if( $type == 'page' && $wp_rewrite->using_permalinks() && $wp_rewrite->use_trailing_slashes )
		$string = untrailingslashit($string);

   return $string;
}

Чтобы код начал работать, нужно обновить (сбросить) настройки ЧПУ в админке!

#6 Изменение префикса в URL (ЧПУ) автора (пользователя)

Обычно URL на страницу архива автора выглядит так: http://site.ru/author/egorka/. Допустим нам нужно изменить префикс (slug) с author на creator:

add_action('init', function(){
	$GLOBALS['wp_rewrite']->author_base = 'creator';
}, 1);

Теперь еще, нужно сбросить настройки ЧПУ в админке, для этого просто зайдите на страницу настроек «Постоянные ссылки»!

#7 ЧПУ для типа записи: тип_записи/slug

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

Делается это так:

add_action('init', function(){
	global $wp_rewrite;

	// добавим тег перезаписи, чтобы add_permastruct() его поняла
	// он потом будет заменен на часть регулярки указанной во втором параметре
	$wp_rewrite->add_rewrite_tag( '%book%', '([^/]+)', "post_type=book&name=" );

	$permastruct = 'book/%book%'; // наша структура ЧПУ

	// добавляем структуру ЧПУ
	$wp_rewrite->add_permastruct( 'book', $permastruct, array(
		'with_front'  => true,
		'paged'       => true,
		'feed'        => false,
		'forcomments' => false,
		'walk_dirs'   => false,
		'endpoints'   => false,
	) );

}, 1);

// третий параметр в add_permastruct() можно не указывать, тогда аргументы будут по умолчанию

И нужно обновить (сбросить) настройки ЧПУ в админке!

Про аргументы $args читайте в описании функции add_permastruct().

#8 ЧПУ для типа записи: тип_записи/ID

Допустим, при регистрации типа записи orders с помощью register_post_type() параметр rewrite = false - правила перезаписи не создаются и их нужно создать вручную.

URL должен выглядеть как site.ru/orders/123, где 123 - ID записи.

add_action('init', function(){

	// создам правила ЧПУ
	add_rewrite_tag( '%order_id%', '([0-9]+)', "post_type=orders&p=" );
	// или можно так
	// создам правила ЧПУ - тут 'order/([0-9]+)/?$' полностью совпадает с тем что создало бы add_permastruct() и поэтому заменяет...
//  add_filter( 'orders'.'_rewrite_rules', function( $rules ){
//      return array( 'order/([0-9]+)/?$' => 'index.php?post_type=orders&p=$matches[2]' );
//  } );

	// регистрируем структуру для типа записи
	add_permastruct( 'orders', 'order/%order_id%', array(
		'with_front'  => false,
		'paged'       => false,
		'feed'        => false,
		'forcomments' => false,
		'walk_dirs'   => false,
		'endpoints'   => false,
	));

	// заменим тег в URL на ID
	add_filter('post_type_link', 'orders_permalink', 1, 2);
	function orders_permalink( $permalink, $post ){
		if( false === strpos($permalink, '%order_id%') ) return $permalink;

		return str_replace( '%order_id%', $post->ID, $permalink );
	}

}

#9 Удаление названия типа записи (префикса) из URL

Вариант 1

Допустим у нас есть новый тип записи team, который имеет ЧПУ вида: site.ru/team/nazvanie-zapisi. Мы хотим удалить префикс team и сделать ссылки этого типа записи такого вида: site.ru/nazvanie-zapisi.

add_filter( 'post_type_link', 'remove_post_type_slug', 10, 2 );
add_action( 'pre_get_posts', 'add_post_type_to_get_posts_request' );

// Удаляем префикс с именем типа записи из URL
function remove_post_type_slug( $post_link, $post ){
	if( $post->post_type === 'team' ){
		return str_replace("/$post->post_type/", '/', $post_link );
	}

	return $post_link;
}

// Добавляем тип записи в запрос
function add_post_type_to_get_posts_request( $query ){
	if( is_admin() || ! $query->is_main_query() ) return; // не основной запрос

	// не запрос с name параметром (как у постоянной страницы)
	if( ! isset($query->query['page']) || empty($query->query['name']) || count($query->query) != 2 )
		return;

	$query->set('post_type', array('post', 'page', 'team') ); // 'team' added
}

Тут надо иметь ввиду, что наш новый URL вида site.ru/nazvanie-zapisi по структуре точно такой же как URL постоянных страниц (и возможно как ЧПУ постов). И если название постоянной страницы и записи нового типа будут совпадать, то мы получим конфликт и вероятно будет показана неправильная запись. Но так как такая ситуация очень мало вероятна, мы этот возможный баг просто имеем ввиду и никак не обрабатываем...

Вариант 2

В этом варианте тип записи называется pool_services и тут мы не используем хук post_type_link, а используем метод add_permastruct() - это позволяет убрать название типа записи из URL не только на фронте, но и в админке.

В результате получим URL вида /postname а не /pool-services/postname.

add_action( 'init', 'pw24_post_type_rewrite');
function pw24_post_type_rewrite() {
	global $wp_rewrite;
	// в данном случае тип записи - pool_services
	$wp_rewrite->add_rewrite_tag("%pool_services%", '([^/]+)', "pool_services=");
	$wp_rewrite->add_permastruct('pool_services', '%pool_services%' );
}

add_action( 'pre_get_posts', 'pw24_add_post_type_to_get_posts_request' );
function pw24_add_post_type_to_get_posts_request( $query ){
	if( is_admin() || ! $query->is_main_query() ) return; // не основной запрос

	// не запрос с name параметром (как у постоянной страницы)
	if( ! isset($query->query['page']) || empty($query->query['name']) || count($query->query) != 2 )
		return;

	$query->set('post_type', array('post', 'page', 'pool_services') ); // добавляем 'pool_services'
}

И всё! Задача решена - теперь в ссылках и в админке и на сайте отсутствует название типа записи 'pool-services' и все страницы работают без проблем.

Код WP_Rewrite: wp-includes/class-wp-rewrite.php VER 4.9.8

<?php
class WP_Rewrite {
	/**
	 * Permalink structure for posts.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	public $permalink_structure;

	/**
	 * Whether to add trailing slashes.
	 *
	 * @since 2.2.0
	 * @var bool
	 */
	public $use_trailing_slashes;

	/**
	 * Base for the author permalink structure (example.com/$author_base/authorname).
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $author_base = 'author';

	/**
	 * Permalink structure for author archives.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $author_structure;

	/**
	 * Permalink structure for date archives.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $date_structure;

	/**
	 * Permalink structure for pages.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $page_structure;

	/**
	 * Base of the search permalink structure (example.com/$search_base/query).
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $search_base = 'search';

	/**
	 * Permalink structure for searches.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $search_structure;

	/**
	 * Comments permalink base.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $comments_base = 'comments';

	/**
	 * Pagination permalink base.
	 *
	 * @since 3.1.0
	 * @var string
	 */
	public $pagination_base = 'page';

	/**
	 * Comments pagination permalink base.
	 *
	 * @since 4.2.0
	 * @var string
	 */
	var $comments_pagination_base = 'comment-page';

	/**
	 * Feed permalink base.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $feed_base = 'feed';

	/**
	 * Comments feed permalink structure.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $comment_feed_structure;

	/**
	 * Feed request permalink structure.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $feed_structure;

	/**
	 * The static portion of the post permalink structure.
	 *
	 * If the permalink structure is "/archive/%post_id%" then the front
	 * is "/archive/". If the permalink structure is "/%year%/%postname%/"
	 * then the front is "/".
	 *
	 * @since 1.5.0
	 * @var string
	 *
	 * @see WP_Rewrite::init()
	 */
	public $front;

	/**
	 * The prefix for all permalink structures.
	 *
	 * If PATHINFO/index permalinks are in use then the root is the value of
	 * `WP_Rewrite::$index` with a trailing slash appended. Otherwise the root
	 * will be empty.
	 *
	 * @since 1.5.0
	 * @var string
	 *
	 * @see WP_Rewrite::init()
	 * @see WP_Rewrite::using_index_permalinks()
	 */
	public $root = '';

	/**
	 * The name of the index file which is the entry point to all requests.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	public $index = 'index.php';

	/**
	 * Variable name to use for regex matches in the rewritten query.
	 *
	 * @since 1.5.0
	 * @var string
	 */
	var $matches = '';

	/**
	 * Rewrite rules to match against the request to find the redirect or query.
	 *
	 * @since 1.5.0
	 * @var array
	 */
	var $rules;

	/**
	 * Additional rules added external to the rewrite class.
	 *
	 * Those not generated by the class, see add_rewrite_rule().
	 *
	 * @since 2.1.0
	 * @var array
	 */
	var $extra_rules = array();

	/**
	 * Additional rules that belong at the beginning to match first.
	 *
	 * Those not generated by the class, see add_rewrite_rule().
	 *
	 * @since 2.3.0
	 * @var array
	 */
	var $extra_rules_top = array();

	/**
	 * Rules that don't redirect to WordPress' index.php.
	 *
	 * These rules are written to the mod_rewrite portion of the .htaccess,
	 * and are added by add_external_rule().
	 *
	 * @since 2.1.0
	 * @var array
	 */
	var $non_wp_rules = array();

	/**
	 * Extra permalink structures, e.g. categories, added by add_permastruct().
	 *
	 * @since 2.1.0
	 * @var array
	 */
	var $extra_permastructs = array();

	/**
	 * Endpoints (like /trackback/) added by add_rewrite_endpoint().
	 *
	 * @since 2.1.0
	 * @var array
	 */
	var $endpoints;

	/**
	 * Whether to write every mod_rewrite rule for WordPress into the .htaccess file.
	 *
	 * This is off by default, turning it on might print a lot of rewrite rules
	 * to the .htaccess file.
	 *
	 * @since 2.0.0
	 * @var bool
	 *
	 * @see WP_Rewrite::mod_rewrite_rules()
	 */
	public $use_verbose_rules = false;

	/**
	 * Could post permalinks be confused with those of pages?
	 *
	 * If the first rewrite tag in the post permalink structure is one that could
	 * also match a page name (e.g. %postname% or %author%) then this flag is
	 * set to true. Prior to WordPress 3.3 this flag indicated that every page
	 * would have a set of rules added to the top of the rewrite rules array.
	 * Now it tells WP::parse_request() to check if a URL matching the page
	 * permastruct is actually a page before accepting it.
	 *
	 * @since 2.5.0
	 * @var bool
	 *
	 * @see WP_Rewrite::init()
	 */
	public $use_verbose_page_rules = true;

	/**
	 * Rewrite tags that can be used in permalink structures.
	 *
	 * These are translated into the regular expressions stored in
	 * `WP_Rewrite::$rewritereplace` and are rewritten to the query
	 * variables listed in WP_Rewrite::$queryreplace.
	 *
	 * Additional tags can be added with add_rewrite_tag().
	 *
	 * @since 1.5.0
	 * @var array
	 */
	var $rewritecode = array(
		'%year%',
		'%monthnum%',
		'%day%',
		'%hour%',
		'%minute%',
		'%second%',
		'%postname%',
		'%post_id%',
		'%author%',
		'%pagename%',
		'%search%'
	);

	/**
	 * Regular expressions to be substituted into rewrite rules in place
	 * of rewrite tags, see WP_Rewrite::$rewritecode.
	 *
	 * @since 1.5.0
	 * @var array
	 */
	var $rewritereplace = array(
		'([0-9]{4})',
		'([0-9]{1,2})',
		'([0-9]{1,2})',
		'([0-9]{1,2})',
		'([0-9]{1,2})',
		'([0-9]{1,2})',
		'([^/]+)',
		'([0-9]+)',
		'([^/]+)',
		'([^/]+?)',
		'(.+)'
	);

	/**
	 * Query variables that rewrite tags map to, see WP_Rewrite::$rewritecode.
	 *
	 * @since 1.5.0
	 * @var array
	 */
	var $queryreplace = array(
		'year=',
		'monthnum=',
		'day=',
		'hour=',
		'minute=',
		'second=',
		'name=',
		'p=',
		'author_name=',
		'pagename=',
		's='
	);

	/**
	 * Supported default feeds.
	 *
	 * @since 1.5.0
	 * @var array
	 */
	public $feeds = array( 'feed', 'rdf', 'rss', 'rss2', 'atom' );

	/**
	 * Determines whether permalinks are being used.
	 *
	 * This can be either rewrite module or permalink in the HTTP query string.
	 *
	 * @since 1.5.0
	 *
	 * @return bool True, if permalinks are enabled.
	 */
	public function using_permalinks() {
		return ! empty($this->permalink_structure);
	}

	/**
	 * Determines whether permalinks are being used and rewrite module is not enabled.
	 *
	 * Means that permalink links are enabled and index.php is in the URL.
	 *
	 * @since 1.5.0
	 *
	 * @return bool Whether permalink links are enabled and index.php is in the URL.
	 */
	public function using_index_permalinks() {
		if ( empty( $this->permalink_structure ) ) {
			return false;
		}

		// If the index is not in the permalink, we're using mod_rewrite.
		return preg_match( '#^/*' . $this->index . '#', $this->permalink_structure );
	}

	/**
	 * Determines whether permalinks are being used and rewrite module is enabled.
	 *
	 * Using permalinks and index.php is not in the URL.
	 *
	 * @since 1.5.0
	 *
	 * @return bool Whether permalink links are enabled and index.php is NOT in the URL.
	 */
	public function using_mod_rewrite_permalinks() {
		return $this->using_permalinks() && ! $this->using_index_permalinks();
	}

	/**
	 * Indexes for matches for usage in preg_*() functions.
	 *
	 * The format of the string is, with empty matches property value, '$NUM'.
	 * The 'NUM' will be replaced with the value in the $number parameter. With
	 * the matches property not empty, the value of the returned string will
	 * contain that value of the matches property. The format then will be
	 * '$MATCHES[NUM]', with MATCHES as the value in the property and NUM the
	 * value of the $number parameter.
	 *
	 * @since 1.5.0
	 *
	 * @param int $number Index number.
	 * @return string
	 */
	public function preg_index($number) {
		$match_prefix = '$';
		$match_suffix = '';

		if ( ! empty($this->matches) ) {
			$match_prefix = '$' . $this->matches . '[';
			$match_suffix = ']';
		}

		return "$match_prefix$number$match_suffix";
	}

	/**
	 * Retrieves all page and attachments for pages URIs.
	 *
	 * The attachments are for those that have pages as parents and will be
	 * retrieved.
	 *
	 * @since 2.5.0
	 *
	 * @global wpdb $wpdb WordPress database abstraction object.
	 *
	 * @return array Array of page URIs as first element and attachment URIs as second element.
	 */
	public function page_uri_index() {
		global $wpdb;

		// Get pages in order of hierarchy, i.e. children after parents.
		$pages = $wpdb->get_results("SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'page' AND post_status != 'auto-draft'");
		$posts = get_page_hierarchy( $pages );

		// If we have no pages get out quick.
		if ( !$posts )
			return array( array(), array() );

		// Now reverse it, because we need parents after children for rewrite rules to work properly.
		$posts = array_reverse($posts, true);

		$page_uris = array();
		$page_attachment_uris = array();

		foreach ( $posts as $id => $post ) {
			// URL => page name
			$uri = get_page_uri($id);
			$attachments = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent = %d", $id ));
			if ( !empty($attachments) ) {
				foreach ( $attachments as $attachment ) {
					$attach_uri = get_page_uri($attachment->ID);
					$page_attachment_uris[$attach_uri] = $attachment->ID;
				}
			}

			$page_uris[$uri] = $id;
		}

		return array( $page_uris, $page_attachment_uris );
	}

	/**
	 * Retrieves all of the rewrite rules for pages.
	 *
	 * @since 1.5.0
	 *
	 * @return array Page rewrite rules.
	 */
	public function page_rewrite_rules() {
		// The extra .? at the beginning prevents clashes with other regular expressions in the rules array.
		$this->add_rewrite_tag( '%pagename%', '(.?.+?)', 'pagename=' );

		return $this->generate_rewrite_rules( $this->get_page_permastruct(), EP_PAGES, true, true, false, false );
	}

	/**
	 * Retrieves date permalink structure, with year, month, and day.
	 *
	 * The permalink structure for the date, if not set already depends on the
	 * permalink structure. It can be one of three formats. The first is year,
	 * month, day; the second is day, month, year; and the last format is month,
	 * day, year. These are matched against the permalink structure for which
	 * one is used. If none matches, then the default will be used, which is
	 * year, month, day.
	 *
	 * Prevents post ID and date permalinks from overlapping. In the case of
	 * post_id, the date permalink will be prepended with front permalink with
	 * 'date/' before the actual permalink to form the complete date permalink
	 * structure.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False on no permalink structure. Date permalink structure.
	 */
	public function get_date_permastruct() {
		if ( isset($this->date_structure) )
			return $this->date_structure;

		if ( empty($this->permalink_structure) ) {
			$this->date_structure = '';
			return false;
		}

		// The date permalink must have year, month, and day separated by slashes.
		$endians = array('%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%', '%monthnum%/%day%/%year%');

		$this->date_structure = '';
		$date_endian = '';

		foreach ( $endians as $endian ) {
			if ( false !== strpos($this->permalink_structure, $endian) ) {
				$date_endian= $endian;
				break;
			}
		}

		if ( empty($date_endian) )
			$date_endian = '%year%/%monthnum%/%day%';

		/*
		 * Do not allow the date tags and %post_id% to overlap in the permalink
		 * structure. If they do, move the date tags to $front/date/.
		 */
		$front = $this->front;
		preg_match_all('/%.+?%/', $this->permalink_structure, $tokens);
		$tok_index = 1;
		foreach ( (array) $tokens[0] as $token) {
			if ( '%post_id%' == $token && ($tok_index <= 3) ) {
				$front = $front . 'date/';
				break;
			}
			$tok_index++;
		}

		$this->date_structure = $front . $date_endian;

		return $this->date_structure;
	}

	/**
	 * Retrieves the year permalink structure without month and day.
	 *
	 * Gets the date permalink structure and strips out the month and day
	 * permalink structures.
	 *
	 * @since 1.5.0
	 *
	 * @return false|string False on failure. Year structure on success.
	 */
	public function get_year_permastruct() {
		$structure = $this->get_date_permastruct();

		if ( empty($structure) )
			return false;

		$structure = str_replace('%monthnum%', '', $structure);
		$structure = str_replace('%day%', '', $structure);
		$structure = preg_replace('#/+#', '/', $structure);

		return $structure;
	}

	/**
	 * Retrieves the month permalink structure without day and with year.
	 *
	 * Gets the date permalink structure and strips out the day permalink
	 * structures. Keeps the year permalink structure.
	 *
	 * @since 1.5.0
	 *
	 * @return false|string False on failure. Year/Month structure on success.
	 */
	public function get_month_permastruct() {
		$structure = $this->get_date_permastruct();

		if ( empty($structure) )
			return false;

		$structure = str_replace('%day%', '', $structure);
		$structure = preg_replace('#/+#', '/', $structure);

		return $structure;
	}

	/**
	 * Retrieves the day permalink structure with month and year.
	 *
	 * Keeps date permalink structure with all year, month, and day.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False on failure. Year/Month/Day structure on success.
	 */
	public function get_day_permastruct() {
		return $this->get_date_permastruct();
	}

	/**
	 * Retrieves the permalink structure for categories.
	 *
	 * If the category_base property has no value, then the category structure
	 * will have the front property value, followed by 'category', and finally
	 * '%category%'. If it does, then the root property will be used, along with
	 * the category_base property value.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False on failure. Category permalink structure.
	 */
	public function get_category_permastruct() {
		return $this->get_extra_permastruct('category');
	}

	/**
	 * Retrieve the permalink structure for tags.
	 *
	 * If the tag_base property has no value, then the tag structure will have
	 * the front property value, followed by 'tag', and finally '%tag%'. If it
	 * does, then the root property will be used, along with the tag_base
	 * property value.
	 *
	 * @since 2.3.0
	 *
	 * @return string|false False on failure. Tag permalink structure.
	 */
	public function get_tag_permastruct() {
		return $this->get_extra_permastruct('post_tag');
	}

	/**
	 * Retrieves an extra permalink structure by name.
	 *
	 * @since 2.5.0
	 *
	 * @param string $name Permalink structure name.
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_extra_permastruct($name) {
		if ( empty($this->permalink_structure) )
			return false;

		if ( isset($this->extra_permastructs[$name]) )
			return $this->extra_permastructs[$name]['struct'];

		return false;
	}

	/**
	 * Retrieves the author permalink structure.
	 *
	 * The permalink structure is front property, author base, and finally
	 * '/%author%'. Will set the author_structure property and then return it
	 * without attempting to set the value again.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_author_permastruct() {
		if ( isset($this->author_structure) )
			return $this->author_structure;

		if ( empty($this->permalink_structure) ) {
			$this->author_structure = '';
			return false;
		}

		$this->author_structure = $this->front . $this->author_base . '/%author%';

		return $this->author_structure;
	}

	/**
	 * Retrieves the search permalink structure.
	 *
	 * The permalink structure is root property, search base, and finally
	 * '/%search%'. Will set the search_structure property and then return it
	 * without attempting to set the value again.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_search_permastruct() {
		if ( isset($this->search_structure) )
			return $this->search_structure;

		if ( empty($this->permalink_structure) ) {
			$this->search_structure = '';
			return false;
		}

		$this->search_structure = $this->root . $this->search_base . '/%search%';

		return $this->search_structure;
	}

	/**
	 * Retrieves the page permalink structure.
	 *
	 * The permalink structure is root property, and '%pagename%'. Will set the
	 * page_structure property and then return it without attempting to set the
	 * value again.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_page_permastruct() {
		if ( isset($this->page_structure) )
			return $this->page_structure;

		if (empty($this->permalink_structure)) {
			$this->page_structure = '';
			return false;
		}

		$this->page_structure = $this->root . '%pagename%';

		return $this->page_structure;
	}

	/**
	 * Retrieves the feed permalink structure.
	 *
	 * The permalink structure is root property, feed base, and finally
	 * '/%feed%'. Will set the feed_structure property and then return it
	 * without attempting to set the value again.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_feed_permastruct() {
		if ( isset($this->feed_structure) )
			return $this->feed_structure;

		if ( empty($this->permalink_structure) ) {
			$this->feed_structure = '';
			return false;
		}

		$this->feed_structure = $this->root . $this->feed_base . '/%feed%';

		return $this->feed_structure;
	}

	/**
	 * Retrieves the comment feed permalink structure.
	 *
	 * The permalink structure is root property, comment base property, feed
	 * base and finally '/%feed%'. Will set the comment_feed_structure property
	 * and then return it without attempting to set the value again.
	 *
	 * @since 1.5.0
	 *
	 * @return string|false False if not found. Permalink structure string.
	 */
	public function get_comment_feed_permastruct() {
		if ( isset($this->comment_feed_structure) )
			return $this->comment_feed_structure;

		if (empty($this->permalink_structure)) {
			$this->comment_feed_structure = '';
			return false;
		}

		$this->comment_feed_structure = $this->root . $this->comments_base . '/' . $this->feed_base . '/%feed%';

		return $this->comment_feed_structure;
	}

	/**
	 * Adds or updates existing rewrite tags (e.g. %postname%).
	 *
	 * If the tag already exists, replace the existing pattern and query for
	 * that tag, otherwise add the new tag.
	 *
	 * @since 1.5.0
	 *
	 * @see WP_Rewrite::$rewritecode
	 * @see WP_Rewrite::$rewritereplace
	 * @see WP_Rewrite::$queryreplace
	 *
	 * @param string $tag   Name of the rewrite tag to add or update.
	 * @param string $regex Regular expression to substitute the tag for in rewrite rules.
	 * @param string $query String to append to the rewritten query. Must end in '='.
	 */
	public function add_rewrite_tag( $tag, $regex, $query ) {
		$position = array_search( $tag, $this->rewritecode );
		if ( false !== $position && null !== $position ) {
			$this->rewritereplace[ $position ] = $regex;
			$this->queryreplace[ $position ] = $query;
		} else {
			$this->rewritecode[] = $tag;
			$this->rewritereplace[] = $regex;
			$this->queryreplace[] = $query;
		}
	}


	/**
	 * Removes an existing rewrite tag.
	 *
	 * @since 4.5.0
	 *
	 * @see WP_Rewrite::$rewritecode
	 * @see WP_Rewrite::$rewritereplace
	 * @see WP_Rewrite::$queryreplace
	 *
	 * @param string $tag Name of the rewrite tag to remove.
	 */
	public function remove_rewrite_tag( $tag ) {
		$position = array_search( $tag, $this->rewritecode );
		if ( false !== $position && null !== $position ) {
			unset( $this->rewritecode[ $position ] );
			unset( $this->rewritereplace[ $position ] );
			unset( $this->queryreplace[ $position ] );
		}
	}

	/**
	 * Generates rewrite rules from a permalink structure.
	 *
	 * The main WP_Rewrite function for building the rewrite rule list. The
	 * contents of the function is a mix of black magic and regular expressions,
	 * so best just ignore the contents and move to the parameters.
	 *
	 * @since 1.5.0
	 *
	 * @param string $permalink_structure The permalink structure.
	 * @param int    $ep_mask             Optional. Endpoint mask defining what endpoints are added to the structure.
	 *                                    Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
	 *                                    `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
	 *                                    `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
	 *                                    Default `EP_NONE`.
	 * @param bool   $paged               Optional. Whether archive pagination rules should be added for the structure.
	 *                                    Default true.
	 * @param bool   $feed                Optional Whether feed rewrite rules should be added for the structure.
	 *                                    Default true.
	 * @param bool   $forcomments         Optional. Whether the feed rules should be a query for a comments feed.
	 *                                    Default false.
	 * @param bool   $walk_dirs           Optional. Whether the 'directories' making up the structure should be walked
	 *                                    over and rewrite rules built for each in-turn. Default true.
	 * @param bool   $endpoints           Optional. Whether endpoints should be applied to the generated rewrite rules.
	 *                                    Default true.
	 * @return array Rewrite rule list.
	 */
	public function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) {
		// Build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
		$feedregex2 = '';
		foreach ( (array) $this->feeds as $feed_name)
			$feedregex2 .= $feed_name . '|';
		$feedregex2 = '(' . trim($feedregex2, '|') . ')/?$';

		/*
		 * $feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
		 * and <permalink>/atom are both possible
		 */
		$feedregex = $this->feed_base . '/' . $feedregex2;

		// Build a regex to match the trackback and page/xx parts of URLs.
		$trackbackregex = 'trackback/?$';
		$pageregex = $this->pagination_base . '/?([0-9]{1,})/?$';
		$commentregex = $this->comments_pagination_base . '-([0-9]{1,})/?$';
		$embedregex = 'embed/?$';

		// Build up an array of endpoint regexes to append => queries to append.
		if ( $endpoints ) {
			$ep_query_append = array ();
			foreach ( (array) $this->endpoints as $endpoint) {
				// Match everything after the endpoint name, but allow for nothing to appear there.
				$epmatch = $endpoint[1] . '(/(.*))?/?$';

				// This will be appended on to the rest of the query for each dir.
				$epquery = '&' . $endpoint[2] . '=';
				$ep_query_append[$epmatch] = array ( $endpoint[0], $epquery );
			}
		}

		// Get everything up to the first rewrite tag.
		$front = substr($permalink_structure, 0, strpos($permalink_structure, '%'));

		// Build an array of the tags (note that said array ends up being in $tokens[0]).
		preg_match_all('/%.+?%/', $permalink_structure, $tokens);

		$num_tokens = count($tokens[0]);

		$index = $this->index; //probably 'index.php'
		$feedindex = $index;
		$trackbackindex = $index;
		$embedindex = $index;

		/*
		 * Build a list from the rewritecode and queryreplace arrays, that will look something
		 * like tagname=$matches[i] where i is the current $i.
		 */
		$queries = array();
		for ( $i = 0; $i < $num_tokens; ++$i ) {
			if ( 0 < $i )
				$queries[$i] = $queries[$i - 1] . '&';
			else
				$queries[$i] = '';

			$query_token = str_replace($this->rewritecode, $this->queryreplace, $tokens[0][$i]) . $this->preg_index($i+1);
			$queries[$i] .= $query_token;
		}

		// Get the structure, minus any cruft (stuff that isn't tags) at the front.
		$structure = $permalink_structure;
		if ( $front != '/' )
			$structure = str_replace($front, '', $structure);

		/*
		 * Create a list of dirs to walk over, making rewrite rules for each level
		 * so for example, a $structure of /%year%/%monthnum%/%postname% would create
		 * rewrite rules for /%year%/, /%year%/%monthnum%/ and /%year%/%monthnum%/%postname%
		 */
		$structure = trim($structure, '/');
		$dirs = $walk_dirs ? explode('/', $structure) : array( $structure );
		$num_dirs = count($dirs);

		// Strip slashes from the front of $front.
		$front = preg_replace('|^/+|', '', $front);

		// The main workhorse loop.
		$post_rewrite = array();
		$struct = $front;
		for ( $j = 0; $j < $num_dirs; ++$j ) {
			// Get the struct for this dir, and trim slashes off the front.
			$struct .= $dirs[$j] . '/'; // Accumulate. see comment near explode('/', $structure) above.
			$struct = ltrim($struct, '/');

			// Replace tags with regexes.
			$match = str_replace($this->rewritecode, $this->rewritereplace, $struct);

			// Make a list of tags, and store how many there are in $num_toks.
			$num_toks = preg_match_all('/%.+?%/', $struct, $toks);

			// Get the 'tagname=$matches[i]'.
			$query = ( ! empty( $num_toks ) && isset( $queries[$num_toks - 1] ) ) ? $queries[$num_toks - 1] : '';

			// Set up $ep_mask_specific which is used to match more specific URL types.
			switch ( $dirs[$j] ) {
				case '%year%':
					$ep_mask_specific = EP_YEAR;
					break;
				case '%monthnum%':
					$ep_mask_specific = EP_MONTH;
					break;
				case '%day%':
					$ep_mask_specific = EP_DAY;
					break;
				default:
					$ep_mask_specific = EP_NONE;
			}

			// Create query for /page/xx.
			$pagematch = $match . $pageregex;
			$pagequery = $index . '?' . $query . '&paged=' . $this->preg_index($num_toks + 1);

			// Create query for /comment-page-xx.
			$commentmatch = $match . $commentregex;
			$commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index($num_toks + 1);

			if ( get_option('page_on_front') ) {
				// Create query for Root /comment-page-xx.
				$rootcommentmatch = $match . $commentregex;
				$rootcommentquery = $index . '?' . $query . '&page_id=' . get_option('page_on_front') . '&cpage=' . $this->preg_index($num_toks + 1);
			}

			// Create query for /feed/(feed|atom|rss|rss2|rdf).
			$feedmatch = $match . $feedregex;
			$feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);

			// Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
			$feedmatch2 = $match . $feedregex2;
			$feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);

			// Create query and regex for embeds.
			$embedmatch = $match . $embedregex;
			$embedquery = $embedindex . '?' . $query . '&embed=true';

			// If asked to, turn the feed queries into comment feed ones.
			if ( $forcomments ) {
				$feedquery .= '&withcomments=1';
				$feedquery2 .= '&withcomments=1';
			}

			// Start creating the array of rewrites for this dir.
			$rewrite = array();

			// ...adding on /feed/ regexes => queries
			if ( $feed ) {
				$rewrite = array( $feedmatch => $feedquery, $feedmatch2 => $feedquery2, $embedmatch => $embedquery );
			}

			//...and /page/xx ones
			if ( $paged ) {
				$rewrite = array_merge( $rewrite, array( $pagematch => $pagequery ) );
			}

			// Only on pages with comments add ../comment-page-xx/.
			if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask ) {
				$rewrite = array_merge($rewrite, array($commentmatch => $commentquery));
			} elseif ( EP_ROOT & $ep_mask && get_option('page_on_front') ) {
				$rewrite = array_merge($rewrite, array($rootcommentmatch => $rootcommentquery));
			}

			// Do endpoints.
			if ( $endpoints ) {
				foreach ( (array) $ep_query_append as $regex => $ep) {
					// Add the endpoints on if the mask fits.
					if ( $ep[0] & $ep_mask || $ep[0] & $ep_mask_specific )
						$rewrite[$match . $regex] = $index . '?' . $query . $ep[1] . $this->preg_index($num_toks + 2);
				}
			}

			// If we've got some tags in this dir.
			if ( $num_toks ) {
				$post = false;
				$page = false;

				/*
				 * Check to see if this dir is permalink-level: i.e. the structure specifies an
				 * individual post. Do this by checking it contains at least one of 1) post name,
				 * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
				 * minute all present). Set these flags now as we need them for the endpoints.
				 */
				if ( strpos($struct, '%postname%') !== false
						|| strpos($struct, '%post_id%') !== false
						|| strpos($struct, '%pagename%') !== false
						|| (strpos($struct, '%year%') !== false && strpos($struct, '%monthnum%') !== false && strpos($struct, '%day%') !== false && strpos($struct, '%hour%') !== false && strpos($struct, '%minute%') !== false && strpos($struct, '%second%') !== false)
						) {
					$post = true;
					if ( strpos($struct, '%pagename%') !== false )
						$page = true;
				}

				if ( ! $post ) {
					// For custom post types, we need to add on endpoints as well.
					foreach ( get_post_types( array('_builtin' => false ) ) as $ptype ) {
						if ( strpos($struct, "%$ptype%") !== false ) {
							$post = true;

							// This is for page style attachment URLs.
							$page = is_post_type_hierarchical( $ptype );
							break;
						}
					}
				}

				// If creating rules for a permalink, do all the endpoints like attachments etc.
				if ( $post ) {
					// Create query and regex for trackback.
					$trackbackmatch = $match . $trackbackregex;
					$trackbackquery = $trackbackindex . '?' . $query . '&tb=1';

					// Create query and regex for embeds.
					$embedmatch = $match . $embedregex;
					$embedquery = $embedindex . '?' . $query . '&embed=true';

					// Trim slashes from the end of the regex for this dir.
					$match = rtrim($match, '/');

					// Get rid of brackets.
					$submatchbase = str_replace( array('(', ')'), '', $match);

					// Add a rule for at attachments, which take the form of <permalink>/some-text.
					$sub1 = $submatchbase . '/([^/]+)/';

					// Add trackback regex <permalink>/trackback/...
					$sub1tb = $sub1 . $trackbackregex;

					// And <permalink>/feed/(atom|...)
					$sub1feed = $sub1 . $feedregex;

					// And <permalink>/(feed|atom...)
					$sub1feed2 = $sub1 . $feedregex2;

					// And <permalink>/comment-page-xx
					$sub1comment = $sub1 . $commentregex;

					// And <permalink>/embed/...
					$sub1embed = $sub1 . $embedregex;

					/*
					 * Add another rule to match attachments in the explicit form:
					 * <permalink>/attachment/some-text
					 */
					$sub2 = $submatchbase . '/attachment/([^/]+)/';

					// And add trackbacks <permalink>/attachment/trackback.
					$sub2tb = $sub2 . $trackbackregex;

					// Feeds, <permalink>/attachment/feed/(atom|...)
					$sub2feed = $sub2 . $feedregex;

					// And feeds again on to this <permalink>/attachment/(feed|atom...)
					$sub2feed2 = $sub2 . $feedregex2;

					// And <permalink>/comment-page-xx
					$sub2comment = $sub2 . $commentregex;

					// And <permalink>/embed/...
					$sub2embed = $sub2 . $embedregex;

					// Create queries for these extra tag-ons we've just dealt with.
					$subquery = $index . '?attachment=' . $this->preg_index(1);
					$subtbquery = $subquery . '&tb=1';
					$subfeedquery = $subquery . '&feed=' . $this->preg_index(2);
					$subcommentquery = $subquery . '&cpage=' . $this->preg_index(2);
					$subembedquery = $subquery . '&embed=true';

					// Do endpoints for attachments.
					if ( !empty($endpoints) ) {
						foreach ( (array) $ep_query_append as $regex => $ep ) {
							if ( $ep[0] & EP_ATTACHMENT ) {
								$rewrite[$sub1 . $regex] = $subquery . $ep[1] . $this->preg_index(3);
								$rewrite[$sub2 . $regex] = $subquery . $ep[1] . $this->preg_index(3);
							}
						}
					}

					/*
					 * Now we've finished with endpoints, finish off the $sub1 and $sub2 matches
					 * add a ? as we don't have to match that last slash, and finally a $ so we
					 * match to the end of the URL
					 */
					$sub1 .= '?$';
					$sub2 .= '?$';

					/*
					 * Post pagination, e.g. <permalink>/2/
					 * Previously: '(/[0-9]+)?/?$', which produced '/2' for page.
					 * When cast to int, returned 0.
					 */
					$match = $match . '(?:/([0-9]+))?/?$';
					$query = $index . '?' . $query . '&page=' . $this->preg_index($num_toks + 1);

				// Not matching a permalink so this is a lot simpler.
				} else {
					// Close the match and finalise the query.
					$match .= '?$';
					$query = $index . '?' . $query;
				}

				/*
				 * Create the final array for this dir by joining the $rewrite array (which currently
				 * only contains rules/queries for trackback, pages etc) to the main regex/query for
				 * this dir
				 */
				$rewrite = array_merge($rewrite, array($match => $query));

				// If we're matching a permalink, add those extras (attachments etc) on.
				if ( $post ) {
					// Add trackback.
					$rewrite = array_merge(array($trackbackmatch => $trackbackquery), $rewrite);

					// Add embed.
					$rewrite = array_merge( array( $embedmatch => $embedquery ), $rewrite );

					// Add regexes/queries for attachments, attachment trackbacks and so on.
					if ( ! $page ) {
						// Require <permalink>/attachment/stuff form for pages because of confusion with subpages.
						$rewrite = array_merge( $rewrite, array(
							$sub1        => $subquery,
							$sub1tb      => $subtbquery,
							$sub1feed    => $subfeedquery,
							$sub1feed2   => $subfeedquery,
							$sub1comment => $subcommentquery,
							$sub1embed   => $subembedquery
						) );
					}

					$rewrite = array_merge( array( $sub2 => $subquery, $sub2tb => $subtbquery, $sub2feed => $subfeedquery, $sub2feed2 => $subfeedquery, $sub2comment => $subcommentquery, $sub2embed => $subembedquery ), $rewrite );
				}
			}
			// Add the rules for this dir to the accumulating $post_rewrite.
			$post_rewrite = array_merge($rewrite, $post_rewrite);
		}

		// The finished rules. phew!
		return $post_rewrite;
	}

	/**
	 * Generates rewrite rules with permalink structure and walking directory only.
	 *
	 * Shorten version of WP_Rewrite::generate_rewrite_rules() that allows for shorter
	 * list of parameters. See the method for longer description of what generating
	 * rewrite rules does.
	 *
	 * @since 1.5.0
	 *
	 * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters.
	 *
	 * @param string $permalink_structure The permalink structure to generate rules.
	 * @param bool   $walk_dirs           Optional, default is false. Whether to create list of directories to walk over.
	 * @return array
	 */
	public function generate_rewrite_rule($permalink_structure, $walk_dirs = false) {
		return $this->generate_rewrite_rules($permalink_structure, EP_NONE, false, false, false, $walk_dirs);
	}

	/**
	 * Constructs rewrite matches and queries from permalink structure.
	 *
	 * Runs the action {@see 'generate_rewrite_rules'} with the parameter that is an
	 * reference to the current WP_Rewrite instance to further manipulate the
	 * permalink structures and rewrite rules. Runs the {@see 'rewrite_rules_array'}
	 * filter on the full rewrite rule array.
	 *
	 * There are two ways to manipulate the rewrite rules, one by hooking into
	 * the {@see 'generate_rewrite_rules'} action and gaining full control of the
	 * object or just manipulating the rewrite rule array before it is passed
	 * from the function.
	 *
	 * @since 1.5.0
	 *
	 * @return array An associate array of matches and queries.
	 */
	public function rewrite_rules() {
		$rewrite = array();

		if ( empty($this->permalink_structure) )
			return $rewrite;

		// robots.txt -only if installed at the root
		$home_path = parse_url( home_url() );
		$robots_rewrite = ( empty( $home_path['path'] ) || '/' == $home_path['path'] ) ? array( 'robots\.txt$' => $this->index . '?robots=1' ) : array();

		// Old feed and service files.
		$deprecated_files = array(
			'.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$' => $this->index . '?feed=old',
			'.*wp-app\.php(/.*)?$' => $this->index . '?error=403',
		);

		// Registration rules.
		$registration_pages = array();
		if ( is_multisite() && is_main_site() ) {
			$registration_pages['.*wp-signup.php$'] = $this->index . '?signup=true';
			$registration_pages['.*wp-activate.php$'] = $this->index . '?activate=true';
		}

		// Deprecated.
		$registration_pages['.*wp-register.php$'] = $this->index . '?register=true';

		// Post rewrite rules.
		$post_rewrite = $this->generate_rewrite_rules( $this->permalink_structure, EP_PERMALINK );

		/**
		 * Filters rewrite rules used for "post" archives.
		 *
		 * @since 1.5.0
		 *
		 * @param array $post_rewrite The rewrite rules for posts.
		 */
		$post_rewrite = apply_filters( 'post_rewrite_rules', $post_rewrite );

		// Date rewrite rules.
		$date_rewrite = $this->generate_rewrite_rules($this->get_date_permastruct(), EP_DATE);

		/**
		 * Filters rewrite rules used for date archives.
		 *
		 * Likely date archives would include /yyyy/, /yyyy/mm/, and /yyyy/mm/dd/.
		 *
		 * @since 1.5.0
		 *
		 * @param array $date_rewrite The rewrite rules for date archives.
		 */
		$date_rewrite = apply_filters( 'date_rewrite_rules', $date_rewrite );

		// Root-level rewrite rules.
		$root_rewrite = $this->generate_rewrite_rules($this->root . '/', EP_ROOT);

		/**
		 * Filters rewrite rules used for root-level archives.
		 *
		 * Likely root-level archives would include pagination rules for the homepage
		 * as well as site-wide post feeds (e.g. /feed/, and /feed/atom/).
		 *
		 * @since 1.5.0
		 *
		 * @param array $root_rewrite The root-level rewrite rules.
		 */
		$root_rewrite = apply_filters( 'root_rewrite_rules', $root_rewrite );

		// Comments rewrite rules.
		$comments_rewrite = $this->generate_rewrite_rules($this->root . $this->comments_base, EP_COMMENTS, false, true, true, false);

		/**
		 * Filters rewrite rules used for comment feed archives.
		 *
		 * Likely comments feed archives include /comments/feed/, and /comments/feed/atom/.
		 *
		 * @since 1.5.0
		 *
		 * @param array $comments_rewrite The rewrite rules for the site-wide comments feeds.
		 */
		$comments_rewrite = apply_filters( 'comments_rewrite_rules', $comments_rewrite );

		// Search rewrite rules.
		$search_structure = $this->get_search_permastruct();
		$search_rewrite = $this->generate_rewrite_rules($search_structure, EP_SEARCH);

		/**
		 * Filters rewrite rules used for search archives.
		 *
		 * Likely search-related archives include /search/search+query/ as well as
		 * pagination and feed paths for a search.
		 *
		 * @since 1.5.0
		 *
		 * @param array $search_rewrite The rewrite rules for search queries.
		 */
		$search_rewrite = apply_filters( 'search_rewrite_rules', $search_rewrite );

		// Author rewrite rules.
		$author_rewrite = $this->generate_rewrite_rules($this->get_author_permastruct(), EP_AUTHORS);

		/**
		 * Filters rewrite rules used for author archives.
		 *
		 * Likely author archives would include /author/author-name/, as well as
		 * pagination and feed paths for author archives.
		 *
		 * @since 1.5.0
		 *
		 * @param array $author_rewrite The rewrite rules for author archives.
		 */
		$author_rewrite = apply_filters( 'author_rewrite_rules', $author_rewrite );

		// Pages rewrite rules.
		$page_rewrite = $this->page_rewrite_rules();

		/**
		 * Filters rewrite rules used for "page" post type archives.
		 *
		 * @since 1.5.0
		 *
		 * @param array $page_rewrite The rewrite rules for the "page" post type.
		 */
		$page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite );

		// Extra permastructs.
		foreach ( $this->extra_permastructs as $permastructname => $struct ) {
			if ( is_array( $struct ) ) {
				if ( count( $struct ) == 2 )
					$rules = $this->generate_rewrite_rules( $struct[0], $struct[1] );
				else
					$rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
			} else {
				$rules = $this->generate_rewrite_rules( $struct );
			}

			/**
			 * Filters rewrite rules used for individual permastructs.
			 *
			 * The dynamic portion of the hook name, `$permastructname`, refers
			 * to the name of the registered permastruct, e.g. 'post_tag' (tags),
			 * 'category' (categories), etc.
			 *
			 * @since 3.1.0
			 *
			 * @param array $rules The rewrite rules generated for the current permastruct.
			 */
			$rules = apply_filters( "{$permastructname}_rewrite_rules", $rules );
			if ( 'post_tag' == $permastructname ) {

				/**
				 * Filters rewrite rules used specifically for Tags.
				 *
				 * @since 2.3.0
				 * @deprecated 3.1.0 Use 'post_tag_rewrite_rules' instead
				 *
				 * @param array $rules The rewrite rules generated for tags.
				 */
				$rules = apply_filters( 'tag_rewrite_rules', $rules );
			}

			$this->extra_rules_top = array_merge($this->extra_rules_top, $rules);
		}

		// Put them together.
		if ( $this->use_verbose_page_rules )
			$this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite,  $author_rewrite, $date_rewrite, $page_rewrite, $post_rewrite, $this->extra_rules);
		else
			$this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite,  $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules);

		/**
		 * Fires after the rewrite rules are generated.
		 *
		 * @since 1.5.0
		 *
		 * @param WP_Rewrite $this Current WP_Rewrite instance (passed by reference).
		 */
		do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );

		/**
		 * Filters the full set of generated rewrite rules.
		 *
		 * @since 1.5.0
		 *
		 * @param array $this->rules The compiled array of rewrite rules.
		 */
		$this->rules = apply_filters( 'rewrite_rules_array', $this->rules );

		return $this->rules;
	}

	/**
	 * Retrieves the rewrite rules.
	 *
	 * The difference between this method and WP_Rewrite::rewrite_rules() is that
	 * this method stores the rewrite rules in the 'rewrite_rules' option and retrieves
	 * it. This prevents having to process all of the permalinks to get the rewrite rules
	 * in the form of caching.
	 *
	 * @since 1.5.0
	 *
	 * @return array Rewrite rules.
	 */
	public function wp_rewrite_rules() {
		$this->rules = get_option('rewrite_rules');
		if ( empty($this->rules) ) {
			$this->matches = 'matches';
			$this->rewrite_rules();
			if ( ! did_action( 'wp_loaded' ) ) {
				add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
				return $this->rules;
			}
			update_option('rewrite_rules', $this->rules);
		}

		return $this->rules;
	}

	/**
	 * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess.
	 *
	 * Does not actually write to the .htaccess file, but creates the rules for
	 * the process that will.
	 *
	 * Will add the non_wp_rules property rules to the .htaccess file before
	 * the WordPress rewrite rules one.
	 *
	 * @since 1.5.0
	 *
	 * @return string
	 */
	public function mod_rewrite_rules() {
		if ( ! $this->using_permalinks() )
			return '';

		$site_root = parse_url( site_url() );
		if ( isset( $site_root['path'] ) )
			$site_root = trailingslashit($site_root['path']);

		$home_root = parse_url(home_url());
		if ( isset( $home_root['path'] ) )
			$home_root = trailingslashit($home_root['path']);
		else
			$home_root = '/';

		$rules = "<IfModule mod_rewrite.c>\n";
		$rules .= "RewriteEngine On\n";
		$rules .= "RewriteBase $home_root\n";

		// Prevent -f checks on index.php.
		$rules .= "RewriteRule ^index\.php$ - [L]\n";

		// Add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all).
		foreach ( (array) $this->non_wp_rules as $match => $query) {
			// Apache 1.3 does not support the reluctant (non-greedy) modifier.
			$match = str_replace('.+?', '.+', $match);

			$rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
		}

		if ( $this->use_verbose_rules ) {
			$this->matches = '';
			$rewrite = $this->rewrite_rules();
			$num_rules = count($rewrite);
			$rules .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
				"RewriteCond %{REQUEST_FILENAME} -d\n" .
				"RewriteRule ^.*$ - [S=$num_rules]\n";

			foreach ( (array) $rewrite as $match => $query) {
				// Apache 1.3 does not support the reluctant (non-greedy) modifier.
				$match = str_replace('.+?', '.+', $match);

				if ( strpos($query, $this->index) !== false )
					$rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
				else
					$rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
			}
		} else {
			$rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
				"RewriteCond %{REQUEST_FILENAME} !-d\n" .
				"RewriteRule . {$home_root}{$this->index} [L]\n";
		}

		$rules .= "</IfModule>\n";

		/**
		 * Filters the list of rewrite rules formatted for output to an .htaccess file.
		 *
		 * @since 1.5.0
		 *
		 * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
		 */
		$rules = apply_filters( 'mod_rewrite_rules', $rules );

		/**
		 * Filters the list of rewrite rules formatted for output to an .htaccess file.
		 *
		 * @since 1.5.0
		 * @deprecated 1.5.0 Use the mod_rewrite_rules filter instead.
		 *
		 * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
		 */
		return apply_filters( 'rewrite_rules', $rules );
	}

	/**
	 * Retrieves IIS7 URL Rewrite formatted rewrite rules to write to web.config file.
	 *
	 * Does not actually write to the web.config file, but creates the rules for
	 * the process that will.
	 *
	 * @since 2.8.0
	 *
	 * @param bool $add_parent_tags Optional. Whether to add parent tags to the rewrite rule sets.
	 *                              Default false.
	 * @return string IIS7 URL rewrite rule sets.
	 */
	public function iis7_url_rewrite_rules( $add_parent_tags = false ) {
		if ( ! $this->using_permalinks() )
			return '';
		$rules = '';
		if ( $add_parent_tags ) {
			$rules .= '<configuration>
	<system.webServer>
		<rewrite>
			<rules>';
		}

		$rules .= '
			<rule name="WordPress: ' . esc_attr( home_url() ) . '" patternSyntax="Wildcard">
				<match url="*" />
					<conditions>
						<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
						<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
					</conditions>
				<action type="Rewrite" url="index.php" />
			</rule>';

		if ( $add_parent_tags ) {
			$rules .= '
			</rules>
		</rewrite>
	</system.webServer>
</configuration>';
		}

		/**
		 * Filters the list of rewrite rules formatted for output to a web.config.
		 *
		 * @since 2.8.0
		 *
		 * @param string $rules Rewrite rules formatted for IIS web.config.
		 */
		return apply_filters( 'iis7_url_rewrite_rules', $rules );
	}

	/**
	 * Adds a rewrite rule that transforms a URL structure to a set of query vars.
	 *
	 * Any value in the $after parameter that isn't 'bottom' will result in the rule
	 * being placed at the top of the rewrite rules.
	 *
	 * @since 2.1.0
	 * @since 4.4.0 Array support was added to the `$query` parameter.
	 *
	 * @param string       $regex Regular expression to match request against.
	 * @param string|array $query The corresponding query vars for this rewrite rule.
	 * @param string       $after Optional. Priority of the new rule. Accepts 'top'
	 *                            or 'bottom'. Default 'bottom'.
	 */
	public function add_rule( $regex, $query, $after = 'bottom' ) {
		if ( is_array( $query ) ) {
			$external = false;
			$query = add_query_arg( $query, 'index.php' );
		} else {
			$index = false === strpos( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' );
			$front = substr( $query, 0, $index );

			$external = $front != $this->index;
		}

		// "external" = it doesn't correspond to index.php.
		if ( $external ) {
			$this->add_external_rule( $regex, $query );
		} else {
			if ( 'bottom' == $after ) {
				$this->extra_rules = array_merge( $this->extra_rules, array( $regex => $query ) );
			} else {
				$this->extra_rules_top = array_merge( $this->extra_rules_top, array( $regex => $query ) );
			}
		}
	}

	/**
	 * Adds a rewrite rule that doesn't correspond to index.php.
	 *
	 * @since 2.1.0
	 *
	 * @param string $regex Regular expression to match request against.
	 * @param string $query The corresponding query vars for this rewrite rule.
	 */
	public function add_external_rule( $regex, $query ) {
		$this->non_wp_rules[ $regex ] = $query;
	}

	/**
	 * Adds an endpoint, like /trackback/.
	 *
	 * @since 2.1.0
	 * @since 3.9.0 $query_var parameter added.
	 * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
	 *
	 * @see add_rewrite_endpoint() for full documentation.
	 * @global WP $wp
	 *
	 * @param string      $name      Name of the endpoint.
	 * @param int         $places    Endpoint mask describing the places the endpoint should be added.
	 * @param string|bool $query_var Optional. Name of the corresponding query variable. Pass `false` to
	 *                               skip registering a query_var for this endpoint. Defaults to the
	 *                               value of `$name`.
	 */
	public function add_endpoint( $name, $places, $query_var = true ) {
		global $wp;

		// For backward compatibility, if null has explicitly been passed as `$query_var`, assume `true`.
		if ( true === $query_var || null === func_get_arg( 2 ) ) {
			$query_var = $name;
		}
		$this->endpoints[] = array( $places, $name, $query_var );

		if ( $query_var ) {
			$wp->add_query_var( $query_var );
		}
	}

	/**
	 * Adds a new permalink structure.
	 *
	 * A permalink structure (permastruct) is an abstract definition of a set of rewrite rules;
	 * it is an easy way of expressing a set of regular expressions that rewrite to a set of
	 * query strings. The new permastruct is added to the WP_Rewrite::$extra_permastructs array.
	 *
	 * When the rewrite rules are built by WP_Rewrite::rewrite_rules(), all of these extra
	 * permastructs are passed to WP_Rewrite::generate_rewrite_rules() which transforms them
	 * into the regular expressions that many love to hate.
	 *
	 * The `$args` parameter gives you control over how WP_Rewrite::generate_rewrite_rules()
	 * works on the new permastruct.
	 *
	 * @since 2.5.0
	 *
	 * @param string $name   Name for permalink structure.
	 * @param string $struct Permalink structure (e.g. category/%category%)
	 * @param array  $args   {
	 *     Optional. Arguments for building rewrite rules based on the permalink structure.
	 *     Default empty array.
	 *
	 *     @type bool $with_front  Whether the structure should be prepended with `WP_Rewrite::$front`.
	 *                             Default true.
	 *     @type int  $ep_mask     The endpoint mask defining which endpoints are added to the structure.
	 *                             Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
	 *                             `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
	 *                             `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
	 *                             Default `EP_NONE`.
	 *     @type bool $paged       Whether archive pagination rules should be added for the structure.
	 *                             Default true.
	 *     @type bool $feed        Whether feed rewrite rules should be added for the structure. Default true.
	 *     @type bool $forcomments Whether the feed rules should be a query for a comments feed. Default false.
	 *     @type bool $walk_dirs   Whether the 'directories' making up the structure should be walked over
	 *                             and rewrite rules built for each in-turn. Default true.
	 *     @type bool $endpoints   Whether endpoints should be applied to the generated rules. Default true.
	 * }
	 */
	public function add_permastruct( $name, $struct, $args = array() ) {
		// Back-compat for the old parameters: $with_front and $ep_mask.
		if ( ! is_array( $args ) )
			$args = array( 'with_front' => $args );
		if ( func_num_args() == 4 )
			$args['ep_mask'] = func_get_arg( 3 );

		$defaults = array(
			'with_front' => true,
			'ep_mask' => EP_NONE,
			'paged' => true,
			'feed' => true,
			'forcomments' => false,
			'walk_dirs' => true,
			'endpoints' => true,
		);
		$args = array_intersect_key( $args, $defaults );
		$args = wp_parse_args( $args, $defaults );

		if ( $args['with_front'] )
			$struct = $this->front . $struct;
		else
			$struct = $this->root . $struct;
		$args['struct'] = $struct;

		$this->extra_permastructs[ $name ] = $args;
	}

	/**
	 * Removes a permalink structure.
	 *
	 * @since 4.5.0
	 *
	 * @param string $name Name for permalink structure.
	 */
	public function remove_permastruct( $name ) {
		unset( $this->extra_permastructs[ $name ] );
	}

	/**
	 * Removes rewrite rules and then recreate rewrite rules.
	 *
	 * Calls WP_Rewrite::wp_rewrite_rules() after removing the 'rewrite_rules' option.
	 * If the function named 'save_mod_rewrite_rules' exists, it will be called.
	 *
	 * @since 2.0.1
	 *
	 * @staticvar bool $do_hard_later
	 *
	 * @param bool $hard Whether to update .htaccess (hard flush) or just update rewrite_rules option (soft flush). Default is true (hard).
	 */
	public function flush_rules( $hard = true ) {
		static $do_hard_later = null;

		// Prevent this action from running before everyone has registered their rewrites.
		if ( ! did_action( 'wp_loaded' ) ) {
			add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
			$do_hard_later = ( isset( $do_hard_later ) ) ? $do_hard_later || $hard : $hard;
			return;
		}

		if ( isset( $do_hard_later ) ) {
			$hard = $do_hard_later;
			unset( $do_hard_later );
		}

		update_option( 'rewrite_rules', '' );
		$this->wp_rewrite_rules();

		/**
		 * Filters whether a "hard" rewrite rule flush should be performed when requested.
		 *
		 * A "hard" flush updates .htaccess (Apache) or web.config (IIS).
		 *
		 * @since 3.7.0
		 *
		 * @param bool $hard Whether to flush rewrite rules "hard". Default true.
		 */
		if ( ! $hard || ! apply_filters( 'flush_rewrite_rules_hard', true ) ) {
			return;
		}
		if ( function_exists( 'save_mod_rewrite_rules' ) )
			save_mod_rewrite_rules();
		if ( function_exists( 'iis7_save_url_rewrite_rules' ) )
			iis7_save_url_rewrite_rules();
	}

	/**
	 * Sets up the object's properties.
	 *
	 * The 'use_verbose_page_rules' object property will be set to true if the
	 * permalink structure begins with one of the following: '%postname%', '%category%',
	 * '%tag%', or '%author%'.
	 *
	 * @since 1.5.0
	 */
	public function init() {
		$this->extra_rules = $this->non_wp_rules = $this->endpoints = array();
		$this->permalink_structure = get_option('permalink_structure');
		$this->front = substr($this->permalink_structure, 0, strpos($this->permalink_structure, '%'));
		$this->root = '';

		if ( $this->using_index_permalinks() )
			$this->root = $this->index . '/';

		unset($this->author_structure);
		unset($this->date_structure);
		unset($this->page_structure);
		unset($this->search_structure);
		unset($this->feed_structure);
		unset($this->comment_feed_structure);
		$this->use_trailing_slashes = ( '/' == substr($this->permalink_structure, -1, 1) );

		// Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
		if ( preg_match("/^[^%]*%(?:postname|category|tag|author)%/", $this->permalink_structure) )
			 $this->use_verbose_page_rules = true;
		else
			$this->use_verbose_page_rules = false;
	}

	/**
	 * Sets the main permalink structure for the site.
	 *
	 * Will update the 'permalink_structure' option, if there is a difference
	 * between the current permalink structure and the parameter value. Calls
	 * WP_Rewrite::init() after the option is updated.
	 *
	 * Fires the {@see 'permalink_structure_changed'} action once the init call has
	 * processed passing the old and new values
	 *
	 * @since 1.5.0
	 *
	 * @param string $permalink_structure Permalink structure.
	 */
	public function set_permalink_structure($permalink_structure) {
		if ( $permalink_structure != $this->permalink_structure ) {
			$old_permalink_structure = $this->permalink_structure;
			update_option('permalink_structure', $permalink_structure);

			$this->init();

			/**
			 * Fires after the permalink structure is updated.
			 *
			 * @since 2.8.0
			 *
			 * @param string $old_permalink_structure The previous permalink structure.
			 * @param string $permalink_structure     The new permalink structure.
			 */
			do_action( 'permalink_structure_changed', $old_permalink_structure, $permalink_structure );
		}
	}

	/**
	 * Sets the category base for the category permalink.
	 *
	 * Will update the 'category_base' option, if there is a difference between
	 * the current category base and the parameter value. Calls WP_Rewrite::init()
	 * after the option is updated.
	 *
	 * @since 1.5.0
	 *
	 * @param string $category_base Category permalink structure base.
	 */
	public function set_category_base($category_base) {
		if ( $category_base != get_option('category_base') ) {
			update_option('category_base', $category_base);
			$this->init();
		}
	}

	/**
	 * Sets the tag base for the tag permalink.
	 *
	 * Will update the 'tag_base' option, if there is a difference between the
	 * current tag base and the parameter value. Calls WP_Rewrite::init() after
	 * the option is updated.
	 *
	 * @since 2.3.0
	 *
	 * @param string $tag_base Tag permalink structure base.
	 */
	public function set_tag_base( $tag_base ) {
		if ( $tag_base != get_option( 'tag_base') ) {
			update_option( 'tag_base', $tag_base );
			$this->init();
		}
	}

	/**
	 * Constructor - Calls init(), which runs setup.
	 *
	 * @since 1.5.0
	 *
	 */
	public function __construct() {
		$this->init();
	}
}

Cвязанные функции

Из метки: Rewrite (ЧПУ перезапись)

Еще из раздела: Классы

25 комментов
Полезные 4 Вопросы 1 Все
  • Александр

    Можно ли удалять правила из данного класса!?

    Ответить2.2 года назад #
    • Kama6811

      Да конечно. Юзай хук rewrite_rules_array

      // удалим правила
      add_filter('rewrite_rules_array', 'my_remove_rewrite_rules');
      function my_remove_rewrite_rules( $rules ){
      	unset( $rules['(project)/(\d*)$'] );
      
      	return $rules;
      }
      Ответить2.2 года назад #
      • Александр

        Спс! Сейчас буду пробовать!

        Ответить2.2 года назад #
        • Kama6811

          Просто обновить настройки ЧПУ в настройках ВП не забудь после вставки хука...

          Ответить2.2 года назад #
          • Александр

            Спасибо! Хук сработал, избавился от гадости которая все портила, но появилась загвоздка такого рода, прописываю правило

            '^([^/]*)/([^/]*)/page/?([0-9]{1,})/?$' => 'index.php?post_type=$matches[1]&name=$matches[2]&paged=$matches[3]'

            обновляю ЧПУ в ВП

            перехожу на страницу site.net/category/brend/page/2/ и получается изза /page/ почему то не хочет правильно работать, делаю правило опираясь на те которые в самом классе, может подскажете что я не правильно делаю!? Если не до дал какую то информацию нужную скажите, опишу!

            Ответить2.2 года назад #
            • Kama6811

              Потому что для site.net/category/brend/page/2/ срабатывает твое новое правило, а оно отвечает за тип записи. Чтобы поправить, попробуй свое новое правило, поставить ниже правил рубрик, чтобы правила рубрик срабатывали раньше и если они не подойдут, то проверка дойдет до твоего правила...

              Ответить2.2 года назад #
              • Александр

                Думаю немного нужно описать структуру каталога, структура состоит из произвольного типа записи category->brend, "category" родительские посты, "brend" является дочерними постами, возможно я не правильно прописываю структуру в начале, возможно стоит попробовать добавить ЧПУ для дочерних страниц add_rewrite_rule( '^(parent/slug)/([^/]*)', 'index.php?pagename=$matches[1]&foo=$matches[2]', 'top' ); или я смотрю не в ту сторону!

                Ответить2.2 года назад #
  • Александр

    Доброго времени суток! Возвращаюсь вновь к данной теме, подскажите пожалуйста как правильно сделать такую вещь - создал произвольный тип записи product, ЧПУ в админке настроены таким образом /%category%/%postname%/, url такого рода mysite/product/velosiped, хочу сделать mysite/velosiped, то есть убрать название произвольного типа из url, как это правильно сделать!? Заранее спасибо!

    Ответить1.8 год назад #
    • Александр

      Вопрос решён, извиняюсь за тревогу! Сменил /%category%/%postname%/ на /%postname%/ и в конец functions.php добавил немного кода

      function remove_slug($post_link, $post, $leavename)
      {
      	//if ('products' != $post->post_type || 'publish' != $post->post_status) {
      	//    return $post_link;
      	//}
      	$post_link = str_replace('/' . $post->post_type . '/', '/', $post_link);
      	return $post_link;
      }
      
      add_filter('post_type_link', 'remove_slug', 10, 3);
      
      function parse_request( $query ) {
      	if ( ! $query->is_main_query() )
      		return;
      	if ( 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
      		return;
      	}
      	if ( ! empty( $query->query['name'] ) ) {
      		$query->set( 'post_type', array( 'post', 'product', 'page' ) );
      	}
      }
      add_action( 'pre_get_posts', 'parse_request' );

      в строке

      $query->set( 'post_type', array( 'post', 'product', 'page' ) );

      поставил тип записи product
      может кому пригодится

      2
      Ответить1.8 год назад #
      • Kama6811

        Добавил ваш код в примеры... Спасибо! thank_you

        Ответить1.8 год назад #
      • Татьяна cайт: 24proweb.ru

        Большое вам спасибо Kama и Александр!
        Очень выручили - у меня наконец-то получилось убрать название типа записи из всех ссылок так, чтобы никакие страницы не "поломались".

        До этого мне попадались примеры подобного кода, но не хватало заветных нюансов, из-за чего моя главная страница просто "терялась" и начинала показывать все посты и страницы вперемешку, абсолютно игнорируя, что ей указано показывать конкретную страницу.

        Хочу поделиться, потому как код Александра как он есть, не решает эту задачу окончательно.
        Да, из ссылок на сайте действительно убирается название типа записи, но в админке оно остается как при редактировании статьи с произвольным post_type в строке "Постоянная ссылка", так и при отображении списка всех этих страниц при наведении на ссылку "Перейти" у любой их этих записей.

        Путем многочасовых мучений я для себя оставила только две функции, которые удовлетворяют решению этой задачи:

        1) Первым делом меняем структуру ссылок для наших post_type (как оказалось, в фильтре add_filter('post_type_link', 'remove_slug', 10, 3); из кода Александра вообще нет надобности).

        У меня были ссылки вида mysite.ru/pool-services/postname, мне нужно было просто mysite.ru/postname, что мы и делаем с помощью add_permastruct:

        function pw24_post_type_rewrite() {
        	global $wp_rewrite;
        	// в данном случае тип записи - pool_services
        	$wp_rewrite->add_rewrite_tag("%pool_services%", '([^/]+)', "pool_services=");
        	$wp_rewrite->add_permastruct('pool_services', '%pool_services%' );
        }
        
        add_action( 'init', 'pw24_post_type_rewrite');

        2) А вот далее меня спасла как раз вторая функция Александра:

        function pw24_add_post_type_to_get_posts_request( $query ){
        	if( is_admin() || ! $query->is_main_query() ) return; // не основной запрос
        
        	// не запрос с name параметром (как у постоянной страницы)
        	if( ! isset($query->query['page']) || empty($query->query['name']) || count($query->query) != 2 )
        		return;
        
        	$query->set('post_type', array('post', 'page', 'pool_services') ); // добавляем 'pool_services'
        }
        add_action( 'pre_get_posts', 'pw24_add_post_type_to_get_posts_request' );

        И всё! Задача решена - теперь в ссылках и в админке и на сайте отсутствует название типа записи 'pool-services' и все страницы работают без проблем.

        2
        Ответитьмесяц назад #
        • Kama6811

          Молодец! Только еще добавлю, что в этом случае при регистрации типа записи хорошо бы поставить параметр rewrite=false.

          А вообще, вот это решение с pre_get_posts, мне не нравится, на костыль похоже. Там по-другому можно сделать, но так как тестировать не буду, поэтому код не покажу, как столкнусь с такой задачей, обновлю код этот в статье!

          П.С. добавил и ваш пример в статью, спасибо!

          Ответитьмесяц назад #
  • Вася

    Пример #5 Добавим .html для постоянных страниц WordPress
    А как добавить к постам?

    Ответить1.6 год назад #
  • @ Наталия cайт: stupeni.org

    А у меня такая проблема: сейчас стоит структура:/%year%/%monthnum%/%day%/%postname%/ (настраивали давно) сейчас хотим сделать просто /%postname%/ Как сделать перенаправление, чтобы применено было ко всему сайту. Страниц много уже. И не упадут ли после этого позиции сайта, вот еще вопрос.

    Ответить1.5 год назад #
    • Kama6811

      Надо написать регулярку под шаблон: если URL соответствует шаблону: \/[0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2}\/([^/]+)\/?, то перенаправить на последний элемент с 301 редиректом. Можно сделать на PHP, но лучше через .htaccess

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

      Ответить1.5 год назад #
  • Я уже второй день ломаю голову: я написал свой скрипт(плагин) локализации, но сейчас языки переключаются, записывая текущее значение языка сайта в куки, т.е. адрес страницы на английском не отличается от адреса на русском - а это очень не хорошо для СЕО, да и для юзабилити не очень, хоть язык и автоматом определяется при первом посещении - всё равно не дело.

    Задача сделать рерайт ВСЕХ адресов, включая главную на uri вида site.ru/LANG_CODE/page/etc. При этом переменная для редиректа должна быть из куки. Подскажите, какими методами-функциями нужно пользоваться, я просто видимо не могу до конца разобраться с классом.

    Ответить1.4 год назад #
    • После долгих тщетных поисков на просторах стаковерфлова нашёл скрипт, который меня почти устраивает:

      class lou_rewrite_takeover {
        protected static $add_rules = array();
      
        public static function pre_init() {
      	// debug
      	add_action('admin_footer-options-permalink.php', array(__CLASS__, 'qsart_rewrite_debug'));
      
      	// add rw tag
      	add_action('init', array(__CLASS__, 'add_directory_rewrite'));
      
      	// rw rule adds
      	add_filter(is_admin() ? 'setup_theme' : 'do_parse_request', array(__CLASS__, 'do_parse_request'), 0);
      	add_filter('post_rewrite_rules', array(__CLASS__, 'post_rewrite_rules'));
      	add_filter('date_rewrite_rules', array(__CLASS__, 'date_rewrite_rules'));
      	add_filter('root_rewrite_rules', array(__CLASS__, 'root_rewrite_rules'));
      	add_filter('comments_rewrite_rules', array(__CLASS__, 'comments_rewrite_rules'));
      	add_filter('search_rewrite_rules', array(__CLASS__, 'search_rewrite_rules'));
      	add_filter('author_rewrite_rules', array(__CLASS__, 'author_rewrite_rules'));
      	add_filter('page_rewrite_rules', array(__CLASS__, 'page_rewrite_rules'));
      	add_filter('rewrite_rules_array', array(__CLASS__, 'final_rules_correction'), PHP_INT_MAX, 1);
      
      	// query vars
      	add_filter('query_vars', array(__CLASS__, 'add_lang_query_var'), 10, 1);
      	add_filter('request', array(__CLASS__, 'default_language'), 9);
      
      	// fix permalinks
      	$link_filters_needing_rewrite = array(
      	  'post_link',
      	  'post_type_link',
      	  'page_link',
      	  'attachment_link',
      	  'search_link',
      	  'post_type_archive_link',
      	  'year_link',
      	  'month_link',
      	  'day_link',
      	  'feed_link',
      	  'author_link',
      	  'term_link',
      	  'category_feed_link',
      	  'term_feed_link',
      	  'taxonomy_feed_link',
      	  'author_feed_link',
      	  'search_feed_link',
      	  'post_type_archive_feed_link',
      	);
      	add_filter('pre_post_link', array(__CLASS__, 'change_permalink_structure'), 10, 3);
      	foreach ($link_filters_needing_rewrite as $link_filter)
      	  add_filter($link_filter, array(__CLASS__, 'rewrite_lang_in_permalink'), 11, 3);
        }
      
        public static function do_parse_request($cur) {
      	self::get_page_permastruct();
      	self::get_author_permastruct();
      	self::correct_extras();
      	return $cur;
        }
      
        public static function get_supported_langs() {
      	return apply_filters('lou-get-supported-languages', array(
      	  'en', 'ru', 'fr', 'de'
      	));
        }
      
        public static function add_directory_rewrite() {
      	global $wp_rewrite;
      	$supported_languages = self::get_supported_langs();
      	add_rewrite_tag('%lang%', '('.implode('|', $supported_languages).')');
        }
      
        public static function unleadingslashit($str) {
      	return ltrim($str, '/');
        }
      
        public static function final_rules_correction($rules) {
      	global $wp_rewrite;
      
      	$new_rules = array();
      	$supported_languages = self::get_supported_langs();
      	$find = implode('|', $supported_languages);
      	$find_find = '#(?preg_index('%%%'), '#'));
      
      	foreach ($rules as $k => $v) {
      	  if (preg_match($find_find, $k)) {
      		$nk = preg_replace($find_find, '('.$find.')', $k);
      		$parts = explode('?', $v);
      		$index = array_shift($parts);
      		$pv = implode('?', $parts);
      		$pv = preg_replace_callback('#'.$preg_node.'#', function ($matches) use ($wp_rewrite) {
      		  return $wp_rewrite->preg_index($matches[1]+1);
      		}, $pv);
      		$nv = $index.'?lang='.$wp_rewrite->preg_index(1).(!empty($pv) ? '&'.$pv : '');
      		$new_rules[$nk] = $nv;
      	  } else {
      		$new_rules[$k] = $v;
      	  }
      	}
      
      	return $new_rules;
        }
      
        public static function change_permalink_structure($struct) {
      	$struct = self::unleadingslashit($struct);
      	$struct = preg_replace('#^%lang%/?#', '', $struct);
      	return '/%lang%/'.$struct;
        }
      
        public static function extras_rewrite_rules($rules, $struct) {
      	global $wp_rewrite;
      
      	if ( is_array( $struct ) ) {
      	  if ( count( $struct ) == 2 )
      		$new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct[0]), $struct[1] );
      	  else
      		$new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct['struct']), $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
      	} else {
      	  $new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct) );
      	}
      
      	return $new_rules + $rules;
        }
      
        public static function post_rewrite_rules($rules) {
      	global $wp_rewrite;
      
      	// hack to add code for extras type urls (usually created by other plugins)
      	$func = array(__CLASS__, 'extras_rewrite_rules');
      	foreach ($wp_rewrite->extra_permastructs as $type => $struct) {
      	  $filter = ($type == 'post_tag' ? 'tag' : $type).'_rewrite_rules';
      	  add_filter($filter, function ($rules) use ($struct, $func) { return call_user_func_array($func, array($rules, $struct)); });
      	}
      
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->permalink_structure), EP_PERMALINK ) + $rules;
        }
      
        public static function date_rewrite_rules($rules) {
      	global $wp_rewrite;
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_date_permastruct()), EP_DATE) + $rules;
        }
      
        public static function root_rewrite_rules($rules) {
      	global $wp_rewrite;
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_date_permastruct()), EP_DATE) + $rules;
        }
      
        public static function comments_rewrite_rules($rules) {
      	global $wp_rewrite;
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->root . $wp_rewrite->comments_base), EP_COMMENTS, false, true, true, false) + $rules;
        }
      
        public static function search_rewrite_rules($rules) {
      	global $wp_rewrite;
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_search_permastruct()), EP_SEARCH) + $rules;
        }
      
        public static function author_rewrite_rules($rules) {
      	global $wp_rewrite;
      	return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_author_permastruct()), EP_AUTHORS) + $rules;
        }
      
        public static function page_rewrite_rules($rules) {
      	global $wp_rewrite;
      	$page_structure = self::get_page_permastruct();
      	return $wp_rewrite->generate_rewrite_rules( $page_structure, EP_PAGES, true, true, false, false ) + $rules;
        }
      
        protected static function get_page_permastruct() {
      	global $wp_rewrite;
      
      	if (empty($wp_rewrite->permalink_structure)) {
      	  $wp_rewrite->page_structure = '';
      	  return false;
      	}
      
      	$wp_rewrite->page_structure = self::change_permalink_structure($wp_rewrite->root . '%pagename%');
      
      	return $wp_rewrite->page_structure;
        }
      
        protected static function get_author_permastruct() {
      	global $wp_rewrite;
      
      	if ( empty($wp_rewrite->permalink_structure) ) {
      	  $wp_rewrite->author_structure = '';
      	  return false;
      	}
      
      	$wp_rewrite->author_structure = self::change_permalink_structure($wp_rewrite->front . $wp_rewrite->author_base . '/%author%');
      
      	return $wp_rewrite->author_structure;
        }
      
        protected static function correct_extras() {
      	global $wp_rewrite;
      
      	foreach ($wp_rewrite->extra_permastructs as $k => $v)
      	  $wp_rewrite->extra_permastructs[$k]['struct'] = self::change_permalink_structure($v['struct']);
        }
      
        public static function get_default_post_lang($post) {
      	return ( $lang = $_COOKIE['lang'] ) ? $lang : 'en';
        }
      
        public static function rewrite_lang_in_permalink($permalink, $post=0, $leavename=false) {
      	// find the default post language via a function you have created to
      	// determine the default language url. this could be based on the current
      	// language the user has selected on the frontend, or based on the current
      	// url, or based on the post itself. it is up to you
      	$lang = self::get_default_post_lang($post);
      
      	// once you have the default language, it is a simple search and replace
      	return str_replace('%lang%', $lang, $permalink);
        }
      
        public static function add_lang_query_var($vars) {
      	// tell WP to expect the lang query_var, which you can then later use
      	$vars[] = 'lang';
      
      	// return the new list of query vars, which includes our lang param
      	return array_unique($vars);
        }
      
        public static function default_language($vars) {
      	if (array_diff( array_keys($vars), array('preview', 'page', 'paged', 'cpage') ))
      	  $vars['lang'] = !empty($vars['lang']) ? $vars['lang'] : 'en';
      	return $vars;
        }
      
        public static function qsart_rewrite_debug() {
      	if (isset($_COOKIE['rwdebug']) && $_COOKIE['rwdebug'] == 1) {
      	  global $wp_rewrite;
      	  echo '';
      	  print_r($wp_rewrite->rules);
      	  echo '';
      	}
        }
      }
      
      if (defined('ABSPATH') && function_exists('add_action')) {
        lou_rewrite_takeover::pre_init();
      }

      Проблемы у него такие: не добавляет языковой постфикс для главной страницы и при переключении языка случается такая ссылка: /ru/test-lokali/?lang=en

      Ответить1.4 год назад #
      • Kama6811

        Ну тут вам думаю никто не поможет, в это дело надо вникать сидеть, тестировать... Так сходу нечего сказать. Делай редирект с /ru/test-lokali/?lang=en на /ru/test-lokali/.

        Ответить1.4 год назад #
  • Orka

    Приветствую, такой вопрос,
    как убрать слаг таксономии из урла, пытался сделать по аналогии с category
    долго ковырял код плагина "no-category-base-wpml" но чтот не выходит то одно отваливается то другое.

    $args = array(
    	'taxonomy'      => array( 'metro','services'), 
    	'hide_empty'    => false
    	); 
    
    	$myterms = get_terms( $args );
    	foreach ($myterms as $term){
    		$nt[$term->taxonomy][]=$term;
    	}
    
    	foreach( $nt as $key=>$term ) {
    		foreach($term as $t){
    			$term_nicename = $t->slug;
    		$category_rewrite['('.$term_nicename.')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?'.$key.'=$matches[1]&feed=$matches[2]';
    		$category_rewrite["({$term_nicename})/{$wp_rewrite->pagination_base}/?([0-9]{1,})/?$"] = 'index.php?'.$key.'=$matches[1]&paged=$matches[2]';
    		$category_rewrite['('.$term_nicename.')/?$'] = 'index.php?'.$key.'=$matches[1]';
    		}
    
    	}

    перезаписывал правила, вроде все ок
    смущает только category_name в правилах перезаписи я указывал как видно из кода слаг таксономии правильно ли это? может что еще можете подсказать?

    Ответить1.3 год назад #
  • Сергей

    не могу понять, должен ли срабатывать пример с добавлением .html с произвольным типом записи? У меня не работает sad

    Ответитьгод назад #
    • Сергей

      сделал с помощью другога примера из статьи, спасибо

      1
      Ответитьгод назад #
  • @ dontbbs2004

    Добрый день.
    Реальна ли такая задача?
    Есть записи и переменная в сессии или куках $lang.
    Можно ли значение этой переменной выводить в url при открытии ссылки.
    Например, есть запись с адресом http://site.ru/stimul/

    если значение $lang=en, то url будет

    http://site.ru/en/stimul/

    Значение может быть любым, но пост должен открываться один и тот же.

    Ответить4 месяца назад #
    • @ campusboy2994 cайт: www.youtube.com/c/wpplus

      Привет. Я думаю, что реальна, ведь плагины мультиязычности так и делают (один из вариантов построения url).

      Ответить4 месяца назад #
      • @ dontbbs2004

        перекопал кучу сайтов по запросу, но ничего не нашел.
        Есть варианты подмены гет запроса на профикс, но в конце адреса, а не после домена

        Ответить4 месяца назад #
Здравствуйте, !     Войти . Зарегистрироваться