home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / class-wp-rewrite.php < prev    next >
Encoding:
PHP Script  |  2017-10-02  |  57.5 KB  |  1,892 lines

  1. <?php
  2. /**
  3.  * Rewrite API: WP_Rewrite class
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Rewrite
  7.  * @since 1.5.0
  8.  */
  9.  
  10. /**
  11.  * Core class used to implement a rewrite component API.
  12.  *
  13.  * The WordPress Rewrite class writes the rewrite module rules to the .htaccess
  14.  * file. It also handles parsing the request to get the correct setup for the
  15.  * WordPress Query class.
  16.  *
  17.  * The Rewrite along with WP class function as a front controller for WordPress.
  18.  * You can add rules to trigger your page view and processing using this
  19.  * component. The full functionality of a front controller does not exist,
  20.  * meaning you can't define how the template files load based on the rewrite
  21.  * rules.
  22.  *
  23.  * @since 1.5.0
  24.  */
  25. class WP_Rewrite {
  26.     /**
  27.      * Permalink structure for posts.
  28.      *
  29.      * @since 1.5.0
  30.      * @var string
  31.      */
  32.     public $permalink_structure;
  33.  
  34.     /**
  35.      * Whether to add trailing slashes.
  36.      *
  37.      * @since 2.2.0
  38.      * @var bool
  39.      */
  40.     public $use_trailing_slashes;
  41.  
  42.     /**
  43.      * Base for the author permalink structure (example.com/$author_base/authorname).
  44.      *
  45.      * @since 1.5.0
  46.      * @var string
  47.      */
  48.     var $author_base = 'author';
  49.  
  50.     /**
  51.      * Permalink structure for author archives.
  52.      *
  53.      * @since 1.5.0
  54.      * @var string
  55.      */
  56.     var $author_structure;
  57.  
  58.     /**
  59.      * Permalink structure for date archives.
  60.      *
  61.      * @since 1.5.0
  62.      * @var string
  63.      */
  64.     var $date_structure;
  65.  
  66.     /**
  67.      * Permalink structure for pages.
  68.      *
  69.      * @since 1.5.0
  70.      * @var string
  71.      */
  72.     var $page_structure;
  73.  
  74.     /**
  75.      * Base of the search permalink structure (example.com/$search_base/query).
  76.      *
  77.      * @since 1.5.0
  78.      * @var string
  79.      */
  80.     var $search_base = 'search';
  81.  
  82.     /**
  83.      * Permalink structure for searches.
  84.      *
  85.      * @since 1.5.0
  86.      * @var string
  87.      */
  88.     var $search_structure;
  89.  
  90.     /**
  91.      * Comments permalink base.
  92.      *
  93.      * @since 1.5.0
  94.      * @var string
  95.      */
  96.     var $comments_base = 'comments';
  97.  
  98.     /**
  99.      * Pagination permalink base.
  100.      *
  101.      * @since 3.1.0
  102.      * @var string
  103.      */
  104.     public $pagination_base = 'page';
  105.  
  106.     /**
  107.      * Comments pagination permalink base.
  108.      *
  109.      * @since 4.2.0
  110.      * @var string
  111.      */
  112.     var $comments_pagination_base = 'comment-page';
  113.  
  114.     /**
  115.      * Feed permalink base.
  116.      *
  117.      * @since 1.5.0
  118.      * @var string
  119.      */
  120.     var $feed_base = 'feed';
  121.  
  122.     /**
  123.      * Comments feed permalink structure.
  124.      *
  125.      * @since 1.5.0
  126.      * @var string
  127.      */
  128.     var $comment_feed_structure;
  129.  
  130.     /**
  131.      * Feed request permalink structure.
  132.      *
  133.      * @since 1.5.0
  134.      * @var string
  135.      */
  136.     var $feed_structure;
  137.  
  138.     /**
  139.      * The static portion of the post permalink structure.
  140.      *
  141.      * If the permalink structure is "/archive/%post_id%" then the front
  142.      * is "/archive/". If the permalink structure is "/%year%/%postname%/"
  143.      * then the front is "/".
  144.      *
  145.      * @since 1.5.0
  146.      * @var string
  147.      *
  148.      * @see WP_Rewrite::init()
  149.      */
  150.     public $front;
  151.  
  152.     /**
  153.      * The prefix for all permalink structures.
  154.      *
  155.      * If PATHINFO/index permalinks are in use then the root is the value of
  156.      * `WP_Rewrite::$index` with a trailing slash appended. Otherwise the root
  157.      * will be empty.
  158.      *
  159.      * @since 1.5.0
  160.      * @var string
  161.      *
  162.      * @see WP_Rewrite::init()
  163.      * @see WP_Rewrite::using_index_permalinks()
  164.      */
  165.     public $root = '';
  166.  
  167.     /**
  168.      * The name of the index file which is the entry point to all requests.
  169.      *
  170.      * @since 1.5.0
  171.      * @var string
  172.      */
  173.     public $index = 'index.php';
  174.  
  175.     /**
  176.      * Variable name to use for regex matches in the rewritten query.
  177.      *
  178.      * @since 1.5.0
  179.      * @var string
  180.      */
  181.     var $matches = '';
  182.  
  183.     /**
  184.      * Rewrite rules to match against the request to find the redirect or query.
  185.      *
  186.      * @since 1.5.0
  187.      * @var array
  188.      */
  189.     var $rules;
  190.  
  191.     /**
  192.      * Additional rules added external to the rewrite class.
  193.      *
  194.      * Those not generated by the class, see add_rewrite_rule().
  195.      *
  196.      * @since 2.1.0
  197.      * @var array
  198.      */
  199.     var $extra_rules = array();
  200.  
  201.     /**
  202.      * Additional rules that belong at the beginning to match first.
  203.      *
  204.      * Those not generated by the class, see add_rewrite_rule().
  205.      *
  206.      * @since 2.3.0
  207.      * @var array
  208.      */
  209.     var $extra_rules_top = array();
  210.  
  211.     /**
  212.      * Rules that don't redirect to WordPress' index.php.
  213.      *
  214.      * These rules are written to the mod_rewrite portion of the .htaccess,
  215.      * and are added by add_external_rule().
  216.      *
  217.      * @since 2.1.0
  218.      * @var array
  219.      */
  220.     var $non_wp_rules = array();
  221.  
  222.     /**
  223.      * Extra permalink structures, e.g. categories, added by add_permastruct().
  224.      *
  225.      * @since 2.1.0
  226.      * @var array
  227.      */
  228.     var $extra_permastructs = array();
  229.  
  230.     /**
  231.      * Endpoints (like /trackback/) added by add_rewrite_endpoint().
  232.      *
  233.      * @since 2.1.0
  234.      * @var array
  235.      */
  236.     var $endpoints;
  237.  
  238.     /**
  239.      * Whether to write every mod_rewrite rule for WordPress into the .htaccess file.
  240.      *
  241.      * This is off by default, turning it on might print a lot of rewrite rules
  242.      * to the .htaccess file.
  243.      *
  244.      * @since 2.0.0
  245.      * @var bool
  246.      *
  247.      * @see WP_Rewrite::mod_rewrite_rules()
  248.      */
  249.     public $use_verbose_rules = false;
  250.  
  251.     /**
  252.      * Could post permalinks be confused with those of pages?
  253.      *
  254.      * If the first rewrite tag in the post permalink structure is one that could
  255.      * also match a page name (e.g. %postname% or %author%) then this flag is
  256.      * set to true. Prior to WordPress 3.3 this flag indicated that every page
  257.      * would have a set of rules added to the top of the rewrite rules array.
  258.      * Now it tells WP::parse_request() to check if a URL matching the page
  259.      * permastruct is actually a page before accepting it.
  260.      *
  261.      * @since 2.5.0
  262.      * @var bool
  263.      *
  264.      * @see WP_Rewrite::init()
  265.      */
  266.     public $use_verbose_page_rules = true;
  267.  
  268.     /**
  269.      * Rewrite tags that can be used in permalink structures.
  270.      *
  271.      * These are translated into the regular expressions stored in
  272.      * `WP_Rewrite::$rewritereplace` and are rewritten to the query
  273.      * variables listed in WP_Rewrite::$queryreplace.
  274.      *
  275.      * Additional tags can be added with add_rewrite_tag().
  276.      *
  277.      * @since 1.5.0
  278.      * @var array
  279.      */
  280.     var $rewritecode = array(
  281.         '%year%',
  282.         '%monthnum%',
  283.         '%day%',
  284.         '%hour%',
  285.         '%minute%',
  286.         '%second%',
  287.         '%postname%',
  288.         '%post_id%',
  289.         '%author%',
  290.         '%pagename%',
  291.         '%search%'
  292.     );
  293.  
  294.     /**
  295.      * Regular expressions to be substituted into rewrite rules in place
  296.      * of rewrite tags, see WP_Rewrite::$rewritecode.
  297.      *
  298.      * @since 1.5.0
  299.      * @var array
  300.      */
  301.     var $rewritereplace = array(
  302.         '([0-9]{4})',
  303.         '([0-9]{1,2})',
  304.         '([0-9]{1,2})',
  305.         '([0-9]{1,2})',
  306.         '([0-9]{1,2})',
  307.         '([0-9]{1,2})',
  308.         '([^/]+)',
  309.         '([0-9]+)',
  310.         '([^/]+)',
  311.         '([^/]+?)',
  312.         '(.+)'
  313.     );
  314.  
  315.     /**
  316.      * Query variables that rewrite tags map to, see WP_Rewrite::$rewritecode.
  317.      *
  318.      * @since 1.5.0
  319.      * @var array
  320.      */
  321.     var $queryreplace = array(
  322.         'year=',
  323.         'monthnum=',
  324.         'day=',
  325.         'hour=',
  326.         'minute=',
  327.         'second=',
  328.         'name=',
  329.         'p=',
  330.         'author_name=',
  331.         'pagename=',
  332.         's='
  333.     );
  334.  
  335.     /**
  336.      * Supported default feeds.
  337.      *
  338.      * @since 1.5.0
  339.      * @var array
  340.      */
  341.     public $feeds = array( 'feed', 'rdf', 'rss', 'rss2', 'atom' );
  342.  
  343.     /**
  344.      * Determines whether permalinks are being used.
  345.      *
  346.      * This can be either rewrite module or permalink in the HTTP query string.
  347.      *
  348.      * @since 1.5.0
  349.      *
  350.      * @return bool True, if permalinks are enabled.
  351.      */
  352.     public function using_permalinks() {
  353.         return ! empty($this->permalink_structure);
  354.     }
  355.  
  356.     /**
  357.      * Determines whether permalinks are being used and rewrite module is not enabled.
  358.      *
  359.      * Means that permalink links are enabled and index.php is in the URL.
  360.      *
  361.      * @since 1.5.0
  362.      *
  363.      * @return bool Whether permalink links are enabled and index.php is in the URL.
  364.      */
  365.     public function using_index_permalinks() {
  366.         if ( empty( $this->permalink_structure ) ) {
  367.             return false;
  368.         }
  369.  
  370.         // If the index is not in the permalink, we're using mod_rewrite.
  371.         return preg_match( '#^/*' . $this->index . '#', $this->permalink_structure );
  372.     }
  373.  
  374.     /**
  375.      * Determines whether permalinks are being used and rewrite module is enabled.
  376.      *
  377.      * Using permalinks and index.php is not in the URL.
  378.      *
  379.      * @since 1.5.0
  380.      *
  381.      * @return bool Whether permalink links are enabled and index.php is NOT in the URL.
  382.      */
  383.     public function using_mod_rewrite_permalinks() {
  384.         return $this->using_permalinks() && ! $this->using_index_permalinks();
  385.     }
  386.  
  387.     /**
  388.      * Indexes for matches for usage in preg_*() functions.
  389.      *
  390.      * The format of the string is, with empty matches property value, '$NUM'.
  391.      * The 'NUM' will be replaced with the value in the $number parameter. With
  392.      * the matches property not empty, the value of the returned string will
  393.      * contain that value of the matches property. The format then will be
  394.      * '$MATCHES[NUM]', with MATCHES as the value in the property and NUM the
  395.      * value of the $number parameter.
  396.      *
  397.      * @since 1.5.0
  398.      *
  399.      * @param int $number Index number.
  400.      * @return string
  401.      */
  402.     public function preg_index($number) {
  403.         $match_prefix = '$';
  404.         $match_suffix = '';
  405.  
  406.         if ( ! empty($this->matches) ) {
  407.             $match_prefix = '$' . $this->matches . '[';
  408.             $match_suffix = ']';
  409.         }
  410.  
  411.         return "$match_prefix$number$match_suffix";
  412.     }
  413.  
  414.     /**
  415.      * Retrieves all page and attachments for pages URIs.
  416.      *
  417.      * The attachments are for those that have pages as parents and will be
  418.      * retrieved.
  419.      *
  420.      * @since 2.5.0
  421.      *
  422.      * @global wpdb $wpdb WordPress database abstraction object.
  423.      *
  424.      * @return array Array of page URIs as first element and attachment URIs as second element.
  425.      */
  426.     public function page_uri_index() {
  427.         global $wpdb;
  428.  
  429.         // Get pages in order of hierarchy, i.e. children after parents.
  430.         $pages = $wpdb->get_results("SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'page' AND post_status != 'auto-draft'");
  431.         $posts = get_page_hierarchy( $pages );
  432.  
  433.         // If we have no pages get out quick.
  434.         if ( !$posts )
  435.             return array( array(), array() );
  436.  
  437.         // Now reverse it, because we need parents after children for rewrite rules to work properly.
  438.         $posts = array_reverse($posts, true);
  439.  
  440.         $page_uris = array();
  441.         $page_attachment_uris = array();
  442.  
  443.         foreach ( $posts as $id => $post ) {
  444.             // URL => page name
  445.             $uri = get_page_uri($id);
  446.             $attachments = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent = %d", $id ));
  447.             if ( !empty($attachments) ) {
  448.                 foreach ( $attachments as $attachment ) {
  449.                     $attach_uri = get_page_uri($attachment->ID);
  450.                     $page_attachment_uris[$attach_uri] = $attachment->ID;
  451.                 }
  452.             }
  453.  
  454.             $page_uris[$uri] = $id;
  455.         }
  456.  
  457.         return array( $page_uris, $page_attachment_uris );
  458.     }
  459.  
  460.     /**
  461.      * Retrieves all of the rewrite rules for pages.
  462.      *
  463.      * @since 1.5.0
  464.      *
  465.      * @return array Page rewrite rules.
  466.      */
  467.     public function page_rewrite_rules() {
  468.         // The extra .? at the beginning prevents clashes with other regular expressions in the rules array.
  469.         $this->add_rewrite_tag( '%pagename%', '(.?.+?)', 'pagename=' );
  470.  
  471.         return $this->generate_rewrite_rules( $this->get_page_permastruct(), EP_PAGES, true, true, false, false );
  472.     }
  473.  
  474.     /**
  475.      * Retrieves date permalink structure, with year, month, and day.
  476.      *
  477.      * The permalink structure for the date, if not set already depends on the
  478.      * permalink structure. It can be one of three formats. The first is year,
  479.      * month, day; the second is day, month, year; and the last format is month,
  480.      * day, year. These are matched against the permalink structure for which
  481.      * one is used. If none matches, then the default will be used, which is
  482.      * year, month, day.
  483.      *
  484.      * Prevents post ID and date permalinks from overlapping. In the case of
  485.      * post_id, the date permalink will be prepended with front permalink with
  486.      * 'date/' before the actual permalink to form the complete date permalink
  487.      * structure.
  488.      *
  489.      * @since 1.5.0
  490.      *
  491.      * @return string|false False on no permalink structure. Date permalink structure.
  492.      */
  493.     public function get_date_permastruct() {
  494.         if ( isset($this->date_structure) )
  495.             return $this->date_structure;
  496.  
  497.         if ( empty($this->permalink_structure) ) {
  498.             $this->date_structure = '';
  499.             return false;
  500.         }
  501.  
  502.         // The date permalink must have year, month, and day separated by slashes.
  503.         $endians = array('%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%', '%monthnum%/%day%/%year%');
  504.  
  505.         $this->date_structure = '';
  506.         $date_endian = '';
  507.  
  508.         foreach ( $endians as $endian ) {
  509.             if ( false !== strpos($this->permalink_structure, $endian) ) {
  510.                 $date_endian= $endian;
  511.                 break;
  512.             }
  513.         }
  514.  
  515.         if ( empty($date_endian) )
  516.             $date_endian = '%year%/%monthnum%/%day%';
  517.  
  518.         /*
  519.          * Do not allow the date tags and %post_id% to overlap in the permalink
  520.          * structure. If they do, move the date tags to $front/date/.
  521.          */
  522.         $front = $this->front;
  523.         preg_match_all('/%.+?%/', $this->permalink_structure, $tokens);
  524.         $tok_index = 1;
  525.         foreach ( (array) $tokens[0] as $token) {
  526.             if ( '%post_id%' == $token && ($tok_index <= 3) ) {
  527.                 $front = $front . 'date/';
  528.                 break;
  529.             }
  530.             $tok_index++;
  531.         }
  532.  
  533.         $this->date_structure = $front . $date_endian;
  534.  
  535.         return $this->date_structure;
  536.     }
  537.  
  538.     /**
  539.      * Retrieves the year permalink structure without month and day.
  540.      *
  541.      * Gets the date permalink structure and strips out the month and day
  542.      * permalink structures.
  543.      *
  544.      * @since 1.5.0
  545.      *
  546.      * @return false|string False on failure. Year structure on success.
  547.      */
  548.     public function get_year_permastruct() {
  549.         $structure = $this->get_date_permastruct();
  550.  
  551.         if ( empty($structure) )
  552.             return false;
  553.  
  554.         $structure = str_replace('%monthnum%', '', $structure);
  555.         $structure = str_replace('%day%', '', $structure);
  556.         $structure = preg_replace('#/+#', '/', $structure);
  557.  
  558.         return $structure;
  559.     }
  560.  
  561.     /**
  562.      * Retrieves the month permalink structure without day and with year.
  563.      *
  564.      * Gets the date permalink structure and strips out the day permalink
  565.      * structures. Keeps the year permalink structure.
  566.      *
  567.      * @since 1.5.0
  568.      *
  569.      * @return false|string False on failure. Year/Month structure on success.
  570.      */
  571.     public function get_month_permastruct() {
  572.         $structure = $this->get_date_permastruct();
  573.  
  574.         if ( empty($structure) )
  575.             return false;
  576.  
  577.         $structure = str_replace('%day%', '', $structure);
  578.         $structure = preg_replace('#/+#', '/', $structure);
  579.  
  580.         return $structure;
  581.     }
  582.  
  583.     /**
  584.      * Retrieves the day permalink structure with month and year.
  585.      *
  586.      * Keeps date permalink structure with all year, month, and day.
  587.      *
  588.      * @since 1.5.0
  589.      *
  590.      * @return string|false False on failure. Year/Month/Day structure on success.
  591.      */
  592.     public function get_day_permastruct() {
  593.         return $this->get_date_permastruct();
  594.     }
  595.  
  596.     /**
  597.      * Retrieves the permalink structure for categories.
  598.      *
  599.      * If the category_base property has no value, then the category structure
  600.      * will have the front property value, followed by 'category', and finally
  601.      * '%category%'. If it does, then the root property will be used, along with
  602.      * the category_base property value.
  603.      *
  604.      * @since 1.5.0
  605.      *
  606.      * @return string|false False on failure. Category permalink structure.
  607.      */
  608.     public function get_category_permastruct() {
  609.         return $this->get_extra_permastruct('category');
  610.     }
  611.  
  612.     /**
  613.      * Retrieve the permalink structure for tags.
  614.      *
  615.      * If the tag_base property has no value, then the tag structure will have
  616.      * the front property value, followed by 'tag', and finally '%tag%'. If it
  617.      * does, then the root property will be used, along with the tag_base
  618.      * property value.
  619.      *
  620.      * @since 2.3.0
  621.      *
  622.      * @return string|false False on failure. Tag permalink structure.
  623.      */
  624.     public function get_tag_permastruct() {
  625.         return $this->get_extra_permastruct('post_tag');
  626.     }
  627.  
  628.     /**
  629.      * Retrieves an extra permalink structure by name.
  630.      *
  631.      * @since 2.5.0
  632.      *
  633.      * @param string $name Permalink structure name.
  634.      * @return string|false False if not found. Permalink structure string.
  635.      */
  636.     public function get_extra_permastruct($name) {
  637.         if ( empty($this->permalink_structure) )
  638.             return false;
  639.  
  640.         if ( isset($this->extra_permastructs[$name]) )
  641.             return $this->extra_permastructs[$name]['struct'];
  642.  
  643.         return false;
  644.     }
  645.  
  646.     /**
  647.      * Retrieves the author permalink structure.
  648.      *
  649.      * The permalink structure is front property, author base, and finally
  650.      * '/%author%'. Will set the author_structure property and then return it
  651.      * without attempting to set the value again.
  652.      *
  653.      * @since 1.5.0
  654.      *
  655.      * @return string|false False if not found. Permalink structure string.
  656.      */
  657.     public function get_author_permastruct() {
  658.         if ( isset($this->author_structure) )
  659.             return $this->author_structure;
  660.  
  661.         if ( empty($this->permalink_structure) ) {
  662.             $this->author_structure = '';
  663.             return false;
  664.         }
  665.  
  666.         $this->author_structure = $this->front . $this->author_base . '/%author%';
  667.  
  668.         return $this->author_structure;
  669.     }
  670.  
  671.     /**
  672.      * Retrieves the search permalink structure.
  673.      *
  674.      * The permalink structure is root property, search base, and finally
  675.      * '/%search%'. Will set the search_structure property and then return it
  676.      * without attempting to set the value again.
  677.      *
  678.      * @since 1.5.0
  679.      *
  680.      * @return string|false False if not found. Permalink structure string.
  681.      */
  682.     public function get_search_permastruct() {
  683.         if ( isset($this->search_structure) )
  684.             return $this->search_structure;
  685.  
  686.         if ( empty($this->permalink_structure) ) {
  687.             $this->search_structure = '';
  688.             return false;
  689.         }
  690.  
  691.         $this->search_structure = $this->root . $this->search_base . '/%search%';
  692.  
  693.         return $this->search_structure;
  694.     }
  695.  
  696.     /**
  697.      * Retrieves the page permalink structure.
  698.      *
  699.      * The permalink structure is root property, and '%pagename%'. Will set the
  700.      * page_structure property and then return it without attempting to set the
  701.      * value again.
  702.      *
  703.      * @since 1.5.0
  704.      *
  705.      * @return string|false False if not found. Permalink structure string.
  706.      */
  707.     public function get_page_permastruct() {
  708.         if ( isset($this->page_structure) )
  709.             return $this->page_structure;
  710.  
  711.         if (empty($this->permalink_structure)) {
  712.             $this->page_structure = '';
  713.             return false;
  714.         }
  715.  
  716.         $this->page_structure = $this->root . '%pagename%';
  717.  
  718.         return $this->page_structure;
  719.     }
  720.  
  721.     /**
  722.      * Retrieves the feed permalink structure.
  723.      *
  724.      * The permalink structure is root property, feed base, and finally
  725.      * '/%feed%'. Will set the feed_structure property and then return it
  726.      * without attempting to set the value again.
  727.      *
  728.      * @since 1.5.0
  729.      *
  730.      * @return string|false False if not found. Permalink structure string.
  731.      */
  732.     public function get_feed_permastruct() {
  733.         if ( isset($this->feed_structure) )
  734.             return $this->feed_structure;
  735.  
  736.         if ( empty($this->permalink_structure) ) {
  737.             $this->feed_structure = '';
  738.             return false;
  739.         }
  740.  
  741.         $this->feed_structure = $this->root . $this->feed_base . '/%feed%';
  742.  
  743.         return $this->feed_structure;
  744.     }
  745.  
  746.     /**
  747.      * Retrieves the comment feed permalink structure.
  748.      *
  749.      * The permalink structure is root property, comment base property, feed
  750.      * base and finally '/%feed%'. Will set the comment_feed_structure property
  751.      * and then return it without attempting to set the value again.
  752.      *
  753.      * @since 1.5.0
  754.      *
  755.      * @return string|false False if not found. Permalink structure string.
  756.      */
  757.     public function get_comment_feed_permastruct() {
  758.         if ( isset($this->comment_feed_structure) )
  759.             return $this->comment_feed_structure;
  760.  
  761.         if (empty($this->permalink_structure)) {
  762.             $this->comment_feed_structure = '';
  763.             return false;
  764.         }
  765.  
  766.         $this->comment_feed_structure = $this->root . $this->comments_base . '/' . $this->feed_base . '/%feed%';
  767.  
  768.         return $this->comment_feed_structure;
  769.     }
  770.  
  771.     /**
  772.      * Adds or updates existing rewrite tags (e.g. %postname%).
  773.      *
  774.      * If the tag already exists, replace the existing pattern and query for
  775.      * that tag, otherwise add the new tag.
  776.      *
  777.      * @since 1.5.0
  778.      *
  779.      * @see WP_Rewrite::$rewritecode
  780.      * @see WP_Rewrite::$rewritereplace
  781.      * @see WP_Rewrite::$queryreplace
  782.      *
  783.      * @param string $tag   Name of the rewrite tag to add or update.
  784.      * @param string $regex Regular expression to substitute the tag for in rewrite rules.
  785.      * @param string $query String to append to the rewritten query. Must end in '='.
  786.      */
  787.     public function add_rewrite_tag( $tag, $regex, $query ) {
  788.         $position = array_search( $tag, $this->rewritecode );
  789.         if ( false !== $position && null !== $position ) {
  790.             $this->rewritereplace[ $position ] = $regex;
  791.             $this->queryreplace[ $position ] = $query;
  792.         } else {
  793.             $this->rewritecode[] = $tag;
  794.             $this->rewritereplace[] = $regex;
  795.             $this->queryreplace[] = $query;
  796.         }
  797.     }
  798.  
  799.  
  800.     /**
  801.      * Removes an existing rewrite tag.
  802.      *
  803.      * @since 4.5.0
  804.      *
  805.      * @see WP_Rewrite::$rewritecode
  806.      * @see WP_Rewrite::$rewritereplace
  807.      * @see WP_Rewrite::$queryreplace
  808.      *
  809.      * @param string $tag Name of the rewrite tag to remove.
  810.      */
  811.     public function remove_rewrite_tag( $tag ) {
  812.         $position = array_search( $tag, $this->rewritecode );
  813.         if ( false !== $position && null !== $position ) {
  814.             unset( $this->rewritecode[ $position ] );
  815.             unset( $this->rewritereplace[ $position ] );
  816.             unset( $this->queryreplace[ $position ] );
  817.         }
  818.     }
  819.  
  820.     /**
  821.      * Generates rewrite rules from a permalink structure.
  822.      *
  823.      * The main WP_Rewrite function for building the rewrite rule list. The
  824.      * contents of the function is a mix of black magic and regular expressions,
  825.      * so best just ignore the contents and move to the parameters.
  826.      *
  827.      * @since 1.5.0
  828.      *
  829.      * @param string $permalink_structure The permalink structure.
  830.      * @param int    $ep_mask             Optional. Endpoint mask defining what endpoints are added to the structure.
  831.      *                                    Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
  832.      *                                    `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
  833.      *                                    `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
  834.      *                                    Default `EP_NONE`.
  835.      * @param bool   $paged               Optional. Whether archive pagination rules should be added for the structure.
  836.      *                                    Default true.
  837.      * @param bool   $feed                Optional Whether feed rewrite rules should be added for the structure.
  838.      *                                    Default true.
  839.      * @param bool   $forcomments         Optional. Whether the feed rules should be a query for a comments feed.
  840.      *                                    Default false.
  841.      * @param bool   $walk_dirs           Optional. Whether the 'directories' making up the structure should be walked
  842.      *                                    over and rewrite rules built for each in-turn. Default true.
  843.      * @param bool   $endpoints           Optional. Whether endpoints should be applied to the generated rewrite rules.
  844.      *                                    Default true.
  845.      * @return array Rewrite rule list.
  846.      */
  847.     public function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) {
  848.         // Build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
  849.         $feedregex2 = '';
  850.         foreach ( (array) $this->feeds as $feed_name)
  851.             $feedregex2 .= $feed_name . '|';
  852.         $feedregex2 = '(' . trim($feedregex2, '|') . ')/?$';
  853.  
  854.         /*
  855.          * $feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
  856.          * and <permalink>/atom are both possible
  857.          */
  858.         $feedregex = $this->feed_base . '/' . $feedregex2;
  859.  
  860.         // Build a regex to match the trackback and page/xx parts of URLs.
  861.         $trackbackregex = 'trackback/?$';
  862.         $pageregex = $this->pagination_base . '/?([0-9]{1,})/?$';
  863.         $commentregex = $this->comments_pagination_base . '-([0-9]{1,})/?$';
  864.         $embedregex = 'embed/?$';
  865.  
  866.         // Build up an array of endpoint regexes to append => queries to append.
  867.         if ( $endpoints ) {
  868.             $ep_query_append = array ();
  869.             foreach ( (array) $this->endpoints as $endpoint) {
  870.                 // Match everything after the endpoint name, but allow for nothing to appear there.
  871.                 $epmatch = $endpoint[1] . '(/(.*))?/?$';
  872.  
  873.                 // This will be appended on to the rest of the query for each dir.
  874.                 $epquery = '&' . $endpoint[2] . '=';
  875.                 $ep_query_append[$epmatch] = array ( $endpoint[0], $epquery );
  876.             }
  877.         }
  878.  
  879.         // Get everything up to the first rewrite tag.
  880.         $front = substr($permalink_structure, 0, strpos($permalink_structure, '%'));
  881.  
  882.         // Build an array of the tags (note that said array ends up being in $tokens[0]).
  883.         preg_match_all('/%.+?%/', $permalink_structure, $tokens);
  884.  
  885.         $num_tokens = count($tokens[0]);
  886.  
  887.         $index = $this->index; //probably 'index.php'
  888.         $feedindex = $index;
  889.         $trackbackindex = $index;
  890.         $embedindex = $index;
  891.  
  892.         /*
  893.          * Build a list from the rewritecode and queryreplace arrays, that will look something
  894.          * like tagname=$matches[i] where i is the current $i.
  895.          */
  896.         $queries = array();
  897.         for ( $i = 0; $i < $num_tokens; ++$i ) {
  898.             if ( 0 < $i )
  899.                 $queries[$i] = $queries[$i - 1] . '&';
  900.             else
  901.                 $queries[$i] = '';
  902.  
  903.             $query_token = str_replace($this->rewritecode, $this->queryreplace, $tokens[0][$i]) . $this->preg_index($i+1);
  904.             $queries[$i] .= $query_token;
  905.         }
  906.  
  907.         // Get the structure, minus any cruft (stuff that isn't tags) at the front.
  908.         $structure = $permalink_structure;
  909.         if ( $front != '/' )
  910.             $structure = str_replace($front, '', $structure);
  911.  
  912.         /*
  913.          * Create a list of dirs to walk over, making rewrite rules for each level
  914.          * so for example, a $structure of /%year%/%monthnum%/%postname% would create
  915.          * rewrite rules for /%year%/, /%year%/%monthnum%/ and /%year%/%monthnum%/%postname%
  916.          */
  917.         $structure = trim($structure, '/');
  918.         $dirs = $walk_dirs ? explode('/', $structure) : array( $structure );
  919.         $num_dirs = count($dirs);
  920.  
  921.         // Strip slashes from the front of $front.
  922.         $front = preg_replace('|^/+|', '', $front);
  923.  
  924.         // The main workhorse loop.
  925.         $post_rewrite = array();
  926.         $struct = $front;
  927.         for ( $j = 0; $j < $num_dirs; ++$j ) {
  928.             // Get the struct for this dir, and trim slashes off the front.
  929.             $struct .= $dirs[$j] . '/'; // Accumulate. see comment near explode('/', $structure) above.
  930.             $struct = ltrim($struct, '/');
  931.  
  932.             // Replace tags with regexes.
  933.             $match = str_replace($this->rewritecode, $this->rewritereplace, $struct);
  934.  
  935.             // Make a list of tags, and store how many there are in $num_toks.
  936.             $num_toks = preg_match_all('/%.+?%/', $struct, $toks);
  937.  
  938.             // Get the 'tagname=$matches[i]'.
  939.             $query = ( ! empty( $num_toks ) && isset( $queries[$num_toks - 1] ) ) ? $queries[$num_toks - 1] : '';
  940.  
  941.             // Set up $ep_mask_specific which is used to match more specific URL types.
  942.             switch ( $dirs[$j] ) {
  943.                 case '%year%':
  944.                     $ep_mask_specific = EP_YEAR;
  945.                     break;
  946.                 case '%monthnum%':
  947.                     $ep_mask_specific = EP_MONTH;
  948.                     break;
  949.                 case '%day%':
  950.                     $ep_mask_specific = EP_DAY;
  951.                     break;
  952.                 default:
  953.                     $ep_mask_specific = EP_NONE;
  954.             }
  955.  
  956.             // Create query for /page/xx.
  957.             $pagematch = $match . $pageregex;
  958.             $pagequery = $index . '?' . $query . '&paged=' . $this->preg_index($num_toks + 1);
  959.  
  960.             // Create query for /comment-page-xx.
  961.             $commentmatch = $match . $commentregex;
  962.             $commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index($num_toks + 1);
  963.  
  964.             if ( get_option('page_on_front') ) {
  965.                 // Create query for Root /comment-page-xx.
  966.                 $rootcommentmatch = $match . $commentregex;
  967.                 $rootcommentquery = $index . '?' . $query . '&page_id=' . get_option('page_on_front') . '&cpage=' . $this->preg_index($num_toks + 1);
  968.             }
  969.  
  970.             // Create query for /feed/(feed|atom|rss|rss2|rdf).
  971.             $feedmatch = $match . $feedregex;
  972.             $feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
  973.  
  974.             // Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
  975.             $feedmatch2 = $match . $feedregex2;
  976.             $feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
  977.  
  978.             // Create query and regex for embeds.
  979.             $embedmatch = $match . $embedregex;
  980.             $embedquery = $embedindex . '?' . $query . '&embed=true';
  981.  
  982.             // If asked to, turn the feed queries into comment feed ones.
  983.             if ( $forcomments ) {
  984.                 $feedquery .= '&withcomments=1';
  985.                 $feedquery2 .= '&withcomments=1';
  986.             }
  987.  
  988.             // Start creating the array of rewrites for this dir.
  989.             $rewrite = array();
  990.  
  991.             // ...adding on /feed/ regexes => queries
  992.             if ( $feed ) {
  993.                 $rewrite = array( $feedmatch => $feedquery, $feedmatch2 => $feedquery2, $embedmatch => $embedquery );
  994.             }
  995.  
  996.             //...and /page/xx ones
  997.             if ( $paged ) {
  998.                 $rewrite = array_merge( $rewrite, array( $pagematch => $pagequery ) );
  999.             }
  1000.  
  1001.             // Only on pages with comments add ../comment-page-xx/.
  1002.             if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask ) {
  1003.                 $rewrite = array_merge($rewrite, array($commentmatch => $commentquery));
  1004.             } elseif ( EP_ROOT & $ep_mask && get_option('page_on_front') ) {
  1005.                 $rewrite = array_merge($rewrite, array($rootcommentmatch => $rootcommentquery));
  1006.             }
  1007.  
  1008.             // Do endpoints.
  1009.             if ( $endpoints ) {
  1010.                 foreach ( (array) $ep_query_append as $regex => $ep) {
  1011.                     // Add the endpoints on if the mask fits.
  1012.                     if ( $ep[0] & $ep_mask || $ep[0] & $ep_mask_specific )
  1013.                         $rewrite[$match . $regex] = $index . '?' . $query . $ep[1] . $this->preg_index($num_toks + 2);
  1014.                 }
  1015.             }
  1016.  
  1017.             // If we've got some tags in this dir.
  1018.             if ( $num_toks ) {
  1019.                 $post = false;
  1020.                 $page = false;
  1021.  
  1022.                 /*
  1023.                  * Check to see if this dir is permalink-level: i.e. the structure specifies an
  1024.                  * individual post. Do this by checking it contains at least one of 1) post name,
  1025.                  * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
  1026.                  * minute all present). Set these flags now as we need them for the endpoints.
  1027.                  */
  1028.                 if ( strpos($struct, '%postname%') !== false
  1029.                         || strpos($struct, '%post_id%') !== false
  1030.                         || strpos($struct, '%pagename%') !== false
  1031.                         || (strpos($struct, '%year%') !== false && strpos($struct, '%monthnum%') !== false && strpos($struct, '%day%') !== false && strpos($struct, '%hour%') !== false && strpos($struct, '%minute%') !== false && strpos($struct, '%second%') !== false)
  1032.                         ) {
  1033.                     $post = true;
  1034.                     if ( strpos($struct, '%pagename%') !== false )
  1035.                         $page = true;
  1036.                 }
  1037.  
  1038.                 if ( ! $post ) {
  1039.                     // For custom post types, we need to add on endpoints as well.
  1040.                     foreach ( get_post_types( array('_builtin' => false ) ) as $ptype ) {
  1041.                         if ( strpos($struct, "%$ptype%") !== false ) {
  1042.                             $post = true;
  1043.  
  1044.                             // This is for page style attachment URLs.
  1045.                             $page = is_post_type_hierarchical( $ptype );
  1046.                             break;
  1047.                         }
  1048.                     }
  1049.                 }
  1050.  
  1051.                 // If creating rules for a permalink, do all the endpoints like attachments etc.
  1052.                 if ( $post ) {
  1053.                     // Create query and regex for trackback.
  1054.                     $trackbackmatch = $match . $trackbackregex;
  1055.                     $trackbackquery = $trackbackindex . '?' . $query . '&tb=1';
  1056.  
  1057.                     // Create query and regex for embeds.
  1058.                     $embedmatch = $match . $embedregex;
  1059.                     $embedquery = $embedindex . '?' . $query . '&embed=true';
  1060.  
  1061.                     // Trim slashes from the end of the regex for this dir.
  1062.                     $match = rtrim($match, '/');
  1063.  
  1064.                     // Get rid of brackets.
  1065.                     $submatchbase = str_replace( array('(', ')'), '', $match);
  1066.  
  1067.                     // Add a rule for at attachments, which take the form of <permalink>/some-text.
  1068.                     $sub1 = $submatchbase . '/([^/]+)/';
  1069.  
  1070.                     // Add trackback regex <permalink>/trackback/...
  1071.                     $sub1tb = $sub1 . $trackbackregex;
  1072.  
  1073.                     // And <permalink>/feed/(atom|...)
  1074.                     $sub1feed = $sub1 . $feedregex;
  1075.  
  1076.                     // And <permalink>/(feed|atom...)
  1077.                     $sub1feed2 = $sub1 . $feedregex2;
  1078.  
  1079.                     // And <permalink>/comment-page-xx
  1080.                     $sub1comment = $sub1 . $commentregex;
  1081.  
  1082.                     // And <permalink>/embed/...
  1083.                     $sub1embed = $sub1 . $embedregex;
  1084.  
  1085.                     /*
  1086.                      * Add another rule to match attachments in the explicit form:
  1087.                      * <permalink>/attachment/some-text
  1088.                      */
  1089.                     $sub2 = $submatchbase . '/attachment/([^/]+)/';
  1090.  
  1091.                     // And add trackbacks <permalink>/attachment/trackback.
  1092.                     $sub2tb = $sub2 . $trackbackregex;
  1093.  
  1094.                     // Feeds, <permalink>/attachment/feed/(atom|...)
  1095.                     $sub2feed = $sub2 . $feedregex;
  1096.  
  1097.                     // And feeds again on to this <permalink>/attachment/(feed|atom...)
  1098.                     $sub2feed2 = $sub2 . $feedregex2;
  1099.  
  1100.                     // And <permalink>/comment-page-xx
  1101.                     $sub2comment = $sub2 . $commentregex;
  1102.  
  1103.                     // And <permalink>/embed/...
  1104.                     $sub2embed = $sub2 . $embedregex;
  1105.  
  1106.                     // Create queries for these extra tag-ons we've just dealt with.
  1107.                     $subquery = $index . '?attachment=' . $this->preg_index(1);
  1108.                     $subtbquery = $subquery . '&tb=1';
  1109.                     $subfeedquery = $subquery . '&feed=' . $this->preg_index(2);
  1110.                     $subcommentquery = $subquery . '&cpage=' . $this->preg_index(2);
  1111.                     $subembedquery = $subquery . '&embed=true';
  1112.  
  1113.                     // Do endpoints for attachments.
  1114.                     if ( !empty($endpoints) ) {
  1115.                         foreach ( (array) $ep_query_append as $regex => $ep ) {
  1116.                             if ( $ep[0] & EP_ATTACHMENT ) {
  1117.                                 $rewrite[$sub1 . $regex] = $subquery . $ep[1] . $this->preg_index(3);
  1118.                                 $rewrite[$sub2 . $regex] = $subquery . $ep[1] . $this->preg_index(3);
  1119.                             }
  1120.                         }
  1121.                     }
  1122.  
  1123.                     /*
  1124.                      * Now we've finished with endpoints, finish off the $sub1 and $sub2 matches
  1125.                      * add a ? as we don't have to match that last slash, and finally a $ so we
  1126.                      * match to the end of the URL
  1127.                      */
  1128.                     $sub1 .= '?$';
  1129.                     $sub2 .= '?$';
  1130.  
  1131.                     /*
  1132.                      * Post pagination, e.g. <permalink>/2/
  1133.                      * Previously: '(/[0-9]+)?/?$', which produced '/2' for page.
  1134.                      * When cast to int, returned 0.
  1135.                      */
  1136.                     $match = $match . '(?:/([0-9]+))?/?$';
  1137.                     $query = $index . '?' . $query . '&page=' . $this->preg_index($num_toks + 1);
  1138.  
  1139.                 // Not matching a permalink so this is a lot simpler.
  1140.                 } else {
  1141.                     // Close the match and finalise the query.
  1142.                     $match .= '?$';
  1143.                     $query = $index . '?' . $query;
  1144.                 }
  1145.  
  1146.                 /*
  1147.                  * Create the final array for this dir by joining the $rewrite array (which currently
  1148.                  * only contains rules/queries for trackback, pages etc) to the main regex/query for
  1149.                  * this dir
  1150.                  */
  1151.                 $rewrite = array_merge($rewrite, array($match => $query));
  1152.  
  1153.                 // If we're matching a permalink, add those extras (attachments etc) on.
  1154.                 if ( $post ) {
  1155.                     // Add trackback.
  1156.                     $rewrite = array_merge(array($trackbackmatch => $trackbackquery), $rewrite);
  1157.  
  1158.                     // Add embed.
  1159.                     $rewrite = array_merge( array( $embedmatch => $embedquery ), $rewrite );
  1160.  
  1161.                     // Add regexes/queries for attachments, attachment trackbacks and so on.
  1162.                     if ( ! $page ) {
  1163.                         // Require <permalink>/attachment/stuff form for pages because of confusion with subpages.
  1164.                         $rewrite = array_merge( $rewrite, array(
  1165.                             $sub1        => $subquery,
  1166.                             $sub1tb      => $subtbquery,
  1167.                             $sub1feed    => $subfeedquery,
  1168.                             $sub1feed2   => $subfeedquery,
  1169.                             $sub1comment => $subcommentquery,
  1170.                             $sub1embed   => $subembedquery
  1171.                         ) );
  1172.                     }
  1173.  
  1174.                     $rewrite = array_merge( array( $sub2 => $subquery, $sub2tb => $subtbquery, $sub2feed => $subfeedquery, $sub2feed2 => $subfeedquery, $sub2comment => $subcommentquery, $sub2embed => $subembedquery ), $rewrite );
  1175.                 }
  1176.             }
  1177.             // Add the rules for this dir to the accumulating $post_rewrite.
  1178.             $post_rewrite = array_merge($rewrite, $post_rewrite);
  1179.         }
  1180.  
  1181.         // The finished rules. phew!
  1182.         return $post_rewrite;
  1183.     }
  1184.  
  1185.     /**
  1186.      * Generates rewrite rules with permalink structure and walking directory only.
  1187.      *
  1188.      * Shorten version of WP_Rewrite::generate_rewrite_rules() that allows for shorter
  1189.      * list of parameters. See the method for longer description of what generating
  1190.      * rewrite rules does.
  1191.      *
  1192.      * @since 1.5.0
  1193.      *
  1194.      * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters.
  1195.      *
  1196.      * @param string $permalink_structure The permalink structure to generate rules.
  1197.      * @param bool   $walk_dirs           Optional, default is false. Whether to create list of directories to walk over.
  1198.      * @return array
  1199.      */
  1200.     public function generate_rewrite_rule($permalink_structure, $walk_dirs = false) {
  1201.         return $this->generate_rewrite_rules($permalink_structure, EP_NONE, false, false, false, $walk_dirs);
  1202.     }
  1203.  
  1204.     /**
  1205.      * Constructs rewrite matches and queries from permalink structure.
  1206.      *
  1207.      * Runs the action {@see 'generate_rewrite_rules'} with the parameter that is an
  1208.      * reference to the current WP_Rewrite instance to further manipulate the
  1209.      * permalink structures and rewrite rules. Runs the {@see 'rewrite_rules_array'}
  1210.      * filter on the full rewrite rule array.
  1211.      *
  1212.      * There are two ways to manipulate the rewrite rules, one by hooking into
  1213.      * the {@see 'generate_rewrite_rules'} action and gaining full control of the
  1214.      * object or just manipulating the rewrite rule array before it is passed
  1215.      * from the function.
  1216.      *
  1217.      * @since 1.5.0
  1218.      *
  1219.      * @return array An associate array of matches and queries.
  1220.      */
  1221.     public function rewrite_rules() {
  1222.         $rewrite = array();
  1223.  
  1224.         if ( empty($this->permalink_structure) )
  1225.             return $rewrite;
  1226.  
  1227.         // robots.txt -only if installed at the root
  1228.         $home_path = parse_url( home_url() );
  1229.         $robots_rewrite = ( empty( $home_path['path'] ) || '/' == $home_path['path'] ) ? array( 'robots\.txt$' => $this->index . '?robots=1' ) : array();
  1230.  
  1231.         // Old feed and service files.
  1232.         $deprecated_files = array(
  1233.             '.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$' => $this->index . '?feed=old',
  1234.             '.*wp-app\.php(/.*)?$' => $this->index . '?error=403',
  1235.         );
  1236.  
  1237.         // Registration rules.
  1238.         $registration_pages = array();
  1239.         if ( is_multisite() && is_main_site() ) {
  1240.             $registration_pages['.*wp-signup.php$'] = $this->index . '?signup=true';
  1241.             $registration_pages['.*wp-activate.php$'] = $this->index . '?activate=true';
  1242.         }
  1243.  
  1244.         // Deprecated.
  1245.         $registration_pages['.*wp-register.php$'] = $this->index . '?register=true';
  1246.  
  1247.         // Post rewrite rules.
  1248.         $post_rewrite = $this->generate_rewrite_rules( $this->permalink_structure, EP_PERMALINK );
  1249.  
  1250.         /**
  1251.          * Filters rewrite rules used for "post" archives.
  1252.          *
  1253.          * @since 1.5.0
  1254.          *
  1255.          * @param array $post_rewrite The rewrite rules for posts.
  1256.          */
  1257.         $post_rewrite = apply_filters( 'post_rewrite_rules', $post_rewrite );
  1258.  
  1259.         // Date rewrite rules.
  1260.         $date_rewrite = $this->generate_rewrite_rules($this->get_date_permastruct(), EP_DATE);
  1261.  
  1262.         /**
  1263.          * Filters rewrite rules used for date archives.
  1264.          *
  1265.          * Likely date archives would include /yyyy/, /yyyy/mm/, and /yyyy/mm/dd/.
  1266.          *
  1267.          * @since 1.5.0
  1268.          *
  1269.          * @param array $date_rewrite The rewrite rules for date archives.
  1270.          */
  1271.         $date_rewrite = apply_filters( 'date_rewrite_rules', $date_rewrite );
  1272.  
  1273.         // Root-level rewrite rules.
  1274.         $root_rewrite = $this->generate_rewrite_rules($this->root . '/', EP_ROOT);
  1275.  
  1276.         /**
  1277.          * Filters rewrite rules used for root-level archives.
  1278.          *
  1279.          * Likely root-level archives would include pagination rules for the homepage
  1280.          * as well as site-wide post feeds (e.g. /feed/, and /feed/atom/).
  1281.          *
  1282.          * @since 1.5.0
  1283.          *
  1284.          * @param array $root_rewrite The root-level rewrite rules.
  1285.          */
  1286.         $root_rewrite = apply_filters( 'root_rewrite_rules', $root_rewrite );
  1287.  
  1288.         // Comments rewrite rules.
  1289.         $comments_rewrite = $this->generate_rewrite_rules($this->root . $this->comments_base, EP_COMMENTS, false, true, true, false);
  1290.  
  1291.         /**
  1292.          * Filters rewrite rules used for comment feed archives.
  1293.          *
  1294.          * Likely comments feed archives include /comments/feed/, and /comments/feed/atom/.
  1295.          *
  1296.          * @since 1.5.0
  1297.          *
  1298.          * @param array $comments_rewrite The rewrite rules for the site-wide comments feeds.
  1299.          */
  1300.         $comments_rewrite = apply_filters( 'comments_rewrite_rules', $comments_rewrite );
  1301.  
  1302.         // Search rewrite rules.
  1303.         $search_structure = $this->get_search_permastruct();
  1304.         $search_rewrite = $this->generate_rewrite_rules($search_structure, EP_SEARCH);
  1305.  
  1306.         /**
  1307.          * Filters rewrite rules used for search archives.
  1308.          *
  1309.          * Likely search-related archives include /search/search+query/ as well as
  1310.          * pagination and feed paths for a search.
  1311.          *
  1312.          * @since 1.5.0
  1313.          *
  1314.          * @param array $search_rewrite The rewrite rules for search queries.
  1315.          */
  1316.         $search_rewrite = apply_filters( 'search_rewrite_rules', $search_rewrite );
  1317.  
  1318.         // Author rewrite rules.
  1319.         $author_rewrite = $this->generate_rewrite_rules($this->get_author_permastruct(), EP_AUTHORS);
  1320.  
  1321.         /**
  1322.          * Filters rewrite rules used for author archives.
  1323.          *
  1324.          * Likely author archives would include /author/author-name/, as well as
  1325.          * pagination and feed paths for author archives.
  1326.          *
  1327.          * @since 1.5.0
  1328.          *
  1329.          * @param array $author_rewrite The rewrite rules for author archives.
  1330.          */
  1331.         $author_rewrite = apply_filters( 'author_rewrite_rules', $author_rewrite );
  1332.  
  1333.         // Pages rewrite rules.
  1334.         $page_rewrite = $this->page_rewrite_rules();
  1335.  
  1336.         /**
  1337.          * Filters rewrite rules used for "page" post type archives.
  1338.          *
  1339.          * @since 1.5.0
  1340.          *
  1341.          * @param array $page_rewrite The rewrite rules for the "page" post type.
  1342.          */
  1343.         $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite );
  1344.  
  1345.         // Extra permastructs.
  1346.         foreach ( $this->extra_permastructs as $permastructname => $struct ) {
  1347.             if ( is_array( $struct ) ) {
  1348.                 if ( count( $struct ) == 2 )
  1349.                     $rules = $this->generate_rewrite_rules( $struct[0], $struct[1] );
  1350.                 else
  1351.                     $rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
  1352.             } else {
  1353.                 $rules = $this->generate_rewrite_rules( $struct );
  1354.             }
  1355.  
  1356.             /**
  1357.              * Filters rewrite rules used for individual permastructs.
  1358.              *
  1359.              * The dynamic portion of the hook name, `$permastructname`, refers
  1360.              * to the name of the registered permastruct, e.g. 'post_tag' (tags),
  1361.              * 'category' (categories), etc.
  1362.              *
  1363.              * @since 3.1.0
  1364.              *
  1365.              * @param array $rules The rewrite rules generated for the current permastruct.
  1366.              */
  1367.             $rules = apply_filters( "{$permastructname}_rewrite_rules", $rules );
  1368.             if ( 'post_tag' == $permastructname ) {
  1369.  
  1370.                 /**
  1371.                  * Filters rewrite rules used specifically for Tags.
  1372.                  *
  1373.                  * @since 2.3.0
  1374.                  * @deprecated 3.1.0 Use 'post_tag_rewrite_rules' instead
  1375.                  *
  1376.                  * @param array $rules The rewrite rules generated for tags.
  1377.                  */
  1378.                 $rules = apply_filters( 'tag_rewrite_rules', $rules );
  1379.             }
  1380.  
  1381.             $this->extra_rules_top = array_merge($this->extra_rules_top, $rules);
  1382.         }
  1383.  
  1384.         // Put them together.
  1385.         if ( $this->use_verbose_page_rules )
  1386.             $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);
  1387.         else
  1388.             $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);
  1389.  
  1390.         /**
  1391.          * Fires after the rewrite rules are generated.
  1392.          *
  1393.          * @since 1.5.0
  1394.          *
  1395.          * @param WP_Rewrite $this Current WP_Rewrite instance (passed by reference).
  1396.          */
  1397.         do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );
  1398.  
  1399.         /**
  1400.          * Filters the full set of generated rewrite rules.
  1401.          *
  1402.          * @since 1.5.0
  1403.          *
  1404.          * @param array $this->rules The compiled array of rewrite rules.
  1405.          */
  1406.         $this->rules = apply_filters( 'rewrite_rules_array', $this->rules );
  1407.  
  1408.         return $this->rules;
  1409.     }
  1410.  
  1411.     /**
  1412.      * Retrieves the rewrite rules.
  1413.      *
  1414.      * The difference between this method and WP_Rewrite::rewrite_rules() is that
  1415.      * this method stores the rewrite rules in the 'rewrite_rules' option and retrieves
  1416.      * it. This prevents having to process all of the permalinks to get the rewrite rules
  1417.      * in the form of caching.
  1418.      *
  1419.      * @since 1.5.0
  1420.      *
  1421.      * @return array Rewrite rules.
  1422.      */
  1423.     public function wp_rewrite_rules() {
  1424.         $this->rules = get_option('rewrite_rules');
  1425.         if ( empty($this->rules) ) {
  1426.             $this->matches = 'matches';
  1427.             $this->rewrite_rules();
  1428.             if ( ! did_action( 'wp_loaded' ) ) {
  1429.                 add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
  1430.                 return $this->rules;
  1431.             }
  1432.             update_option('rewrite_rules', $this->rules);
  1433.         }
  1434.  
  1435.         return $this->rules;
  1436.     }
  1437.  
  1438.     /**
  1439.      * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess.
  1440.      *
  1441.      * Does not actually write to the .htaccess file, but creates the rules for
  1442.      * the process that will.
  1443.      *
  1444.      * Will add the non_wp_rules property rules to the .htaccess file before
  1445.      * the WordPress rewrite rules one.
  1446.      *
  1447.      * @since 1.5.0
  1448.      *
  1449.      * @return string
  1450.      */
  1451.     public function mod_rewrite_rules() {
  1452.         if ( ! $this->using_permalinks() )
  1453.             return '';
  1454.  
  1455.         $site_root = parse_url( site_url() );
  1456.         if ( isset( $site_root['path'] ) )
  1457.             $site_root = trailingslashit($site_root['path']);
  1458.  
  1459.         $home_root = parse_url(home_url());
  1460.         if ( isset( $home_root['path'] ) )
  1461.             $home_root = trailingslashit($home_root['path']);
  1462.         else
  1463.             $home_root = '/';
  1464.  
  1465.         $rules = "<IfModule mod_rewrite.c>\n";
  1466.         $rules .= "RewriteEngine On\n";
  1467.         $rules .= "RewriteBase $home_root\n";
  1468.  
  1469.         // Prevent -f checks on index.php.
  1470.         $rules .= "RewriteRule ^index\.php$ - [L]\n";
  1471.  
  1472.         // Add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all).
  1473.         foreach ( (array) $this->non_wp_rules as $match => $query) {
  1474.             // Apache 1.3 does not support the reluctant (non-greedy) modifier.
  1475.             $match = str_replace('.+?', '.+', $match);
  1476.  
  1477.             $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
  1478.         }
  1479.  
  1480.         if ( $this->use_verbose_rules ) {
  1481.             $this->matches = '';
  1482.             $rewrite = $this->rewrite_rules();
  1483.             $num_rules = count($rewrite);
  1484.             $rules .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
  1485.                 "RewriteCond %{REQUEST_FILENAME} -d\n" .
  1486.                 "RewriteRule ^.*$ - [S=$num_rules]\n";
  1487.  
  1488.             foreach ( (array) $rewrite as $match => $query) {
  1489.                 // Apache 1.3 does not support the reluctant (non-greedy) modifier.
  1490.                 $match = str_replace('.+?', '.+', $match);
  1491.  
  1492.                 if ( strpos($query, $this->index) !== false )
  1493.                     $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
  1494.                 else
  1495.                     $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
  1496.             }
  1497.         } else {
  1498.             $rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
  1499.                 "RewriteCond %{REQUEST_FILENAME} !-d\n" .
  1500.                 "RewriteRule . {$home_root}{$this->index} [L]\n";
  1501.         }
  1502.  
  1503.         $rules .= "</IfModule>\n";
  1504.  
  1505.         /**
  1506.          * Filters the list of rewrite rules formatted for output to an .htaccess file.
  1507.          *
  1508.          * @since 1.5.0
  1509.          *
  1510.          * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
  1511.          */
  1512.         $rules = apply_filters( 'mod_rewrite_rules', $rules );
  1513.  
  1514.         /**
  1515.          * Filters the list of rewrite rules formatted for output to an .htaccess file.
  1516.          *
  1517.          * @since 1.5.0
  1518.          * @deprecated 1.5.0 Use the mod_rewrite_rules filter instead.
  1519.          *
  1520.          * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
  1521.          */
  1522.         return apply_filters( 'rewrite_rules', $rules );
  1523.     }
  1524.  
  1525.     /**
  1526.      * Retrieves IIS7 URL Rewrite formatted rewrite rules to write to web.config file.
  1527.      *
  1528.      * Does not actually write to the web.config file, but creates the rules for
  1529.      * the process that will.
  1530.      *
  1531.      * @since 2.8.0
  1532.      *
  1533.      * @param bool $add_parent_tags Optional. Whether to add parent tags to the rewrite rule sets.
  1534.      *                              Default false.
  1535.      * @return string IIS7 URL rewrite rule sets.
  1536.      */
  1537.     public function iis7_url_rewrite_rules( $add_parent_tags = false ) {
  1538.         if ( ! $this->using_permalinks() )
  1539.             return '';
  1540.         $rules = '';
  1541.         if ( $add_parent_tags ) {
  1542.             $rules .= '<configuration>
  1543.     <system.webServer>
  1544.         <rewrite>
  1545.             <rules>';
  1546.         }
  1547.  
  1548.         $rules .= '
  1549.             <rule name="WordPress: ' . esc_attr( home_url() ) . '" patternSyntax="Wildcard">
  1550.                 <match url="*" />
  1551.                     <conditions>
  1552.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
  1553.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
  1554.                     </conditions>
  1555.                 <action type="Rewrite" url="index.php" />
  1556.             </rule>';
  1557.  
  1558.         if ( $add_parent_tags ) {
  1559.             $rules .= '
  1560.             </rules>
  1561.         </rewrite>
  1562.     </system.webServer>
  1563. </configuration>';
  1564.         }
  1565.  
  1566.         /**
  1567.          * Filters the list of rewrite rules formatted for output to a web.config.
  1568.          *
  1569.          * @since 2.8.0
  1570.          *
  1571.          * @param string $rules Rewrite rules formatted for IIS web.config.
  1572.          */
  1573.         return apply_filters( 'iis7_url_rewrite_rules', $rules );
  1574.     }
  1575.  
  1576.     /**
  1577.      * Adds a rewrite rule that transforms a URL structure to a set of query vars.
  1578.      *
  1579.      * Any value in the $after parameter that isn't 'bottom' will result in the rule
  1580.      * being placed at the top of the rewrite rules.
  1581.      *
  1582.      * @since 2.1.0
  1583.      * @since 4.4.0 Array support was added to the `$query` parameter.
  1584.      *
  1585.      * @param string       $regex Regular expression to match request against.
  1586.      * @param string|array $query The corresponding query vars for this rewrite rule.
  1587.      * @param string       $after Optional. Priority of the new rule. Accepts 'top'
  1588.      *                            or 'bottom'. Default 'bottom'.
  1589.      */
  1590.     public function add_rule( $regex, $query, $after = 'bottom' ) {
  1591.         if ( is_array( $query ) ) {
  1592.             $external = false;
  1593.             $query = add_query_arg( $query, 'index.php' );
  1594.         } else {
  1595.             $index = false === strpos( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' );
  1596.             $front = substr( $query, 0, $index );
  1597.  
  1598.             $external = $front != $this->index;
  1599.         }
  1600.  
  1601.         // "external" = it doesn't correspond to index.php.
  1602.         if ( $external ) {
  1603.             $this->add_external_rule( $regex, $query );
  1604.         } else {
  1605.             if ( 'bottom' == $after ) {
  1606.                 $this->extra_rules = array_merge( $this->extra_rules, array( $regex => $query ) );
  1607.             } else {
  1608.                 $this->extra_rules_top = array_merge( $this->extra_rules_top, array( $regex => $query ) );
  1609.             }
  1610.         }
  1611.     }
  1612.  
  1613.     /**
  1614.      * Adds a rewrite rule that doesn't correspond to index.php.
  1615.      *
  1616.      * @since 2.1.0
  1617.      *
  1618.      * @param string $regex Regular expression to match request against.
  1619.      * @param string $query The corresponding query vars for this rewrite rule.
  1620.      */
  1621.     public function add_external_rule( $regex, $query ) {
  1622.         $this->non_wp_rules[ $regex ] = $query;
  1623.     }
  1624.  
  1625.     /**
  1626.      * Adds an endpoint, like /trackback/.
  1627.      *
  1628.      * @since 2.1.0
  1629.      * @since 3.9.0 $query_var parameter added.
  1630.      * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
  1631.      *
  1632.      * @see add_rewrite_endpoint() for full documentation.
  1633.      * @global WP $wp
  1634.      *
  1635.      * @param string      $name      Name of the endpoint.
  1636.      * @param int         $places    Endpoint mask describing the places the endpoint should be added.
  1637.      * @param string|bool $query_var Optional. Name of the corresponding query variable. Pass `false` to
  1638.      *                               skip registering a query_var for this endpoint. Defaults to the
  1639.      *                               value of `$name`.
  1640.      */
  1641.     public function add_endpoint( $name, $places, $query_var = true ) {
  1642.         global $wp;
  1643.  
  1644.         // For backward compatibility, if null has explicitly been passed as `$query_var`, assume `true`.
  1645.         if ( true === $query_var || null === func_get_arg( 2 ) ) {
  1646.             $query_var = $name;
  1647.         }
  1648.         $this->endpoints[] = array( $places, $name, $query_var );
  1649.  
  1650.         if ( $query_var ) {
  1651.             $wp->add_query_var( $query_var );
  1652.         }
  1653.     }
  1654.  
  1655.     /**
  1656.      * Adds a new permalink structure.
  1657.      *
  1658.      * A permalink structure (permastruct) is an abstract definition of a set of rewrite rules;
  1659.      * it is an easy way of expressing a set of regular expressions that rewrite to a set of
  1660.      * query strings. The new permastruct is added to the WP_Rewrite::$extra_permastructs array.
  1661.      *
  1662.      * When the rewrite rules are built by WP_Rewrite::rewrite_rules(), all of these extra
  1663.      * permastructs are passed to WP_Rewrite::generate_rewrite_rules() which transforms them
  1664.      * into the regular expressions that many love to hate.
  1665.      *
  1666.      * The `$args` parameter gives you control over how WP_Rewrite::generate_rewrite_rules()
  1667.      * works on the new permastruct.
  1668.      *
  1669.      * @since 2.5.0
  1670.      *
  1671.      * @param string $name   Name for permalink structure.
  1672.      * @param string $struct Permalink structure (e.g. category/%category%)
  1673.      * @param array  $args   {
  1674.      *     Optional. Arguments for building rewrite rules based on the permalink structure.
  1675.      *     Default empty array.
  1676.      *
  1677.      *     @type bool $with_front  Whether the structure should be prepended with `WP_Rewrite::$front`.
  1678.      *                             Default true.
  1679.      *     @type int  $ep_mask     The endpoint mask defining which endpoints are added to the structure.
  1680.      *                             Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
  1681.      *                             `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
  1682.      *                             `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
  1683.      *                             Default `EP_NONE`.
  1684.      *     @type bool $paged       Whether archive pagination rules should be added for the structure.
  1685.      *                             Default true.
  1686.      *     @type bool $feed        Whether feed rewrite rules should be added for the structure. Default true.
  1687.      *     @type bool $forcomments Whether the feed rules should be a query for a comments feed. Default false.
  1688.      *     @type bool $walk_dirs   Whether the 'directories' making up the structure should be walked over
  1689.      *                             and rewrite rules built for each in-turn. Default true.
  1690.      *     @type bool $endpoints   Whether endpoints should be applied to the generated rules. Default true.
  1691.      * }
  1692.      */
  1693.     public function add_permastruct( $name, $struct, $args = array() ) {
  1694.         // Back-compat for the old parameters: $with_front and $ep_mask.
  1695.         if ( ! is_array( $args ) )
  1696.             $args = array( 'with_front' => $args );
  1697.         if ( func_num_args() == 4 )
  1698.             $args['ep_mask'] = func_get_arg( 3 );
  1699.  
  1700.         $defaults = array(
  1701.             'with_front' => true,
  1702.             'ep_mask' => EP_NONE,
  1703.             'paged' => true,
  1704.             'feed' => true,
  1705.             'forcomments' => false,
  1706.             'walk_dirs' => true,
  1707.             'endpoints' => true,
  1708.         );
  1709.         $args = array_intersect_key( $args, $defaults );
  1710.         $args = wp_parse_args( $args, $defaults );
  1711.  
  1712.         if ( $args['with_front'] )
  1713.             $struct = $this->front . $struct;
  1714.         else
  1715.             $struct = $this->root . $struct;
  1716.         $args['struct'] = $struct;
  1717.  
  1718.         $this->extra_permastructs[ $name ] = $args;
  1719.     }
  1720.  
  1721.     /**
  1722.      * Removes a permalink structure.
  1723.      *
  1724.      * @since 4.5.0
  1725.      *
  1726.      * @param string $name Name for permalink structure.
  1727.      */
  1728.     public function remove_permastruct( $name ) {
  1729.         unset( $this->extra_permastructs[ $name ] );
  1730.     }
  1731.  
  1732.     /**
  1733.      * Removes rewrite rules and then recreate rewrite rules.
  1734.      *
  1735.      * Calls WP_Rewrite::wp_rewrite_rules() after removing the 'rewrite_rules' option.
  1736.      * If the function named 'save_mod_rewrite_rules' exists, it will be called.
  1737.      *
  1738.      * @since 2.0.1
  1739.      *
  1740.      * @staticvar bool $do_hard_later
  1741.      *
  1742.      * @param bool $hard Whether to update .htaccess (hard flush) or just update rewrite_rules option (soft flush). Default is true (hard).
  1743.      */
  1744.     public function flush_rules( $hard = true ) {
  1745.         static $do_hard_later = null;
  1746.  
  1747.         // Prevent this action from running before everyone has registered their rewrites.
  1748.         if ( ! did_action( 'wp_loaded' ) ) {
  1749.             add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
  1750.             $do_hard_later = ( isset( $do_hard_later ) ) ? $do_hard_later || $hard : $hard;
  1751.             return;
  1752.         }
  1753.  
  1754.         if ( isset( $do_hard_later ) ) {
  1755.             $hard = $do_hard_later;
  1756.             unset( $do_hard_later );
  1757.         }
  1758.  
  1759.         update_option( 'rewrite_rules', '' );
  1760.         $this->wp_rewrite_rules();
  1761.  
  1762.         /**
  1763.          * Filters whether a "hard" rewrite rule flush should be performed when requested.
  1764.          *
  1765.          * A "hard" flush updates .htaccess (Apache) or web.config (IIS).
  1766.          *
  1767.          * @since 3.7.0
  1768.          *
  1769.          * @param bool $hard Whether to flush rewrite rules "hard". Default true.
  1770.          */
  1771.         if ( ! $hard || ! apply_filters( 'flush_rewrite_rules_hard', true ) ) {
  1772.             return;
  1773.         }
  1774.         if ( function_exists( 'save_mod_rewrite_rules' ) )
  1775.             save_mod_rewrite_rules();
  1776.         if ( function_exists( 'iis7_save_url_rewrite_rules' ) )
  1777.             iis7_save_url_rewrite_rules();
  1778.     }
  1779.  
  1780.     /**
  1781.      * Sets up the object's properties.
  1782.      *
  1783.      * The 'use_verbose_page_rules' object property will be set to true if the
  1784.      * permalink structure begins with one of the following: '%postname%', '%category%',
  1785.      * '%tag%', or '%author%'.
  1786.      *
  1787.      * @since 1.5.0
  1788.      */
  1789.     public function init() {
  1790.         $this->extra_rules = $this->non_wp_rules = $this->endpoints = array();
  1791.         $this->permalink_structure = get_option('permalink_structure');
  1792.         $this->front = substr($this->permalink_structure, 0, strpos($this->permalink_structure, '%'));
  1793.         $this->root = '';
  1794.  
  1795.         if ( $this->using_index_permalinks() )
  1796.             $this->root = $this->index . '/';
  1797.  
  1798.         unset($this->author_structure);
  1799.         unset($this->date_structure);
  1800.         unset($this->page_structure);
  1801.         unset($this->search_structure);
  1802.         unset($this->feed_structure);
  1803.         unset($this->comment_feed_structure);
  1804.         $this->use_trailing_slashes = ( '/' == substr($this->permalink_structure, -1, 1) );
  1805.  
  1806.         // Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
  1807.         if ( preg_match("/^[^%]*%(?:postname|category|tag|author)%/", $this->permalink_structure) )
  1808.              $this->use_verbose_page_rules = true;
  1809.         else
  1810.             $this->use_verbose_page_rules = false;
  1811.     }
  1812.  
  1813.     /**
  1814.      * Sets the main permalink structure for the site.
  1815.      *
  1816.      * Will update the 'permalink_structure' option, if there is a difference
  1817.      * between the current permalink structure and the parameter value. Calls
  1818.      * WP_Rewrite::init() after the option is updated.
  1819.      *
  1820.      * Fires the {@see 'permalink_structure_changed'} action once the init call has
  1821.      * processed passing the old and new values
  1822.      *
  1823.      * @since 1.5.0
  1824.      *
  1825.      * @param string $permalink_structure Permalink structure.
  1826.      */
  1827.     public function set_permalink_structure($permalink_structure) {
  1828.         if ( $permalink_structure != $this->permalink_structure ) {
  1829.             $old_permalink_structure = $this->permalink_structure;
  1830.             update_option('permalink_structure', $permalink_structure);
  1831.  
  1832.             $this->init();
  1833.  
  1834.             /**
  1835.              * Fires after the permalink structure is updated.
  1836.              *
  1837.              * @since 2.8.0
  1838.              *
  1839.              * @param string $old_permalink_structure The previous permalink structure.
  1840.              * @param string $permalink_structure     The new permalink structure.
  1841.              */
  1842.             do_action( 'permalink_structure_changed', $old_permalink_structure, $permalink_structure );
  1843.         }
  1844.     }
  1845.  
  1846.     /**
  1847.      * Sets the category base for the category permalink.
  1848.      *
  1849.      * Will update the 'category_base' option, if there is a difference between
  1850.      * the current category base and the parameter value. Calls WP_Rewrite::init()
  1851.      * after the option is updated.
  1852.      *
  1853.      * @since 1.5.0
  1854.      *
  1855.      * @param string $category_base Category permalink structure base.
  1856.      */
  1857.     public function set_category_base($category_base) {
  1858.         if ( $category_base != get_option('category_base') ) {
  1859.             update_option('category_base', $category_base);
  1860.             $this->init();
  1861.         }
  1862.     }
  1863.  
  1864.     /**
  1865.      * Sets the tag base for the tag permalink.
  1866.      *
  1867.      * Will update the 'tag_base' option, if there is a difference between the
  1868.      * current tag base and the parameter value. Calls WP_Rewrite::init() after
  1869.      * the option is updated.
  1870.      *
  1871.      * @since 2.3.0
  1872.      *
  1873.      * @param string $tag_base Tag permalink structure base.
  1874.      */
  1875.     public function set_tag_base( $tag_base ) {
  1876.         if ( $tag_base != get_option( 'tag_base') ) {
  1877.             update_option( 'tag_base', $tag_base );
  1878.             $this->init();
  1879.         }
  1880.     }
  1881.  
  1882.     /**
  1883.      * Constructor - Calls init(), which runs setup.
  1884.      *
  1885.      * @since 1.5.0
  1886.      *
  1887.      */
  1888.     public function __construct() {
  1889.         $this->init();
  1890.     }
  1891. }
  1892.