[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/ -> class-wp-rewrite.php (source)

   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  
 438          // Now reverse it, because we need parents after children for rewrite rules to work properly.
 439          $posts = array_reverse( $posts, true );
 440  
 441          $page_uris            = array();
 442          $page_attachment_uris = array();
 443  
 444          foreach ( $posts as $id => $post ) {
 445              // URL => page name
 446              $uri         = get_page_uri( $id );
 447              $attachments = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent = %d", $id ) );
 448              if ( ! empty( $attachments ) ) {
 449                  foreach ( $attachments as $attachment ) {
 450                      $attach_uri                          = get_page_uri( $attachment->ID );
 451                      $page_attachment_uris[ $attach_uri ] = $attachment->ID;
 452                  }
 453              }
 454  
 455              $page_uris[ $uri ] = $id;
 456          }
 457  
 458          return array( $page_uris, $page_attachment_uris );
 459      }
 460  
 461      /**
 462       * Retrieves all of the rewrite rules for pages.
 463       *
 464       * @since 1.5.0
 465       *
 466       * @return array Page rewrite rules.
 467       */
 468  	public function page_rewrite_rules() {
 469          // The extra .? at the beginning prevents clashes with other regular expressions in the rules array.
 470          $this->add_rewrite_tag( '%pagename%', '(.?.+?)', 'pagename=' );
 471  
 472          return $this->generate_rewrite_rules( $this->get_page_permastruct(), EP_PAGES, true, true, false, false );
 473      }
 474  
 475      /**
 476       * Retrieves date permalink structure, with year, month, and day.
 477       *
 478       * The permalink structure for the date, if not set already depends on the
 479       * permalink structure. It can be one of three formats. The first is year,
 480       * month, day; the second is day, month, year; and the last format is month,
 481       * day, year. These are matched against the permalink structure for which
 482       * one is used. If none matches, then the default will be used, which is
 483       * year, month, day.
 484       *
 485       * Prevents post ID and date permalinks from overlapping. In the case of
 486       * post_id, the date permalink will be prepended with front permalink with
 487       * 'date/' before the actual permalink to form the complete date permalink
 488       * structure.
 489       *
 490       * @since 1.5.0
 491       *
 492       * @return string|false False on no permalink structure. Date permalink structure.
 493       */
 494  	public function get_date_permastruct() {
 495          if ( isset( $this->date_structure ) ) {
 496              return $this->date_structure;
 497          }
 498  
 499          if ( empty( $this->permalink_structure ) ) {
 500              $this->date_structure = '';
 501              return false;
 502          }
 503  
 504          // The date permalink must have year, month, and day separated by slashes.
 505          $endians = array( '%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%', '%monthnum%/%day%/%year%' );
 506  
 507          $this->date_structure = '';
 508          $date_endian          = '';
 509  
 510          foreach ( $endians as $endian ) {
 511              if ( false !== strpos( $this->permalink_structure, $endian ) ) {
 512                  $date_endian = $endian;
 513                  break;
 514              }
 515          }
 516  
 517          if ( empty( $date_endian ) ) {
 518              $date_endian = '%year%/%monthnum%/%day%';
 519          }
 520  
 521          /*
 522           * Do not allow the date tags and %post_id% to overlap in the permalink
 523           * structure. If they do, move the date tags to $front/date/.
 524           */
 525          $front = $this->front;
 526          preg_match_all( '/%.+?%/', $this->permalink_structure, $tokens );
 527          $tok_index = 1;
 528          foreach ( (array) $tokens[0] as $token ) {
 529              if ( '%post_id%' == $token && ( $tok_index <= 3 ) ) {
 530                  $front = $front . 'date/';
 531                  break;
 532              }
 533              $tok_index++;
 534          }
 535  
 536          $this->date_structure = $front . $date_endian;
 537  
 538          return $this->date_structure;
 539      }
 540  
 541      /**
 542       * Retrieves the year permalink structure without month and day.
 543       *
 544       * Gets the date permalink structure and strips out the month and day
 545       * permalink structures.
 546       *
 547       * @since 1.5.0
 548       *
 549       * @return false|string False on failure. Year structure on success.
 550       */
 551  	public function get_year_permastruct() {
 552          $structure = $this->get_date_permastruct();
 553  
 554          if ( empty( $structure ) ) {
 555              return false;
 556          }
 557  
 558          $structure = str_replace( '%monthnum%', '', $structure );
 559          $structure = str_replace( '%day%', '', $structure );
 560          $structure = preg_replace( '#/+#', '/', $structure );
 561  
 562          return $structure;
 563      }
 564  
 565      /**
 566       * Retrieves the month permalink structure without day and with year.
 567       *
 568       * Gets the date permalink structure and strips out the day permalink
 569       * structures. Keeps the year permalink structure.
 570       *
 571       * @since 1.5.0
 572       *
 573       * @return false|string False on failure. Year/Month structure on success.
 574       */
 575  	public function get_month_permastruct() {
 576          $structure = $this->get_date_permastruct();
 577  
 578          if ( empty( $structure ) ) {
 579              return false;
 580          }
 581  
 582          $structure = str_replace( '%day%', '', $structure );
 583          $structure = preg_replace( '#/+#', '/', $structure );
 584  
 585          return $structure;
 586      }
 587  
 588      /**
 589       * Retrieves the day permalink structure with month and year.
 590       *
 591       * Keeps date permalink structure with all year, month, and day.
 592       *
 593       * @since 1.5.0
 594       *
 595       * @return string|false False on failure. Year/Month/Day structure on success.
 596       */
 597  	public function get_day_permastruct() {
 598          return $this->get_date_permastruct();
 599      }
 600  
 601      /**
 602       * Retrieves the permalink structure for categories.
 603       *
 604       * If the category_base property has no value, then the category structure
 605       * will have the front property value, followed by 'category', and finally
 606       * '%category%'. If it does, then the root property will be used, along with
 607       * the category_base property value.
 608       *
 609       * @since 1.5.0
 610       *
 611       * @return string|false False on failure. Category permalink structure.
 612       */
 613  	public function get_category_permastruct() {
 614          return $this->get_extra_permastruct( 'category' );
 615      }
 616  
 617      /**
 618       * Retrieve the permalink structure for tags.
 619       *
 620       * If the tag_base property has no value, then the tag structure will have
 621       * the front property value, followed by 'tag', and finally '%tag%'. If it
 622       * does, then the root property will be used, along with the tag_base
 623       * property value.
 624       *
 625       * @since 2.3.0
 626       *
 627       * @return string|false False on failure. Tag permalink structure.
 628       */
 629  	public function get_tag_permastruct() {
 630          return $this->get_extra_permastruct( 'post_tag' );
 631      }
 632  
 633      /**
 634       * Retrieves an extra permalink structure by name.
 635       *
 636       * @since 2.5.0
 637       *
 638       * @param string $name Permalink structure name.
 639       * @return string|false False if not found. Permalink structure string.
 640       */
 641  	public function get_extra_permastruct( $name ) {
 642          if ( empty( $this->permalink_structure ) ) {
 643              return false;
 644          }
 645  
 646          if ( isset( $this->extra_permastructs[ $name ] ) ) {
 647              return $this->extra_permastructs[ $name ]['struct'];
 648          }
 649  
 650          return false;
 651      }
 652  
 653      /**
 654       * Retrieves the author permalink structure.
 655       *
 656       * The permalink structure is front property, author base, and finally
 657       * '/%author%'. Will set the author_structure property and then return it
 658       * without attempting to set the value again.
 659       *
 660       * @since 1.5.0
 661       *
 662       * @return string|false False if not found. Permalink structure string.
 663       */
 664  	public function get_author_permastruct() {
 665          if ( isset( $this->author_structure ) ) {
 666              return $this->author_structure;
 667          }
 668  
 669          if ( empty( $this->permalink_structure ) ) {
 670              $this->author_structure = '';
 671              return false;
 672          }
 673  
 674          $this->author_structure = $this->front . $this->author_base . '/%author%';
 675  
 676          return $this->author_structure;
 677      }
 678  
 679      /**
 680       * Retrieves the search permalink structure.
 681       *
 682       * The permalink structure is root property, search base, and finally
 683       * '/%search%'. Will set the search_structure property and then return it
 684       * without attempting to set the value again.
 685       *
 686       * @since 1.5.0
 687       *
 688       * @return string|false False if not found. Permalink structure string.
 689       */
 690  	public function get_search_permastruct() {
 691          if ( isset( $this->search_structure ) ) {
 692              return $this->search_structure;
 693          }
 694  
 695          if ( empty( $this->permalink_structure ) ) {
 696              $this->search_structure = '';
 697              return false;
 698          }
 699  
 700          $this->search_structure = $this->root . $this->search_base . '/%search%';
 701  
 702          return $this->search_structure;
 703      }
 704  
 705      /**
 706       * Retrieves the page permalink structure.
 707       *
 708       * The permalink structure is root property, and '%pagename%'. Will set the
 709       * page_structure property and then return it without attempting to set the
 710       * value again.
 711       *
 712       * @since 1.5.0
 713       *
 714       * @return string|false False if not found. Permalink structure string.
 715       */
 716  	public function get_page_permastruct() {
 717          if ( isset( $this->page_structure ) ) {
 718              return $this->page_structure;
 719          }
 720  
 721          if ( empty( $this->permalink_structure ) ) {
 722              $this->page_structure = '';
 723              return false;
 724          }
 725  
 726          $this->page_structure = $this->root . '%pagename%';
 727  
 728          return $this->page_structure;
 729      }
 730  
 731      /**
 732       * Retrieves the feed permalink structure.
 733       *
 734       * The permalink structure is root property, feed base, and finally
 735       * '/%feed%'. Will set the feed_structure property and then return it
 736       * without attempting to set the value again.
 737       *
 738       * @since 1.5.0
 739       *
 740       * @return string|false False if not found. Permalink structure string.
 741       */
 742  	public function get_feed_permastruct() {
 743          if ( isset( $this->feed_structure ) ) {
 744              return $this->feed_structure;
 745          }
 746  
 747          if ( empty( $this->permalink_structure ) ) {
 748              $this->feed_structure = '';
 749              return false;
 750          }
 751  
 752          $this->feed_structure = $this->root . $this->feed_base . '/%feed%';
 753  
 754          return $this->feed_structure;
 755      }
 756  
 757      /**
 758       * Retrieves the comment feed permalink structure.
 759       *
 760       * The permalink structure is root property, comment base property, feed
 761       * base and finally '/%feed%'. Will set the comment_feed_structure property
 762       * and then return it without attempting to set the value again.
 763       *
 764       * @since 1.5.0
 765       *
 766       * @return string|false False if not found. Permalink structure string.
 767       */
 768  	public function get_comment_feed_permastruct() {
 769          if ( isset( $this->comment_feed_structure ) ) {
 770              return $this->comment_feed_structure;
 771          }
 772  
 773          if ( empty( $this->permalink_structure ) ) {
 774              $this->comment_feed_structure = '';
 775              return false;
 776          }
 777  
 778          $this->comment_feed_structure = $this->root . $this->comments_base . '/' . $this->feed_base . '/%feed%';
 779  
 780          return $this->comment_feed_structure;
 781      }
 782  
 783      /**
 784       * Adds or updates existing rewrite tags (e.g. %postname%).
 785       *
 786       * If the tag already exists, replace the existing pattern and query for
 787       * that tag, otherwise add the new tag.
 788       *
 789       * @since 1.5.0
 790       *
 791       * @see WP_Rewrite::$rewritecode
 792       * @see WP_Rewrite::$rewritereplace
 793       * @see WP_Rewrite::$queryreplace
 794       *
 795       * @param string $tag   Name of the rewrite tag to add or update.
 796       * @param string $regex Regular expression to substitute the tag for in rewrite rules.
 797       * @param string $query String to append to the rewritten query. Must end in '='.
 798       */
 799  	public function add_rewrite_tag( $tag, $regex, $query ) {
 800          $position = array_search( $tag, $this->rewritecode );
 801          if ( false !== $position && null !== $position ) {
 802              $this->rewritereplace[ $position ] = $regex;
 803              $this->queryreplace[ $position ]   = $query;
 804          } else {
 805              $this->rewritecode[]    = $tag;
 806              $this->rewritereplace[] = $regex;
 807              $this->queryreplace[]   = $query;
 808          }
 809      }
 810  
 811  
 812      /**
 813       * Removes an existing rewrite tag.
 814       *
 815       * @since 4.5.0
 816       *
 817       * @see WP_Rewrite::$rewritecode
 818       * @see WP_Rewrite::$rewritereplace
 819       * @see WP_Rewrite::$queryreplace
 820       *
 821       * @param string $tag Name of the rewrite tag to remove.
 822       */
 823  	public function remove_rewrite_tag( $tag ) {
 824          $position = array_search( $tag, $this->rewritecode );
 825          if ( false !== $position && null !== $position ) {
 826              unset( $this->rewritecode[ $position ] );
 827              unset( $this->rewritereplace[ $position ] );
 828              unset( $this->queryreplace[ $position ] );
 829          }
 830      }
 831  
 832      /**
 833       * Generates rewrite rules from a permalink structure.
 834       *
 835       * The main WP_Rewrite function for building the rewrite rule list. The
 836       * contents of the function is a mix of black magic and regular expressions,
 837       * so best just ignore the contents and move to the parameters.
 838       *
 839       * @since 1.5.0
 840       *
 841       * @param string $permalink_structure The permalink structure.
 842       * @param int    $ep_mask             Optional. Endpoint mask defining what endpoints are added to the structure.
 843       *                                    Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
 844       *                                    `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
 845       *                                    `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
 846       *                                    Default `EP_NONE`.
 847       * @param bool   $paged               Optional. Whether archive pagination rules should be added for the structure.
 848       *                                    Default true.
 849       * @param bool   $feed                Optional Whether feed rewrite rules should be added for the structure.
 850       *                                    Default true.
 851       * @param bool   $forcomments         Optional. Whether the feed rules should be a query for a comments feed.
 852       *                                    Default false.
 853       * @param bool   $walk_dirs           Optional. Whether the 'directories' making up the structure should be walked
 854       *                                    over and rewrite rules built for each in-turn. Default true.
 855       * @param bool   $endpoints           Optional. Whether endpoints should be applied to the generated rewrite rules.
 856       *                                    Default true.
 857       * @return array Rewrite rule list.
 858       */
 859  	public function generate_rewrite_rules( $permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true ) {
 860          // Build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
 861          $feedregex2 = '';
 862          foreach ( (array) $this->feeds as $feed_name ) {
 863              $feedregex2 .= $feed_name . '|';
 864          }
 865          $feedregex2 = '(' . trim( $feedregex2, '|' ) . ')/?$';
 866  
 867          /*
 868           * $feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
 869           * and <permalink>/atom are both possible
 870           */
 871          $feedregex = $this->feed_base . '/' . $feedregex2;
 872  
 873          // Build a regex to match the trackback and page/xx parts of URLs.
 874          $trackbackregex = 'trackback/?$';
 875          $pageregex      = $this->pagination_base . '/?([0-9]{1,})/?$';
 876          $commentregex   = $this->comments_pagination_base . '-([0-9]{1,})/?$';
 877          $embedregex     = 'embed/?$';
 878  
 879          // Build up an array of endpoint regexes to append => queries to append.
 880          if ( $endpoints ) {
 881              $ep_query_append = array();
 882              foreach ( (array) $this->endpoints as $endpoint ) {
 883                  // Match everything after the endpoint name, but allow for nothing to appear there.
 884                  $epmatch = $endpoint[1] . '(/(.*))?/?$';
 885  
 886                  // This will be appended on to the rest of the query for each dir.
 887                  $epquery                     = '&' . $endpoint[2] . '=';
 888                  $ep_query_append[ $epmatch ] = array( $endpoint[0], $epquery );
 889              }
 890          }
 891  
 892          // Get everything up to the first rewrite tag.
 893          $front = substr( $permalink_structure, 0, strpos( $permalink_structure, '%' ) );
 894  
 895          // Build an array of the tags (note that said array ends up being in $tokens[0]).
 896          preg_match_all( '/%.+?%/', $permalink_structure, $tokens );
 897  
 898          $num_tokens = count( $tokens[0] );
 899  
 900          $index          = $this->index; //probably 'index.php'
 901          $feedindex      = $index;
 902          $trackbackindex = $index;
 903          $embedindex     = $index;
 904  
 905          /*
 906           * Build a list from the rewritecode and queryreplace arrays, that will look something
 907           * like tagname=$matches[i] where i is the current $i.
 908           */
 909          $queries = array();
 910          for ( $i = 0; $i < $num_tokens; ++$i ) {
 911              if ( 0 < $i ) {
 912                  $queries[ $i ] = $queries[ $i - 1 ] . '&';
 913              } else {
 914                  $queries[ $i ] = '';
 915              }
 916  
 917              $query_token    = str_replace( $this->rewritecode, $this->queryreplace, $tokens[0][ $i ] ) . $this->preg_index( $i + 1 );
 918              $queries[ $i ] .= $query_token;
 919          }
 920  
 921          // Get the structure, minus any cruft (stuff that isn't tags) at the front.
 922          $structure = $permalink_structure;
 923          if ( $front != '/' ) {
 924              $structure = str_replace( $front, '', $structure );
 925          }
 926  
 927          /*
 928           * Create a list of dirs to walk over, making rewrite rules for each level
 929           * so for example, a $structure of /%year%/%monthnum%/%postname% would create
 930           * rewrite rules for /%year%/, /%year%/%monthnum%/ and /%year%/%monthnum%/%postname%
 931           */
 932          $structure = trim( $structure, '/' );
 933          $dirs      = $walk_dirs ? explode( '/', $structure ) : array( $structure );
 934          $num_dirs  = count( $dirs );
 935  
 936          // Strip slashes from the front of $front.
 937          $front = preg_replace( '|^/+|', '', $front );
 938  
 939          // The main workhorse loop.
 940          $post_rewrite = array();
 941          $struct       = $front;
 942          for ( $j = 0; $j < $num_dirs; ++$j ) {
 943              // Get the struct for this dir, and trim slashes off the front.
 944              $struct .= $dirs[ $j ] . '/'; // Accumulate. see comment near explode('/', $structure) above.
 945              $struct  = ltrim( $struct, '/' );
 946  
 947              // Replace tags with regexes.
 948              $match = str_replace( $this->rewritecode, $this->rewritereplace, $struct );
 949  
 950              // Make a list of tags, and store how many there are in $num_toks.
 951              $num_toks = preg_match_all( '/%.+?%/', $struct, $toks );
 952  
 953              // Get the 'tagname=$matches[i]'.
 954              $query = ( ! empty( $num_toks ) && isset( $queries[ $num_toks - 1 ] ) ) ? $queries[ $num_toks - 1 ] : '';
 955  
 956              // Set up $ep_mask_specific which is used to match more specific URL types.
 957              switch ( $dirs[ $j ] ) {
 958                  case '%year%':
 959                      $ep_mask_specific = EP_YEAR;
 960                      break;
 961                  case '%monthnum%':
 962                      $ep_mask_specific = EP_MONTH;
 963                      break;
 964                  case '%day%':
 965                      $ep_mask_specific = EP_DAY;
 966                      break;
 967                  default:
 968                      $ep_mask_specific = EP_NONE;
 969              }
 970  
 971              // Create query for /page/xx.
 972              $pagematch = $match . $pageregex;
 973              $pagequery = $index . '?' . $query . '&paged=' . $this->preg_index( $num_toks + 1 );
 974  
 975              // Create query for /comment-page-xx.
 976              $commentmatch = $match . $commentregex;
 977              $commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index( $num_toks + 1 );
 978  
 979              if ( get_option( 'page_on_front' ) ) {
 980                  // Create query for Root /comment-page-xx.
 981                  $rootcommentmatch = $match . $commentregex;
 982                  $rootcommentquery = $index . '?' . $query . '&page_id=' . get_option( 'page_on_front' ) . '&cpage=' . $this->preg_index( $num_toks + 1 );
 983              }
 984  
 985              // Create query for /feed/(feed|atom|rss|rss2|rdf).
 986              $feedmatch = $match . $feedregex;
 987              $feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );
 988  
 989              // Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
 990              $feedmatch2 = $match . $feedregex2;
 991              $feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );
 992  
 993              // Create query and regex for embeds.
 994              $embedmatch = $match . $embedregex;
 995              $embedquery = $embedindex . '?' . $query . '&embed=true';
 996  
 997              // If asked to, turn the feed queries into comment feed ones.
 998              if ( $forcomments ) {
 999                  $feedquery  .= '&withcomments=1';
1000                  $feedquery2 .= '&withcomments=1';
1001              }
1002  
1003              // Start creating the array of rewrites for this dir.
1004              $rewrite = array();
1005  
1006              // ...adding on /feed/ regexes => queries
1007              if ( $feed ) {
1008                  $rewrite = array(
1009                      $feedmatch  => $feedquery,
1010                      $feedmatch2 => $feedquery2,
1011                      $embedmatch => $embedquery,
1012                  );
1013              }
1014  
1015              //...and /page/xx ones
1016              if ( $paged ) {
1017                  $rewrite = array_merge( $rewrite, array( $pagematch => $pagequery ) );
1018              }
1019  
1020              // Only on pages with comments add ../comment-page-xx/.
1021              if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask ) {
1022                  $rewrite = array_merge( $rewrite, array( $commentmatch => $commentquery ) );
1023              } elseif ( EP_ROOT & $ep_mask && get_option( 'page_on_front' ) ) {
1024                  $rewrite = array_merge( $rewrite, array( $rootcommentmatch => $rootcommentquery ) );
1025              }
1026  
1027              // Do endpoints.
1028              if ( $endpoints ) {
1029                  foreach ( (array) $ep_query_append as $regex => $ep ) {
1030                      // Add the endpoints on if the mask fits.
1031                      if ( $ep[0] & $ep_mask || $ep[0] & $ep_mask_specific ) {
1032                          $rewrite[ $match . $regex ] = $index . '?' . $query . $ep[1] . $this->preg_index( $num_toks + 2 );
1033                      }
1034                  }
1035              }
1036  
1037              // If we've got some tags in this dir.
1038              if ( $num_toks ) {
1039                  $post = false;
1040                  $page = false;
1041  
1042                  /*
1043                   * Check to see if this dir is permalink-level: i.e. the structure specifies an
1044                   * individual post. Do this by checking it contains at least one of 1) post name,
1045                   * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
1046                   * minute all present). Set these flags now as we need them for the endpoints.
1047                   */
1048                  if ( strpos( $struct, '%postname%' ) !== false
1049                          || strpos( $struct, '%post_id%' ) !== false
1050                          || strpos( $struct, '%pagename%' ) !== false
1051                          || ( strpos( $struct, '%year%' ) !== false && strpos( $struct, '%monthnum%' ) !== false && strpos( $struct, '%day%' ) !== false && strpos( $struct, '%hour%' ) !== false && strpos( $struct, '%minute%' ) !== false && strpos( $struct, '%second%' ) !== false )
1052                          ) {
1053                      $post = true;
1054                      if ( strpos( $struct, '%pagename%' ) !== false ) {
1055                          $page = true;
1056                      }
1057                  }
1058  
1059                  if ( ! $post ) {
1060                      // For custom post types, we need to add on endpoints as well.
1061                      foreach ( get_post_types( array( '_builtin' => false ) ) as $ptype ) {
1062                          if ( strpos( $struct, "%$ptype%" ) !== false ) {
1063                              $post = true;
1064  
1065                              // This is for page style attachment URLs.
1066                              $page = is_post_type_hierarchical( $ptype );
1067                              break;
1068                          }
1069                      }
1070                  }
1071  
1072                  // If creating rules for a permalink, do all the endpoints like attachments etc.
1073                  if ( $post ) {
1074                      // Create query and regex for trackback.
1075                      $trackbackmatch = $match . $trackbackregex;
1076                      $trackbackquery = $trackbackindex . '?' . $query . '&tb=1';
1077  
1078                      // Create query and regex for embeds.
1079                      $embedmatch = $match . $embedregex;
1080                      $embedquery = $embedindex . '?' . $query . '&embed=true';
1081  
1082                      // Trim slashes from the end of the regex for this dir.
1083                      $match = rtrim( $match, '/' );
1084  
1085                      // Get rid of brackets.
1086                      $submatchbase = str_replace( array( '(', ')' ), '', $match );
1087  
1088                      // Add a rule for at attachments, which take the form of <permalink>/some-text.
1089                      $sub1 = $submatchbase . '/([^/]+)/';
1090  
1091                      // Add trackback regex <permalink>/trackback/...
1092                      $sub1tb = $sub1 . $trackbackregex;
1093  
1094                      // And <permalink>/feed/(atom|...)
1095                      $sub1feed = $sub1 . $feedregex;
1096  
1097                      // And <permalink>/(feed|atom...)
1098                      $sub1feed2 = $sub1 . $feedregex2;
1099  
1100                      // And <permalink>/comment-page-xx
1101                      $sub1comment = $sub1 . $commentregex;
1102  
1103                      // And <permalink>/embed/...
1104                      $sub1embed = $sub1 . $embedregex;
1105  
1106                      /*
1107                       * Add another rule to match attachments in the explicit form:
1108                       * <permalink>/attachment/some-text
1109                       */
1110                      $sub2 = $submatchbase . '/attachment/([^/]+)/';
1111  
1112                      // And add trackbacks <permalink>/attachment/trackback.
1113                      $sub2tb = $sub2 . $trackbackregex;
1114  
1115                      // Feeds, <permalink>/attachment/feed/(atom|...)
1116                      $sub2feed = $sub2 . $feedregex;
1117  
1118                      // And feeds again on to this <permalink>/attachment/(feed|atom...)
1119                      $sub2feed2 = $sub2 . $feedregex2;
1120  
1121                      // And <permalink>/comment-page-xx
1122                      $sub2comment = $sub2 . $commentregex;
1123  
1124                      // And <permalink>/embed/...
1125                      $sub2embed = $sub2 . $embedregex;
1126  
1127                      // Create queries for these extra tag-ons we've just dealt with.
1128                      $subquery        = $index . '?attachment=' . $this->preg_index( 1 );
1129                      $subtbquery      = $subquery . '&tb=1';
1130                      $subfeedquery    = $subquery . '&feed=' . $this->preg_index( 2 );
1131                      $subcommentquery = $subquery . '&cpage=' . $this->preg_index( 2 );
1132                      $subembedquery   = $subquery . '&embed=true';
1133  
1134                      // Do endpoints for attachments.
1135                      if ( ! empty( $endpoints ) ) {
1136                          foreach ( (array) $ep_query_append as $regex => $ep ) {
1137                              if ( $ep[0] & EP_ATTACHMENT ) {
1138                                  $rewrite[ $sub1 . $regex ] = $subquery . $ep[1] . $this->preg_index( 3 );
1139                                  $rewrite[ $sub2 . $regex ] = $subquery . $ep[1] . $this->preg_index( 3 );
1140                              }
1141                          }
1142                      }
1143  
1144                      /*
1145                       * Now we've finished with endpoints, finish off the $sub1 and $sub2 matches
1146                       * add a ? as we don't have to match that last slash, and finally a $ so we
1147                       * match to the end of the URL
1148                       */
1149                      $sub1 .= '?$';
1150                      $sub2 .= '?$';
1151  
1152                      /*
1153                       * Post pagination, e.g. <permalink>/2/
1154                       * Previously: '(/[0-9]+)?/?$', which produced '/2' for page.
1155                       * When cast to int, returned 0.
1156                       */
1157                      $match = $match . '(?:/([0-9]+))?/?$';
1158                      $query = $index . '?' . $query . '&page=' . $this->preg_index( $num_toks + 1 );
1159  
1160                      // Not matching a permalink so this is a lot simpler.
1161                  } else {
1162                      // Close the match and finalise the query.
1163                      $match .= '?$';
1164                      $query  = $index . '?' . $query;
1165                  }
1166  
1167                  /*
1168                   * Create the final array for this dir by joining the $rewrite array (which currently
1169                   * only contains rules/queries for trackback, pages etc) to the main regex/query for
1170                   * this dir
1171                   */
1172                  $rewrite = array_merge( $rewrite, array( $match => $query ) );
1173  
1174                  // If we're matching a permalink, add those extras (attachments etc) on.
1175                  if ( $post ) {
1176                      // Add trackback.
1177                      $rewrite = array_merge( array( $trackbackmatch => $trackbackquery ), $rewrite );
1178  
1179                      // Add embed.
1180                      $rewrite = array_merge( array( $embedmatch => $embedquery ), $rewrite );
1181  
1182                      // Add regexes/queries for attachments, attachment trackbacks and so on.
1183                      if ( ! $page ) {
1184                          // Require <permalink>/attachment/stuff form for pages because of confusion with subpages.
1185                          $rewrite = array_merge(
1186                              $rewrite,
1187                              array(
1188                                  $sub1        => $subquery,
1189                                  $sub1tb      => $subtbquery,
1190                                  $sub1feed    => $subfeedquery,
1191                                  $sub1feed2   => $subfeedquery,
1192                                  $sub1comment => $subcommentquery,
1193                                  $sub1embed   => $subembedquery,
1194                              )
1195                          );
1196                      }
1197  
1198                      $rewrite = array_merge(
1199                          array(
1200                              $sub2        => $subquery,
1201                              $sub2tb      => $subtbquery,
1202                              $sub2feed    => $subfeedquery,
1203                              $sub2feed2   => $subfeedquery,
1204                              $sub2comment => $subcommentquery,
1205                              $sub2embed   => $subembedquery,
1206                          ),
1207                          $rewrite
1208                      );
1209                  }
1210              }
1211              // Add the rules for this dir to the accumulating $post_rewrite.
1212              $post_rewrite = array_merge( $rewrite, $post_rewrite );
1213          }
1214  
1215          // The finished rules. phew!
1216          return $post_rewrite;
1217      }
1218  
1219      /**
1220       * Generates rewrite rules with permalink structure and walking directory only.
1221       *
1222       * Shorten version of WP_Rewrite::generate_rewrite_rules() that allows for shorter
1223       * list of parameters. See the method for longer description of what generating
1224       * rewrite rules does.
1225       *
1226       * @since 1.5.0
1227       *
1228       * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters.
1229       *
1230       * @param string $permalink_structure The permalink structure to generate rules.
1231       * @param bool   $walk_dirs           Optional, default is false. Whether to create list of directories to walk over.
1232       * @return array
1233       */
1234  	public function generate_rewrite_rule( $permalink_structure, $walk_dirs = false ) {
1235          return $this->generate_rewrite_rules( $permalink_structure, EP_NONE, false, false, false, $walk_dirs );
1236      }
1237  
1238      /**
1239       * Constructs rewrite matches and queries from permalink structure.
1240       *
1241       * Runs the action {@see 'generate_rewrite_rules'} with the parameter that is an
1242       * reference to the current WP_Rewrite instance to further manipulate the
1243       * permalink structures and rewrite rules. Runs the {@see 'rewrite_rules_array'}
1244       * filter on the full rewrite rule array.
1245       *
1246       * There are two ways to manipulate the rewrite rules, one by hooking into
1247       * the {@see 'generate_rewrite_rules'} action and gaining full control of the
1248       * object or just manipulating the rewrite rule array before it is passed
1249       * from the function.
1250       *
1251       * @since 1.5.0
1252       *
1253       * @return array An associate array of matches and queries.
1254       */
1255  	public function rewrite_rules() {
1256          $rewrite = array();
1257  
1258          if ( empty( $this->permalink_structure ) ) {
1259              return $rewrite;
1260          }
1261  
1262          // robots.txt -only if installed at the root
1263          $home_path      = parse_url( home_url() );
1264          $robots_rewrite = ( empty( $home_path['path'] ) || '/' == $home_path['path'] ) ? array( 'robots\.txt$' => $this->index . '?robots=1' ) : array();
1265  
1266          // Old feed and service files.
1267          $deprecated_files = array(
1268              '.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$' => $this->index . '?feed=old',
1269              '.*wp-app\.php(/.*)?$' => $this->index . '?error=403',
1270          );
1271  
1272          // Registration rules.
1273          $registration_pages = array();
1274          if ( is_multisite() && is_main_site() ) {
1275              $registration_pages['.*wp-signup.php$']   = $this->index . '?signup=true';
1276              $registration_pages['.*wp-activate.php$'] = $this->index . '?activate=true';
1277          }
1278  
1279          // Deprecated.
1280          $registration_pages['.*wp-register.php$'] = $this->index . '?register=true';
1281  
1282          // Post rewrite rules.
1283          $post_rewrite = $this->generate_rewrite_rules( $this->permalink_structure, EP_PERMALINK );
1284  
1285          /**
1286           * Filters rewrite rules used for "post" archives.
1287           *
1288           * @since 1.5.0
1289           *
1290           * @param array $post_rewrite The rewrite rules for posts.
1291           */
1292          $post_rewrite = apply_filters( 'post_rewrite_rules', $post_rewrite );
1293  
1294          // Date rewrite rules.
1295          $date_rewrite = $this->generate_rewrite_rules( $this->get_date_permastruct(), EP_DATE );
1296  
1297          /**
1298           * Filters rewrite rules used for date archives.
1299           *
1300           * Likely date archives would include /yyyy/, /yyyy/mm/, and /yyyy/mm/dd/.
1301           *
1302           * @since 1.5.0
1303           *
1304           * @param array $date_rewrite The rewrite rules for date archives.
1305           */
1306          $date_rewrite = apply_filters( 'date_rewrite_rules', $date_rewrite );
1307  
1308          // Root-level rewrite rules.
1309          $root_rewrite = $this->generate_rewrite_rules( $this->root . '/', EP_ROOT );
1310  
1311          /**
1312           * Filters rewrite rules used for root-level archives.
1313           *
1314           * Likely root-level archives would include pagination rules for the homepage
1315           * as well as site-wide post feeds (e.g. /feed/, and /feed/atom/).
1316           *
1317           * @since 1.5.0
1318           *
1319           * @param array $root_rewrite The root-level rewrite rules.
1320           */
1321          $root_rewrite = apply_filters( 'root_rewrite_rules', $root_rewrite );
1322  
1323          // Comments rewrite rules.
1324          $comments_rewrite = $this->generate_rewrite_rules( $this->root . $this->comments_base, EP_COMMENTS, false, true, true, false );
1325  
1326          /**
1327           * Filters rewrite rules used for comment feed archives.
1328           *
1329           * Likely comments feed archives include /comments/feed/, and /comments/feed/atom/.
1330           *
1331           * @since 1.5.0
1332           *
1333           * @param array $comments_rewrite The rewrite rules for the site-wide comments feeds.
1334           */
1335          $comments_rewrite = apply_filters( 'comments_rewrite_rules', $comments_rewrite );
1336  
1337          // Search rewrite rules.
1338          $search_structure = $this->get_search_permastruct();
1339          $search_rewrite   = $this->generate_rewrite_rules( $search_structure, EP_SEARCH );
1340  
1341          /**
1342           * Filters rewrite rules used for search archives.
1343           *
1344           * Likely search-related archives include /search/search+query/ as well as
1345           * pagination and feed paths for a search.
1346           *
1347           * @since 1.5.0
1348           *
1349           * @param array $search_rewrite The rewrite rules for search queries.
1350           */
1351          $search_rewrite = apply_filters( 'search_rewrite_rules', $search_rewrite );
1352  
1353          // Author rewrite rules.
1354          $author_rewrite = $this->generate_rewrite_rules( $this->get_author_permastruct(), EP_AUTHORS );
1355  
1356          /**
1357           * Filters rewrite rules used for author archives.
1358           *
1359           * Likely author archives would include /author/author-name/, as well as
1360           * pagination and feed paths for author archives.
1361           *
1362           * @since 1.5.0
1363           *
1364           * @param array $author_rewrite The rewrite rules for author archives.
1365           */
1366          $author_rewrite = apply_filters( 'author_rewrite_rules', $author_rewrite );
1367  
1368          // Pages rewrite rules.
1369          $page_rewrite = $this->page_rewrite_rules();
1370  
1371          /**
1372           * Filters rewrite rules used for "page" post type archives.
1373           *
1374           * @since 1.5.0
1375           *
1376           * @param array $page_rewrite The rewrite rules for the "page" post type.
1377           */
1378          $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite );
1379  
1380          // Extra permastructs.
1381          foreach ( $this->extra_permastructs as $permastructname => $struct ) {
1382              if ( is_array( $struct ) ) {
1383                  if ( count( $struct ) == 2 ) {
1384                      $rules = $this->generate_rewrite_rules( $struct[0], $struct[1] );
1385                  } else {
1386                      $rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
1387                  }
1388              } else {
1389                  $rules = $this->generate_rewrite_rules( $struct );
1390              }
1391  
1392              /**
1393               * Filters rewrite rules used for individual permastructs.
1394               *
1395               * The dynamic portion of the hook name, `$permastructname`, refers
1396               * to the name of the registered permastruct, e.g. 'post_tag' (tags),
1397               * 'category' (categories), etc.
1398               *
1399               * @since 3.1.0
1400               *
1401               * @param array $rules The rewrite rules generated for the current permastruct.
1402               */
1403              $rules = apply_filters( "{$permastructname}_rewrite_rules", $rules );
1404              if ( 'post_tag' == $permastructname ) {
1405  
1406                  /**
1407                   * Filters rewrite rules used specifically for Tags.
1408                   *
1409                   * @since 2.3.0
1410                   * @deprecated 3.1.0 Use 'post_tag_rewrite_rules' instead
1411                   *
1412                   * @param array $rules The rewrite rules generated for tags.
1413                   */
1414                  $rules = apply_filters( 'tag_rewrite_rules', $rules );
1415              }
1416  
1417              $this->extra_rules_top = array_merge( $this->extra_rules_top, $rules );
1418          }
1419  
1420          // Put them together.
1421          if ( $this->use_verbose_page_rules ) {
1422              $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 );
1423          } else {
1424              $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 );
1425          }
1426  
1427          /**
1428           * Fires after the rewrite rules are generated.
1429           *
1430           * @since 1.5.0
1431           *
1432           * @param WP_Rewrite $this Current WP_Rewrite instance (passed by reference).
1433           */
1434          do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );
1435  
1436          /**
1437           * Filters the full set of generated rewrite rules.
1438           *
1439           * @since 1.5.0
1440           *
1441           * @param array $this->rules The compiled array of rewrite rules.
1442           */
1443          $this->rules = apply_filters( 'rewrite_rules_array', $this->rules );
1444  
1445          return $this->rules;
1446      }
1447  
1448      /**
1449       * Retrieves the rewrite rules.
1450       *
1451       * The difference between this method and WP_Rewrite::rewrite_rules() is that
1452       * this method stores the rewrite rules in the 'rewrite_rules' option and retrieves
1453       * it. This prevents having to process all of the permalinks to get the rewrite rules
1454       * in the form of caching.
1455       *
1456       * @since 1.5.0
1457       *
1458       * @return array Rewrite rules.
1459       */
1460  	public function wp_rewrite_rules() {
1461          $this->rules = get_option( 'rewrite_rules' );
1462          if ( empty( $this->rules ) ) {
1463              $this->matches = 'matches';
1464              $this->rewrite_rules();
1465              if ( ! did_action( 'wp_loaded' ) ) {
1466                  add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
1467                  return $this->rules;
1468              }
1469              update_option( 'rewrite_rules', $this->rules );
1470          }
1471  
1472          return $this->rules;
1473      }
1474  
1475      /**
1476       * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess.
1477       *
1478       * Does not actually write to the .htaccess file, but creates the rules for
1479       * the process that will.
1480       *
1481       * Will add the non_wp_rules property rules to the .htaccess file before
1482       * the WordPress rewrite rules one.
1483       *
1484       * @since 1.5.0
1485       *
1486       * @return string
1487       */
1488  	public function mod_rewrite_rules() {
1489          if ( ! $this->using_permalinks() ) {
1490              return '';
1491          }
1492  
1493          $site_root = parse_url( site_url() );
1494          if ( isset( $site_root['path'] ) ) {
1495              $site_root = trailingslashit( $site_root['path'] );
1496          }
1497  
1498          $home_root = parse_url( home_url() );
1499          if ( isset( $home_root['path'] ) ) {
1500              $home_root = trailingslashit( $home_root['path'] );
1501          } else {
1502              $home_root = '/';
1503          }
1504  
1505          $rules  = "<IfModule mod_rewrite.c>\n";
1506          $rules .= "RewriteEngine On\n";
1507          $rules .= "RewriteBase $home_root\n";
1508  
1509          // Prevent -f checks on index.php.
1510          $rules .= "RewriteRule ^index\.php$ - [L]\n";
1511  
1512          // Add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all).
1513          foreach ( (array) $this->non_wp_rules as $match => $query ) {
1514              // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1515              $match = str_replace( '.+?', '.+', $match );
1516  
1517              $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1518          }
1519  
1520          if ( $this->use_verbose_rules ) {
1521              $this->matches = '';
1522              $rewrite       = $this->rewrite_rules();
1523              $num_rules     = count( $rewrite );
1524              $rules        .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
1525                  "RewriteCond %{REQUEST_FILENAME} -d\n" .
1526                  "RewriteRule ^.*$ - [S=$num_rules]\n";
1527  
1528              foreach ( (array) $rewrite as $match => $query ) {
1529                  // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1530                  $match = str_replace( '.+?', '.+', $match );
1531  
1532                  if ( strpos( $query, $this->index ) !== false ) {
1533                      $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1534                  } else {
1535                      $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
1536                  }
1537              }
1538          } else {
1539              $rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
1540                  "RewriteCond %{REQUEST_FILENAME} !-d\n" .
1541                  "RewriteRule . {$home_root}{$this->index} [L]\n";
1542          }
1543  
1544          $rules .= "</IfModule>\n";
1545  
1546          /**
1547           * Filters the list of rewrite rules formatted for output to an .htaccess file.
1548           *
1549           * @since 1.5.0
1550           *
1551           * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
1552           */
1553          $rules = apply_filters( 'mod_rewrite_rules', $rules );
1554  
1555          /**
1556           * Filters the list of rewrite rules formatted for output to an .htaccess file.
1557           *
1558           * @since 1.5.0
1559           * @deprecated 1.5.0 Use the mod_rewrite_rules filter instead.
1560           *
1561           * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
1562           */
1563          return apply_filters( 'rewrite_rules', $rules );
1564      }
1565  
1566      /**
1567       * Retrieves IIS7 URL Rewrite formatted rewrite rules to write to web.config file.
1568       *
1569       * Does not actually write to the web.config file, but creates the rules for
1570       * the process that will.
1571       *
1572       * @since 2.8.0
1573       *
1574       * @param bool $add_parent_tags Optional. Whether to add parent tags to the rewrite rule sets.
1575       *                              Default false.
1576       * @return string IIS7 URL rewrite rule sets.
1577       */
1578  	public function iis7_url_rewrite_rules( $add_parent_tags = false ) {
1579          if ( ! $this->using_permalinks() ) {
1580              return '';
1581          }
1582          $rules = '';
1583          if ( $add_parent_tags ) {
1584              $rules .= '<configuration>
1585      <system.webServer>
1586          <rewrite>
1587              <rules>';
1588          }
1589  
1590          $rules .= '
1591              <rule name="WordPress: ' . esc_attr( home_url() ) . '" patternSyntax="Wildcard">
1592                  <match url="*" />
1593                      <conditions>
1594                          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
1595                          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
1596                      </conditions>
1597                  <action type="Rewrite" url="index.php" />
1598              </rule>';
1599  
1600          if ( $add_parent_tags ) {
1601              $rules .= '
1602              </rules>
1603          </rewrite>
1604      </system.webServer>
1605  </configuration>';
1606          }
1607  
1608          /**
1609           * Filters the list of rewrite rules formatted for output to a web.config.
1610           *
1611           * @since 2.8.0
1612           *
1613           * @param string $rules Rewrite rules formatted for IIS web.config.
1614           */
1615          return apply_filters( 'iis7_url_rewrite_rules', $rules );
1616      }
1617  
1618      /**
1619       * Adds a rewrite rule that transforms a URL structure to a set of query vars.
1620       *
1621       * Any value in the $after parameter that isn't 'bottom' will result in the rule
1622       * being placed at the top of the rewrite rules.
1623       *
1624       * @since 2.1.0
1625       * @since 4.4.0 Array support was added to the `$query` parameter.
1626       *
1627       * @param string       $regex Regular expression to match request against.
1628       * @param string|array $query The corresponding query vars for this rewrite rule.
1629       * @param string       $after Optional. Priority of the new rule. Accepts 'top'
1630       *                            or 'bottom'. Default 'bottom'.
1631       */
1632  	public function add_rule( $regex, $query, $after = 'bottom' ) {
1633          if ( is_array( $query ) ) {
1634              $external = false;
1635              $query    = add_query_arg( $query, 'index.php' );
1636          } else {
1637              $index = false === strpos( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' );
1638              $front = substr( $query, 0, $index );
1639  
1640              $external = $front != $this->index;
1641          }
1642  
1643          // "external" = it doesn't correspond to index.php.
1644          if ( $external ) {
1645              $this->add_external_rule( $regex, $query );
1646          } else {
1647              if ( 'bottom' == $after ) {
1648                  $this->extra_rules = array_merge( $this->extra_rules, array( $regex => $query ) );
1649              } else {
1650                  $this->extra_rules_top = array_merge( $this->extra_rules_top, array( $regex => $query ) );
1651              }
1652          }
1653      }
1654  
1655      /**
1656       * Adds a rewrite rule that doesn't correspond to index.php.
1657       *
1658       * @since 2.1.0
1659       *
1660       * @param string $regex Regular expression to match request against.
1661       * @param string $query The corresponding query vars for this rewrite rule.
1662       */
1663  	public function add_external_rule( $regex, $query ) {
1664          $this->non_wp_rules[ $regex ] = $query;
1665      }
1666  
1667      /**
1668       * Adds an endpoint, like /trackback/.
1669       *
1670       * @since 2.1.0
1671       * @since 3.9.0 $query_var parameter added.
1672       * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
1673       *
1674       * @see add_rewrite_endpoint() for full documentation.
1675       * @global WP $wp Current WordPress environment instance.
1676       *
1677       * @param string      $name      Name of the endpoint.
1678       * @param int         $places    Endpoint mask describing the places the endpoint should be added.
1679       * @param string|bool $query_var Optional. Name of the corresponding query variable. Pass `false` to
1680       *                               skip registering a query_var for this endpoint. Defaults to the
1681       *                               value of `$name`.
1682       */
1683  	public function add_endpoint( $name, $places, $query_var = true ) {
1684          global $wp;
1685  
1686          // For backward compatibility, if null has explicitly been passed as `$query_var`, assume `true`.
1687          if ( true === $query_var || null === $query_var ) {
1688              $query_var = $name;
1689          }
1690          $this->endpoints[] = array( $places, $name, $query_var );
1691  
1692          if ( $query_var ) {
1693              $wp->add_query_var( $query_var );
1694          }
1695      }
1696  
1697      /**
1698       * Adds a new permalink structure.
1699       *
1700       * A permalink structure (permastruct) is an abstract definition of a set of rewrite rules;
1701       * it is an easy way of expressing a set of regular expressions that rewrite to a set of
1702       * query strings. The new permastruct is added to the WP_Rewrite::$extra_permastructs array.
1703       *
1704       * When the rewrite rules are built by WP_Rewrite::rewrite_rules(), all of these extra
1705       * permastructs are passed to WP_Rewrite::generate_rewrite_rules() which transforms them
1706       * into the regular expressions that many love to hate.
1707       *
1708       * The `$args` parameter gives you control over how WP_Rewrite::generate_rewrite_rules()
1709       * works on the new permastruct.
1710       *
1711       * @since 2.5.0
1712       *
1713       * @param string $name   Name for permalink structure.
1714       * @param string $struct Permalink structure (e.g. category/%category%)
1715       * @param array  $args   {
1716       *     Optional. Arguments for building rewrite rules based on the permalink structure.
1717       *     Default empty array.
1718       *
1719       *     @type bool $with_front  Whether the structure should be prepended with `WP_Rewrite::$front`.
1720       *                             Default true.
1721       *     @type int  $ep_mask     The endpoint mask defining which endpoints are added to the structure.
1722       *                             Accepts `EP_NONE`, `EP_PERMALINK`, `EP_ATTACHMENT`, `EP_DATE`, `EP_YEAR`,
1723       *                             `EP_MONTH`, `EP_DAY`, `EP_ROOT`, `EP_COMMENTS`, `EP_SEARCH`, `EP_CATEGORIES`,
1724       *                             `EP_TAGS`, `EP_AUTHORS`, `EP_PAGES`, `EP_ALL_ARCHIVES`, and `EP_ALL`.
1725       *                             Default `EP_NONE`.
1726       *     @type bool $paged       Whether archive pagination rules should be added for the structure.
1727       *                             Default true.
1728       *     @type bool $feed        Whether feed rewrite rules should be added for the structure. Default true.
1729       *     @type bool $forcomments Whether the feed rules should be a query for a comments feed. Default false.
1730       *     @type bool $walk_dirs   Whether the 'directories' making up the structure should be walked over
1731       *                             and rewrite rules built for each in-turn. Default true.
1732       *     @type bool $endpoints   Whether endpoints should be applied to the generated rules. Default true.
1733       * }
1734       */
1735  	public function add_permastruct( $name, $struct, $args = array() ) {
1736          // Back-compat for the old parameters: $with_front and $ep_mask.
1737          if ( ! is_array( $args ) ) {
1738              $args = array( 'with_front' => $args );
1739          }
1740          if ( func_num_args() == 4 ) {
1741              $args['ep_mask'] = func_get_arg( 3 );
1742          }
1743  
1744          $defaults = array(
1745              'with_front'  => true,
1746              'ep_mask'     => EP_NONE,
1747              'paged'       => true,
1748              'feed'        => true,
1749              'forcomments' => false,
1750              'walk_dirs'   => true,
1751              'endpoints'   => true,
1752          );
1753          $args     = array_intersect_key( $args, $defaults );
1754          $args     = wp_parse_args( $args, $defaults );
1755  
1756          if ( $args['with_front'] ) {
1757              $struct = $this->front . $struct;
1758          } else {
1759              $struct = $this->root . $struct;
1760          }
1761          $args['struct'] = $struct;
1762  
1763          $this->extra_permastructs[ $name ] = $args;
1764      }
1765  
1766      /**
1767       * Removes a permalink structure.
1768       *
1769       * @since 4.5.0
1770       *
1771       * @param string $name Name for permalink structure.
1772       */
1773  	public function remove_permastruct( $name ) {
1774          unset( $this->extra_permastructs[ $name ] );
1775      }
1776  
1777      /**
1778       * Removes rewrite rules and then recreate rewrite rules.
1779       *
1780       * Calls WP_Rewrite::wp_rewrite_rules() after removing the 'rewrite_rules' option.
1781       * If the function named 'save_mod_rewrite_rules' exists, it will be called.
1782       *
1783       * @since 2.0.1
1784       *
1785       * @staticvar bool $do_hard_later
1786       *
1787       * @param bool $hard Whether to update .htaccess (hard flush) or just update rewrite_rules option (soft flush). Default is true (hard).
1788       */
1789  	public function flush_rules( $hard = true ) {
1790          static $do_hard_later = null;
1791  
1792          // Prevent this action from running before everyone has registered their rewrites.
1793          if ( ! did_action( 'wp_loaded' ) ) {
1794              add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
1795              $do_hard_later = ( isset( $do_hard_later ) ) ? $do_hard_later || $hard : $hard;
1796              return;
1797          }
1798  
1799          if ( isset( $do_hard_later ) ) {
1800              $hard = $do_hard_later;
1801              unset( $do_hard_later );
1802          }
1803  
1804          update_option( 'rewrite_rules', '' );
1805          $this->wp_rewrite_rules();
1806  
1807          /**
1808           * Filters whether a "hard" rewrite rule flush should be performed when requested.
1809           *
1810           * A "hard" flush updates .htaccess (Apache) or web.config (IIS).
1811           *
1812           * @since 3.7.0
1813           *
1814           * @param bool $hard Whether to flush rewrite rules "hard". Default true.
1815           */
1816          if ( ! $hard || ! apply_filters( 'flush_rewrite_rules_hard', true ) ) {
1817              return;
1818          }
1819          if ( function_exists( 'save_mod_rewrite_rules' ) ) {
1820              save_mod_rewrite_rules();
1821          }
1822          if ( function_exists( 'iis7_save_url_rewrite_rules' ) ) {
1823              iis7_save_url_rewrite_rules();
1824          }
1825      }
1826  
1827      /**
1828       * Sets up the object's properties.
1829       *
1830       * The 'use_verbose_page_rules' object property will be set to true if the
1831       * permalink structure begins with one of the following: '%postname%', '%category%',
1832       * '%tag%', or '%author%'.
1833       *
1834       * @since 1.5.0
1835       */
1836  	public function init() {
1837          $this->extra_rules         = array();
1838          $this->non_wp_rules        = array();
1839          $this->endpoints           = array();
1840          $this->permalink_structure = get_option( 'permalink_structure' );
1841          $this->front               = substr( $this->permalink_structure, 0, strpos( $this->permalink_structure, '%' ) );
1842          $this->root                = '';
1843  
1844          if ( $this->using_index_permalinks() ) {
1845              $this->root = $this->index . '/';
1846          }
1847  
1848          unset( $this->author_structure );
1849          unset( $this->date_structure );
1850          unset( $this->page_structure );
1851          unset( $this->search_structure );
1852          unset( $this->feed_structure );
1853          unset( $this->comment_feed_structure );
1854          $this->use_trailing_slashes = ( '/' == substr( $this->permalink_structure, -1, 1 ) );
1855  
1856          // Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
1857          if ( preg_match( '/^[^%]*%(?:postname|category|tag|author)%/', $this->permalink_structure ) ) {
1858              $this->use_verbose_page_rules = true;
1859          } else {
1860              $this->use_verbose_page_rules = false;
1861          }
1862      }
1863  
1864      /**
1865       * Sets the main permalink structure for the site.
1866       *
1867       * Will update the 'permalink_structure' option, if there is a difference
1868       * between the current permalink structure and the parameter value. Calls
1869       * WP_Rewrite::init() after the option is updated.
1870       *
1871       * Fires the {@see 'permalink_structure_changed'} action once the init call has
1872       * processed passing the old and new values
1873       *
1874       * @since 1.5.0
1875       *
1876       * @param string $permalink_structure Permalink structure.
1877       */
1878  	public function set_permalink_structure( $permalink_structure ) {
1879          if ( $permalink_structure != $this->permalink_structure ) {
1880              $old_permalink_structure = $this->permalink_structure;
1881              update_option( 'permalink_structure', $permalink_structure );
1882  
1883              $this->init();
1884  
1885              /**
1886               * Fires after the permalink structure is updated.
1887               *
1888               * @since 2.8.0
1889               *
1890               * @param string $old_permalink_structure The previous permalink structure.
1891               * @param string $permalink_structure     The new permalink structure.
1892               */
1893              do_action( 'permalink_structure_changed', $old_permalink_structure, $permalink_structure );
1894          }
1895      }
1896  
1897      /**
1898       * Sets the category base for the category permalink.
1899       *
1900       * Will update the 'category_base' option, if there is a difference between
1901       * the current category base and the parameter value. Calls WP_Rewrite::init()
1902       * after the option is updated.
1903       *
1904       * @since 1.5.0
1905       *
1906       * @param string $category_base Category permalink structure base.
1907       */
1908  	public function set_category_base( $category_base ) {
1909          if ( $category_base != get_option( 'category_base' ) ) {
1910              update_option( 'category_base', $category_base );
1911              $this->init();
1912          }
1913      }
1914  
1915      /**
1916       * Sets the tag base for the tag permalink.
1917       *
1918       * Will update the 'tag_base' option, if there is a difference between the
1919       * current tag base and the parameter value. Calls WP_Rewrite::init() after
1920       * the option is updated.
1921       *
1922       * @since 2.3.0
1923       *
1924       * @param string $tag_base Tag permalink structure base.
1925       */
1926  	public function set_tag_base( $tag_base ) {
1927          if ( $tag_base != get_option( 'tag_base' ) ) {
1928              update_option( 'tag_base', $tag_base );
1929              $this->init();
1930          }
1931      }
1932  
1933      /**
1934       * Constructor - Calls init(), which runs setup.
1935       *
1936       * @since 1.5.0
1937       */
1938  	public function __construct() {
1939          $this->init();
1940      }
1941  }


Generated: Tue Oct 22 08:20:01 2019 Cross-referenced by PHPXref 0.7