[ Index ]

PHP Cross Reference of WordPress

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      public $author_base = 'author';
  49  
  50      /**
  51       * Permalink structure for author archives.
  52       *
  53       * @since 1.5.0
  54       * @var string
  55       */
  56      public $author_structure;
  57  
  58      /**
  59       * Permalink structure for date archives.
  60       *
  61       * @since 1.5.0
  62       * @var string
  63       */
  64      public $date_structure;
  65  
  66      /**
  67       * Permalink structure for pages.
  68       *
  69       * @since 1.5.0
  70       * @var string
  71       */
  72      public $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      public $search_base = 'search';
  81  
  82      /**
  83       * Permalink structure for searches.
  84       *
  85       * @since 1.5.0
  86       * @var string
  87       */
  88      public $search_structure;
  89  
  90      /**
  91       * Comments permalink base.
  92       *
  93       * @since 1.5.0
  94       * @var string
  95       */
  96      public $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      public $comments_pagination_base = 'comment-page';
 113  
 114      /**
 115       * Feed permalink base.
 116       *
 117       * @since 1.5.0
 118       * @var string
 119       */
 120      public $feed_base = 'feed';
 121  
 122      /**
 123       * Comments feed permalink structure.
 124       *
 125       * @since 1.5.0
 126       * @var string
 127       */
 128      public $comment_feed_structure;
 129  
 130      /**
 131       * Feed request permalink structure.
 132       *
 133       * @since 1.5.0
 134       * @var string
 135       */
 136      public $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      public $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      public $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      public $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      public $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      public $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      public $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      public $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 string[]
 279       */
 280      public $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 string[]
 300       */
 301      public $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 string[]
 320       */
 321      public $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 string[]
 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 string[] 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 Date permalink structure on success, false on failure.
 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 string|false Year permalink structure on success, false on failure.
 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 string|false Year/Month permalink structure on success, false on failure.
 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 Year/Month/Day permalink structure on success, false on failure.
 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 Category permalink structure on success, false on failure.
 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 Tag permalink structure on success, false on failure.
 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 Permalink structure string on success, false on failure.
 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 Author permalink structure on success, false on failure.
 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 Search permalink structure on success, false on failure.
 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 Page permalink structure on success, false on failure.
 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 Feed permalink structure on success, false on failure.
 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 Comment feed permalink structure on success, false on failure.
 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, true );
 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, true );
 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 a mask of:
 844       *                                    - `EP_ALL`
 845       *                                    - `EP_NONE`
 846       *                                    - `EP_ALL_ARCHIVES`
 847       *                                    - `EP_ATTACHMENT`
 848       *                                    - `EP_AUTHORS`
 849       *                                    - `EP_CATEGORIES`
 850       *                                    - `EP_COMMENTS`
 851       *                                    - `EP_DATE`
 852       *                                    - `EP_DAY`
 853       *                                    - `EP_MONTH`
 854       *                                    - `EP_PAGES`
 855       *                                    - `EP_PERMALINK`
 856       *                                    - `EP_ROOT`
 857       *                                    - `EP_SEARCH`
 858       *                                    - `EP_TAGS`
 859       *                                    - `EP_YEAR`
 860       *                                    Default `EP_NONE`.
 861       * @param bool   $paged               Optional. Whether archive pagination rules should be added for the structure.
 862       *                                    Default true.
 863       * @param bool   $feed                Optional Whether feed rewrite rules should be added for the structure.
 864       *                                    Default true.
 865       * @param bool   $forcomments         Optional. Whether the feed rules should be a query for a comments feed.
 866       *                                    Default false.
 867       * @param bool   $walk_dirs           Optional. Whether the 'directories' making up the structure should be walked
 868       *                                    over and rewrite rules built for each in-turn. Default true.
 869       * @param bool   $endpoints           Optional. Whether endpoints should be applied to the generated rewrite rules.
 870       *                                    Default true.
 871       * @return string[] Array of rewrite rules keyed by their regex pattern.
 872       */
 873  	public function generate_rewrite_rules( $permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true ) {
 874          // Build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
 875          $feedregex2 = '';
 876          foreach ( (array) $this->feeds as $feed_name ) {
 877              $feedregex2 .= $feed_name . '|';
 878          }
 879          $feedregex2 = '(' . trim( $feedregex2, '|' ) . ')/?$';
 880  
 881          /*
 882           * $feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
 883           * and <permalink>/atom are both possible
 884           */
 885          $feedregex = $this->feed_base . '/' . $feedregex2;
 886  
 887          // Build a regex to match the trackback and page/xx parts of URLs.
 888          $trackbackregex = 'trackback/?$';
 889          $pageregex      = $this->pagination_base . '/?([0-9]{1,})/?$';
 890          $commentregex   = $this->comments_pagination_base . '-([0-9]{1,})/?$';
 891          $embedregex     = 'embed/?$';
 892  
 893          // Build up an array of endpoint regexes to append => queries to append.
 894          if ( $endpoints ) {
 895              $ep_query_append = array();
 896              foreach ( (array) $this->endpoints as $endpoint ) {
 897                  // Match everything after the endpoint name, but allow for nothing to appear there.
 898                  $epmatch = $endpoint[1] . '(/(.*))?/?$';
 899  
 900                  // This will be appended on to the rest of the query for each dir.
 901                  $epquery                     = '&' . $endpoint[2] . '=';
 902                  $ep_query_append[ $epmatch ] = array( $endpoint[0], $epquery );
 903              }
 904          }
 905  
 906          // Get everything up to the first rewrite tag.
 907          $front = substr( $permalink_structure, 0, strpos( $permalink_structure, '%' ) );
 908  
 909          // Build an array of the tags (note that said array ends up being in $tokens[0]).
 910          preg_match_all( '/%.+?%/', $permalink_structure, $tokens );
 911  
 912          $num_tokens = count( $tokens[0] );
 913  
 914          $index          = $this->index; // Probably 'index.php'.
 915          $feedindex      = $index;
 916          $trackbackindex = $index;
 917          $embedindex     = $index;
 918  
 919          /*
 920           * Build a list from the rewritecode and queryreplace arrays, that will look something
 921           * like tagname=$matches[i] where i is the current $i.
 922           */
 923          $queries = array();
 924          for ( $i = 0; $i < $num_tokens; ++$i ) {
 925              if ( 0 < $i ) {
 926                  $queries[ $i ] = $queries[ $i - 1 ] . '&';
 927              } else {
 928                  $queries[ $i ] = '';
 929              }
 930  
 931              $query_token    = str_replace( $this->rewritecode, $this->queryreplace, $tokens[0][ $i ] ) . $this->preg_index( $i + 1 );
 932              $queries[ $i ] .= $query_token;
 933          }
 934  
 935          // Get the structure, minus any cruft (stuff that isn't tags) at the front.
 936          $structure = $permalink_structure;
 937          if ( '/' !== $front ) {
 938              $structure = str_replace( $front, '', $structure );
 939          }
 940  
 941          /*
 942           * Create a list of dirs to walk over, making rewrite rules for each level
 943           * so for example, a $structure of /%year%/%monthnum%/%postname% would create
 944           * rewrite rules for /%year%/, /%year%/%monthnum%/ and /%year%/%monthnum%/%postname%
 945           */
 946          $structure = trim( $structure, '/' );
 947          $dirs      = $walk_dirs ? explode( '/', $structure ) : array( $structure );
 948          $num_dirs  = count( $dirs );
 949  
 950          // Strip slashes from the front of $front.
 951          $front = preg_replace( '|^/+|', '', $front );
 952  
 953          // The main workhorse loop.
 954          $post_rewrite = array();
 955          $struct       = $front;
 956          for ( $j = 0; $j < $num_dirs; ++$j ) {
 957              // Get the struct for this dir, and trim slashes off the front.
 958              $struct .= $dirs[ $j ] . '/'; // Accumulate. see comment near explode('/', $structure) above.
 959              $struct  = ltrim( $struct, '/' );
 960  
 961              // Replace tags with regexes.
 962              $match = str_replace( $this->rewritecode, $this->rewritereplace, $struct );
 963  
 964              // Make a list of tags, and store how many there are in $num_toks.
 965              $num_toks = preg_match_all( '/%.+?%/', $struct, $toks );
 966  
 967              // Get the 'tagname=$matches[i]'.
 968              $query = ( ! empty( $num_toks ) && isset( $queries[ $num_toks - 1 ] ) ) ? $queries[ $num_toks - 1 ] : '';
 969  
 970              // Set up $ep_mask_specific which is used to match more specific URL types.
 971              switch ( $dirs[ $j ] ) {
 972                  case '%year%':
 973                      $ep_mask_specific = EP_YEAR;
 974                      break;
 975                  case '%monthnum%':
 976                      $ep_mask_specific = EP_MONTH;
 977                      break;
 978                  case '%day%':
 979                      $ep_mask_specific = EP_DAY;
 980                      break;
 981                  default:
 982                      $ep_mask_specific = EP_NONE;
 983              }
 984  
 985              // Create query for /page/xx.
 986              $pagematch = $match . $pageregex;
 987              $pagequery = $index . '?' . $query . '&paged=' . $this->preg_index( $num_toks + 1 );
 988  
 989              // Create query for /comment-page-xx.
 990              $commentmatch = $match . $commentregex;
 991              $commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index( $num_toks + 1 );
 992  
 993              if ( get_option( 'page_on_front' ) ) {
 994                  // Create query for Root /comment-page-xx.
 995                  $rootcommentmatch = $match . $commentregex;
 996                  $rootcommentquery = $index . '?' . $query . '&page_id=' . get_option( 'page_on_front' ) . '&cpage=' . $this->preg_index( $num_toks + 1 );
 997              }
 998  
 999              // Create query for /feed/(feed|atom|rss|rss2|rdf).
1000              $feedmatch = $match . $feedregex;
1001              $feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );
1002  
1003              // Create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex).
1004              $feedmatch2 = $match . $feedregex2;
1005              $feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index( $num_toks + 1 );
1006  
1007              // Create query and regex for embeds.
1008              $embedmatch = $match . $embedregex;
1009              $embedquery = $embedindex . '?' . $query . '&embed=true';
1010  
1011              // If asked to, turn the feed queries into comment feed ones.
1012              if ( $forcomments ) {
1013                  $feedquery  .= '&withcomments=1';
1014                  $feedquery2 .= '&withcomments=1';
1015              }
1016  
1017              // Start creating the array of rewrites for this dir.
1018              $rewrite = array();
1019  
1020              // ...adding on /feed/ regexes => queries.
1021              if ( $feed ) {
1022                  $rewrite = array(
1023                      $feedmatch  => $feedquery,
1024                      $feedmatch2 => $feedquery2,
1025                      $embedmatch => $embedquery,
1026                  );
1027              }
1028  
1029              // ...and /page/xx ones.
1030              if ( $paged ) {
1031                  $rewrite = array_merge( $rewrite, array( $pagematch => $pagequery ) );
1032              }
1033  
1034              // Only on pages with comments add ../comment-page-xx/.
1035              if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask ) {
1036                  $rewrite = array_merge( $rewrite, array( $commentmatch => $commentquery ) );
1037              } elseif ( EP_ROOT & $ep_mask && get_option( 'page_on_front' ) ) {
1038                  $rewrite = array_merge( $rewrite, array( $rootcommentmatch => $rootcommentquery ) );
1039              }
1040  
1041              // Do endpoints.
1042              if ( $endpoints ) {
1043                  foreach ( (array) $ep_query_append as $regex => $ep ) {
1044                      // Add the endpoints on if the mask fits.
1045                      if ( $ep[0] & $ep_mask || $ep[0] & $ep_mask_specific ) {
1046                          $rewrite[ $match . $regex ] = $index . '?' . $query . $ep[1] . $this->preg_index( $num_toks + 2 );
1047                      }
1048                  }
1049              }
1050  
1051              // If we've got some tags in this dir.
1052              if ( $num_toks ) {
1053                  $post = false;
1054                  $page = false;
1055  
1056                  /*
1057                   * Check to see if this dir is permalink-level: i.e. the structure specifies an
1058                   * individual post. Do this by checking it contains at least one of 1) post name,
1059                   * 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
1060                   * minute all present). Set these flags now as we need them for the endpoints.
1061                   */
1062                  if ( strpos( $struct, '%postname%' ) !== false
1063                          || strpos( $struct, '%post_id%' ) !== false
1064                          || strpos( $struct, '%pagename%' ) !== false
1065                          || ( strpos( $struct, '%year%' ) !== false && strpos( $struct, '%monthnum%' ) !== false && strpos( $struct, '%day%' ) !== false && strpos( $struct, '%hour%' ) !== false && strpos( $struct, '%minute%' ) !== false && strpos( $struct, '%second%' ) !== false )
1066                          ) {
1067                      $post = true;
1068                      if ( strpos( $struct, '%pagename%' ) !== false ) {
1069                          $page = true;
1070                      }
1071                  }
1072  
1073                  if ( ! $post ) {
1074                      // For custom post types, we need to add on endpoints as well.
1075                      foreach ( get_post_types( array( '_builtin' => false ) ) as $ptype ) {
1076                          if ( strpos( $struct, "%$ptype%" ) !== false ) {
1077                              $post = true;
1078  
1079                              // This is for page style attachment URLs.
1080                              $page = is_post_type_hierarchical( $ptype );
1081                              break;
1082                          }
1083                      }
1084                  }
1085  
1086                  // If creating rules for a permalink, do all the endpoints like attachments etc.
1087                  if ( $post ) {
1088                      // Create query and regex for trackback.
1089                      $trackbackmatch = $match . $trackbackregex;
1090                      $trackbackquery = $trackbackindex . '?' . $query . '&tb=1';
1091  
1092                      // Create query and regex for embeds.
1093                      $embedmatch = $match . $embedregex;
1094                      $embedquery = $embedindex . '?' . $query . '&embed=true';
1095  
1096                      // Trim slashes from the end of the regex for this dir.
1097                      $match = rtrim( $match, '/' );
1098  
1099                      // Get rid of brackets.
1100                      $submatchbase = str_replace( array( '(', ')' ), '', $match );
1101  
1102                      // Add a rule for at attachments, which take the form of <permalink>/some-text.
1103                      $sub1 = $submatchbase . '/([^/]+)/';
1104  
1105                      // Add trackback regex <permalink>/trackback/...
1106                      $sub1tb = $sub1 . $trackbackregex;
1107  
1108                      // And <permalink>/feed/(atom|...)
1109                      $sub1feed = $sub1 . $feedregex;
1110  
1111                      // And <permalink>/(feed|atom...)
1112                      $sub1feed2 = $sub1 . $feedregex2;
1113  
1114                      // And <permalink>/comment-page-xx
1115                      $sub1comment = $sub1 . $commentregex;
1116  
1117                      // And <permalink>/embed/...
1118                      $sub1embed = $sub1 . $embedregex;
1119  
1120                      /*
1121                       * Add another rule to match attachments in the explicit form:
1122                       * <permalink>/attachment/some-text
1123                       */
1124                      $sub2 = $submatchbase . '/attachment/([^/]+)/';
1125  
1126                      // And add trackbacks <permalink>/attachment/trackback.
1127                      $sub2tb = $sub2 . $trackbackregex;
1128  
1129                      // Feeds, <permalink>/attachment/feed/(atom|...)
1130                      $sub2feed = $sub2 . $feedregex;
1131  
1132                      // And feeds again on to this <permalink>/attachment/(feed|atom...)
1133                      $sub2feed2 = $sub2 . $feedregex2;
1134  
1135                      // And <permalink>/comment-page-xx
1136                      $sub2comment = $sub2 . $commentregex;
1137  
1138                      // And <permalink>/embed/...
1139                      $sub2embed = $sub2 . $embedregex;
1140  
1141                      // Create queries for these extra tag-ons we've just dealt with.
1142                      $subquery        = $index . '?attachment=' . $this->preg_index( 1 );
1143                      $subtbquery      = $subquery . '&tb=1';
1144                      $subfeedquery    = $subquery . '&feed=' . $this->preg_index( 2 );
1145                      $subcommentquery = $subquery . '&cpage=' . $this->preg_index( 2 );
1146                      $subembedquery   = $subquery . '&embed=true';
1147  
1148                      // Do endpoints for attachments.
1149                      if ( ! empty( $endpoints ) ) {
1150                          foreach ( (array) $ep_query_append as $regex => $ep ) {
1151                              if ( $ep[0] & EP_ATTACHMENT ) {
1152                                  $rewrite[ $sub1 . $regex ] = $subquery . $ep[1] . $this->preg_index( 3 );
1153                                  $rewrite[ $sub2 . $regex ] = $subquery . $ep[1] . $this->preg_index( 3 );
1154                              }
1155                          }
1156                      }
1157  
1158                      /*
1159                       * Now we've finished with endpoints, finish off the $sub1 and $sub2 matches
1160                       * add a ? as we don't have to match that last slash, and finally a $ so we
1161                       * match to the end of the URL
1162                       */
1163                      $sub1 .= '?$';
1164                      $sub2 .= '?$';
1165  
1166                      /*
1167                       * Post pagination, e.g. <permalink>/2/
1168                       * Previously: '(/[0-9]+)?/?$', which produced '/2' for page.
1169                       * When cast to int, returned 0.
1170                       */
1171                      $match = $match . '(?:/([0-9]+))?/?$';
1172                      $query = $index . '?' . $query . '&page=' . $this->preg_index( $num_toks + 1 );
1173  
1174                      // Not matching a permalink so this is a lot simpler.
1175                  } else {
1176                      // Close the match and finalize the query.
1177                      $match .= '?$';
1178                      $query  = $index . '?' . $query;
1179                  }
1180  
1181                  /*
1182                   * Create the final array for this dir by joining the $rewrite array (which currently
1183                   * only contains rules/queries for trackback, pages etc) to the main regex/query for
1184                   * this dir
1185                   */
1186                  $rewrite = array_merge( $rewrite, array( $match => $query ) );
1187  
1188                  // If we're matching a permalink, add those extras (attachments etc) on.
1189                  if ( $post ) {
1190                      // Add trackback.
1191                      $rewrite = array_merge( array( $trackbackmatch => $trackbackquery ), $rewrite );
1192  
1193                      // Add embed.
1194                      $rewrite = array_merge( array( $embedmatch => $embedquery ), $rewrite );
1195  
1196                      // Add regexes/queries for attachments, attachment trackbacks and so on.
1197                      if ( ! $page ) {
1198                          // Require <permalink>/attachment/stuff form for pages because of confusion with subpages.
1199                          $rewrite = array_merge(
1200                              $rewrite,
1201                              array(
1202                                  $sub1        => $subquery,
1203                                  $sub1tb      => $subtbquery,
1204                                  $sub1feed    => $subfeedquery,
1205                                  $sub1feed2   => $subfeedquery,
1206                                  $sub1comment => $subcommentquery,
1207                                  $sub1embed   => $subembedquery,
1208                              )
1209                          );
1210                      }
1211  
1212                      $rewrite = array_merge(
1213                          array(
1214                              $sub2        => $subquery,
1215                              $sub2tb      => $subtbquery,
1216                              $sub2feed    => $subfeedquery,
1217                              $sub2feed2   => $subfeedquery,
1218                              $sub2comment => $subcommentquery,
1219                              $sub2embed   => $subembedquery,
1220                          ),
1221                          $rewrite
1222                      );
1223                  }
1224              }
1225              // Add the rules for this dir to the accumulating $post_rewrite.
1226              $post_rewrite = array_merge( $rewrite, $post_rewrite );
1227          }
1228  
1229          // The finished rules. phew!
1230          return $post_rewrite;
1231      }
1232  
1233      /**
1234       * Generates rewrite rules with permalink structure and walking directory only.
1235       *
1236       * Shorten version of WP_Rewrite::generate_rewrite_rules() that allows for shorter
1237       * list of parameters. See the method for longer description of what generating
1238       * rewrite rules does.
1239       *
1240       * @since 1.5.0
1241       *
1242       * @see WP_Rewrite::generate_rewrite_rules() See for long description and rest of parameters.
1243       *
1244       * @param string $permalink_structure The permalink structure to generate rules.
1245       * @param bool   $walk_dirs           Optional. Whether to create list of directories to walk over.
1246       *                                    Default false.
1247       * @return array
1248       */
1249  	public function generate_rewrite_rule( $permalink_structure, $walk_dirs = false ) {
1250          return $this->generate_rewrite_rules( $permalink_structure, EP_NONE, false, false, false, $walk_dirs );
1251      }
1252  
1253      /**
1254       * Constructs rewrite matches and queries from permalink structure.
1255       *
1256       * Runs the action {@see 'generate_rewrite_rules'} with the parameter that is an
1257       * reference to the current WP_Rewrite instance to further manipulate the
1258       * permalink structures and rewrite rules. Runs the {@see 'rewrite_rules_array'}
1259       * filter on the full rewrite rule array.
1260       *
1261       * There are two ways to manipulate the rewrite rules, one by hooking into
1262       * the {@see 'generate_rewrite_rules'} action and gaining full control of the
1263       * object or just manipulating the rewrite rule array before it is passed
1264       * from the function.
1265       *
1266       * @since 1.5.0
1267       *
1268       * @return string[] An associative array of matches and queries.
1269       */
1270  	public function rewrite_rules() {
1271          $rewrite = array();
1272  
1273          if ( empty( $this->permalink_structure ) ) {
1274              return $rewrite;
1275          }
1276  
1277          // robots.txt -- only if installed at the root.
1278          $home_path      = parse_url( home_url() );
1279          $robots_rewrite = ( empty( $home_path['path'] ) || '/' === $home_path['path'] ) ? array( 'robots\.txt$' => $this->index . '?robots=1' ) : array();
1280  
1281          // favicon.ico -- only if installed at the root.
1282          $favicon_rewrite = ( empty( $home_path['path'] ) || '/' === $home_path['path'] ) ? array( 'favicon\.ico$' => $this->index . '?favicon=1' ) : array();
1283  
1284          // Old feed and service files.
1285          $deprecated_files = array(
1286              '.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$' => $this->index . '?feed=old',
1287              '.*wp-app\.php(/.*)?$' => $this->index . '?error=403',
1288          );
1289  
1290          // Registration rules.
1291          $registration_pages = array();
1292          if ( is_multisite() && is_main_site() ) {
1293              $registration_pages['.*wp-signup.php$']   = $this->index . '?signup=true';
1294              $registration_pages['.*wp-activate.php$'] = $this->index . '?activate=true';
1295          }
1296  
1297          // Deprecated.
1298          $registration_pages['.*wp-register.php$'] = $this->index . '?register=true';
1299  
1300          // Post rewrite rules.
1301          $post_rewrite = $this->generate_rewrite_rules( $this->permalink_structure, EP_PERMALINK );
1302  
1303          /**
1304           * Filters rewrite rules used for "post" archives.
1305           *
1306           * @since 1.5.0
1307           *
1308           * @param string[] $post_rewrite Array of rewrite rules for posts, keyed by their regex pattern.
1309           */
1310          $post_rewrite = apply_filters( 'post_rewrite_rules', $post_rewrite );
1311  
1312          // Date rewrite rules.
1313          $date_rewrite = $this->generate_rewrite_rules( $this->get_date_permastruct(), EP_DATE );
1314  
1315          /**
1316           * Filters rewrite rules used for date archives.
1317           *
1318           * Likely date archives would include `/yyyy/`, `/yyyy/mm/`, and `/yyyy/mm/dd/`.
1319           *
1320           * @since 1.5.0
1321           *
1322           * @param string[] $date_rewrite Array of rewrite rules for date archives, keyed by their regex pattern.
1323           */
1324          $date_rewrite = apply_filters( 'date_rewrite_rules', $date_rewrite );
1325  
1326          // Root-level rewrite rules.
1327          $root_rewrite = $this->generate_rewrite_rules( $this->root . '/', EP_ROOT );
1328  
1329          /**
1330           * Filters rewrite rules used for root-level archives.
1331           *
1332           * Likely root-level archives would include pagination rules for the homepage
1333           * as well as site-wide post feeds (e.g. `/feed/`, and `/feed/atom/`).
1334           *
1335           * @since 1.5.0
1336           *
1337           * @param string[] $root_rewrite Array of root-level rewrite rules, keyed by their regex pattern.
1338           */
1339          $root_rewrite = apply_filters( 'root_rewrite_rules', $root_rewrite );
1340  
1341          // Comments rewrite rules.
1342          $comments_rewrite = $this->generate_rewrite_rules( $this->root . $this->comments_base, EP_COMMENTS, false, true, true, false );
1343  
1344          /**
1345           * Filters rewrite rules used for comment feed archives.
1346           *
1347           * Likely comments feed archives include `/comments/feed/` and `/comments/feed/atom/`.
1348           *
1349           * @since 1.5.0
1350           *
1351           * @param string[] $comments_rewrite Array of rewrite rules for the site-wide comments feeds, keyed by their regex pattern.
1352           */
1353          $comments_rewrite = apply_filters( 'comments_rewrite_rules', $comments_rewrite );
1354  
1355          // Search rewrite rules.
1356          $search_structure = $this->get_search_permastruct();
1357          $search_rewrite   = $this->generate_rewrite_rules( $search_structure, EP_SEARCH );
1358  
1359          /**
1360           * Filters rewrite rules used for search archives.
1361           *
1362           * Likely search-related archives include `/search/search+query/` as well as
1363           * pagination and feed paths for a search.
1364           *
1365           * @since 1.5.0
1366           *
1367           * @param string[] $search_rewrite Array of rewrite rules for search queries, keyed by their regex pattern.
1368           */
1369          $search_rewrite = apply_filters( 'search_rewrite_rules', $search_rewrite );
1370  
1371          // Author rewrite rules.
1372          $author_rewrite = $this->generate_rewrite_rules( $this->get_author_permastruct(), EP_AUTHORS );
1373  
1374          /**
1375           * Filters rewrite rules used for author archives.
1376           *
1377           * Likely author archives would include `/author/author-name/`, as well as
1378           * pagination and feed paths for author archives.
1379           *
1380           * @since 1.5.0
1381           *
1382           * @param string[] $author_rewrite Array of rewrite rules for author archives, keyed by their regex pattern.
1383           */
1384          $author_rewrite = apply_filters( 'author_rewrite_rules', $author_rewrite );
1385  
1386          // Pages rewrite rules.
1387          $page_rewrite = $this->page_rewrite_rules();
1388  
1389          /**
1390           * Filters rewrite rules used for "page" post type archives.
1391           *
1392           * @since 1.5.0
1393           *
1394           * @param string[] $page_rewrite Array of rewrite rules for the "page" post type, keyed by their regex pattern.
1395           */
1396          $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite );
1397  
1398          // Extra permastructs.
1399          foreach ( $this->extra_permastructs as $permastructname => $struct ) {
1400              if ( is_array( $struct ) ) {
1401                  if ( count( $struct ) == 2 ) {
1402                      $rules = $this->generate_rewrite_rules( $struct[0], $struct[1] );
1403                  } else {
1404                      $rules = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
1405                  }
1406              } else {
1407                  $rules = $this->generate_rewrite_rules( $struct );
1408              }
1409  
1410              /**
1411               * Filters rewrite rules used for individual permastructs.
1412               *
1413               * The dynamic portion of the hook name, `$permastructname`, refers
1414               * to the name of the registered permastruct.
1415               *
1416               * Possible hook names include:
1417               *
1418               *  - `category_rewrite_rules`
1419               *  - `post_format_rewrite_rules`
1420               *  - `post_tag_rewrite_rules`
1421               *
1422               * @since 3.1.0
1423               *
1424               * @param string[] $rules Array of rewrite rules generated for the current permastruct, keyed by their regex pattern.
1425               */
1426              $rules = apply_filters( "{$permastructname}_rewrite_rules", $rules );
1427  
1428              if ( 'post_tag' === $permastructname ) {
1429  
1430                  /**
1431                   * Filters rewrite rules used specifically for Tags.
1432                   *
1433                   * @since 2.3.0
1434                   * @deprecated 3.1.0 Use {@see 'post_tag_rewrite_rules'} instead.
1435                   *
1436                   * @param string[] $rules Array of rewrite rules generated for tags, keyed by their regex pattern.
1437                   */
1438                  $rules = apply_filters_deprecated( 'tag_rewrite_rules', array( $rules ), '3.1.0', 'post_tag_rewrite_rules' );
1439              }
1440  
1441              $this->extra_rules_top = array_merge( $this->extra_rules_top, $rules );
1442          }
1443  
1444          // Put them together.
1445          if ( $this->use_verbose_page_rules ) {
1446              $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $favicon_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $page_rewrite, $post_rewrite, $this->extra_rules );
1447          } else {
1448              $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $favicon_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules );
1449          }
1450  
1451          /**
1452           * Fires after the rewrite rules are generated.
1453           *
1454           * @since 1.5.0
1455           *
1456           * @param WP_Rewrite $wp_rewrite Current WP_Rewrite instance (passed by reference).
1457           */
1458          do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );
1459  
1460          /**
1461           * Filters the full set of generated rewrite rules.
1462           *
1463           * @since 1.5.0
1464           *
1465           * @param string[] $rules The compiled array of rewrite rules, keyed by their regex pattern.
1466           */
1467          $this->rules = apply_filters( 'rewrite_rules_array', $this->rules );
1468  
1469          return $this->rules;
1470      }
1471  
1472      /**
1473       * Retrieves the rewrite rules.
1474       *
1475       * The difference between this method and WP_Rewrite::rewrite_rules() is that
1476       * this method stores the rewrite rules in the 'rewrite_rules' option and retrieves
1477       * it. This prevents having to process all of the permalinks to get the rewrite rules
1478       * in the form of caching.
1479       *
1480       * @since 1.5.0
1481       *
1482       * @return string[] Array of rewrite rules keyed by their regex pattern.
1483       */
1484  	public function wp_rewrite_rules() {
1485          $this->rules = get_option( 'rewrite_rules' );
1486          if ( empty( $this->rules ) ) {
1487              $this->matches = 'matches';
1488              $this->rewrite_rules();
1489              if ( ! did_action( 'wp_loaded' ) ) {
1490                  add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
1491                  return $this->rules;
1492              }
1493              update_option( 'rewrite_rules', $this->rules );
1494          }
1495  
1496          return $this->rules;
1497      }
1498  
1499      /**
1500       * Retrieves mod_rewrite-formatted rewrite rules to write to .htaccess.
1501       *
1502       * Does not actually write to the .htaccess file, but creates the rules for
1503       * the process that will.
1504       *
1505       * Will add the non_wp_rules property rules to the .htaccess file before
1506       * the WordPress rewrite rules one.
1507       *
1508       * @since 1.5.0
1509       *
1510       * @return string
1511       */
1512  	public function mod_rewrite_rules() {
1513          if ( ! $this->using_permalinks() ) {
1514              return '';
1515          }
1516  
1517          $site_root = parse_url( site_url() );
1518          if ( isset( $site_root['path'] ) ) {
1519              $site_root = trailingslashit( $site_root['path'] );
1520          }
1521  
1522          $home_root = parse_url( home_url() );
1523          if ( isset( $home_root['path'] ) ) {
1524              $home_root = trailingslashit( $home_root['path'] );
1525          } else {
1526              $home_root = '/';
1527          }
1528  
1529          $rules  = "<IfModule mod_rewrite.c>\n";
1530          $rules .= "RewriteEngine On\n";
1531          $rules .= "RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]\n";
1532          $rules .= "RewriteBase $home_root\n";
1533  
1534          // Prevent -f checks on index.php.
1535          $rules .= "RewriteRule ^index\.php$ - [L]\n";
1536  
1537          // Add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all).
1538          foreach ( (array) $this->non_wp_rules as $match => $query ) {
1539              // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1540              $match = str_replace( '.+?', '.+', $match );
1541  
1542              $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1543          }
1544  
1545          if ( $this->use_verbose_rules ) {
1546              $this->matches = '';
1547              $rewrite       = $this->rewrite_rules();
1548              $num_rules     = count( $rewrite );
1549              $rules        .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
1550                  "RewriteCond %{REQUEST_FILENAME} -d\n" .
1551                  "RewriteRule ^.*$ - [S=$num_rules]\n";
1552  
1553              foreach ( (array) $rewrite as $match => $query ) {
1554                  // Apache 1.3 does not support the reluctant (non-greedy) modifier.
1555                  $match = str_replace( '.+?', '.+', $match );
1556  
1557                  if ( strpos( $query, $this->index ) !== false ) {
1558                      $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
1559                  } else {
1560                      $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
1561                  }
1562              }
1563          } else {
1564              $rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
1565                  "RewriteCond %{REQUEST_FILENAME} !-d\n" .
1566                  "RewriteRule . {$home_root}{$this->index} [L]\n";
1567          }
1568  
1569          $rules .= "</IfModule>\n";
1570  
1571          /**
1572           * Filters the list of rewrite rules formatted for output to an .htaccess file.
1573           *
1574           * @since 1.5.0
1575           *
1576           * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
1577           */
1578          $rules = apply_filters( 'mod_rewrite_rules', $rules );
1579  
1580          /**
1581           * Filters the list of rewrite rules formatted for output to an .htaccess file.
1582           *
1583           * @since 1.5.0
1584           * @deprecated 1.5.0 Use the {@see 'mod_rewrite_rules'} filter instead.
1585           *
1586           * @param string $rules mod_rewrite Rewrite rules formatted for .htaccess.
1587           */
1588          return apply_filters_deprecated( 'rewrite_rules', array( $rules ), '1.5.0', 'mod_rewrite_rules' );
1589      }
1590  
1591      /**
1592       * Retrieves IIS7 URL Rewrite formatted rewrite rules to write to web.config file.
1593       *
1594       * Does not actually write to the web.config file, but creates the rules for
1595       * the process that will.
1596       *
1597       * @since 2.8.0
1598       *
1599       * @param bool $add_parent_tags Optional. Whether to add parent tags to the rewrite rule sets.
1600       *                              Default false.
1601       * @return string IIS7 URL rewrite rule sets.
1602       */
1603  	public function iis7_url_rewrite_rules( $add_parent_tags = false ) {
1604          if ( ! $this->using_permalinks() ) {
1605              return '';
1606          }
1607          $rules = '';
1608          if ( $add_parent_tags ) {
1609              $rules .= '<configuration>
1610      <system.webServer>
1611          <rewrite>
1612              <rules>';
1613          }
1614  
1615          $rules .= '
1616              <rule name="WordPress: ' . esc_attr( home_url() ) . '" patternSyntax="Wildcard">
1617                  <match url="*" />
1618                      <conditions>
1619                          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
1620                          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
1621                      </conditions>
1622                  <action type="Rewrite" url="index.php" />
1623              </rule>';
1624  
1625          if ( $add_parent_tags ) {
1626              $rules .= '
1627              </rules>
1628          </rewrite>
1629      </system.webServer>
1630  </configuration>';
1631          }
1632  
1633          /**
1634           * Filters the list of rewrite rules formatted for output to a web.config.
1635           *
1636           * @since 2.8.0
1637           *
1638           * @param string $rules Rewrite rules formatted for IIS web.config.
1639           */
1640          return apply_filters( 'iis7_url_rewrite_rules', $rules );
1641      }
1642  
1643      /**
1644       * Adds a rewrite rule that transforms a URL structure to a set of query vars.
1645       *
1646       * Any value in the $after parameter that isn't 'bottom' will result in the rule
1647       * being placed at the top of the rewrite rules.
1648       *
1649       * @since 2.1.0
1650       * @since 4.4.0 Array support was added to the `$query` parameter.
1651       *
1652       * @param string       $regex Regular expression to match request against.
1653       * @param string|array $query The corresponding query vars for this rewrite rule.
1654       * @param string       $after Optional. Priority of the new rule. Accepts 'top'
1655       *                            or 'bottom'. Default 'bottom'.
1656       */
1657  	public function add_rule( $regex, $query, $after = 'bottom' ) {
1658          if ( is_array( $query ) ) {
1659              $external = false;
1660              $query    = add_query_arg( $query, 'index.php' );
1661          } else {
1662              $index = false === strpos( $query, '?' ) ? strlen( $query ) : strpos( $query, '?' );
1663              $front = substr( $query, 0, $index );
1664  
1665              $external = $front != $this->index;
1666          }
1667  
1668          // "external" = it doesn't correspond to index.php.
1669          if ( $external ) {
1670              $this->add_external_rule( $regex, $query );
1671          } else {
1672              if ( 'bottom' === $after ) {
1673                  $this->extra_rules = array_merge( $this->extra_rules, array( $regex => $query ) );
1674              } else {
1675                  $this->extra_rules_top = array_merge( $this->extra_rules_top, array( $regex => $query ) );
1676              }
1677          }
1678      }
1679  
1680      /**
1681       * Adds a rewrite rule that doesn't correspond to index.php.
1682       *
1683       * @since 2.1.0
1684       *
1685       * @param string $regex Regular expression to match request against.
1686       * @param string $query The corresponding query vars for this rewrite rule.
1687       */
1688  	public function add_external_rule( $regex, $query ) {
1689          $this->non_wp_rules[ $regex ] = $query;
1690      }
1691  
1692      /**
1693       * Adds an endpoint, like /trackback/.
1694       *
1695       * @since 2.1.0
1696       * @since 3.9.0 $query_var parameter added.
1697       * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
1698       *
1699       * @see add_rewrite_endpoint() for full documentation.
1700       * @global WP $wp Current WordPress environment instance.
1701       *
1702       * @param string      $name      Name of the endpoint.
1703       * @param int         $places    Endpoint mask describing the places the endpoint should be added.
1704       *                               Accepts a mask of:
1705       *                               - `EP_ALL`
1706       *                               - `EP_NONE`
1707       *                               - `EP_ALL_ARCHIVES`
1708       *                               - `EP_ATTACHMENT`
1709       *                               - `EP_AUTHORS`
1710       *                               - `EP_CATEGORIES`
1711       *                               - `EP_COMMENTS`
1712       *                               - `EP_DATE`
1713       *                               - `EP_DAY`
1714       *                               - `EP_MONTH`
1715       *                               - `EP_PAGES`
1716       *                               - `EP_PERMALINK`
1717       *                               - `EP_ROOT`
1718       *                               - `EP_SEARCH`
1719       *                               - `EP_TAGS`
1720       *                               - `EP_YEAR`
1721       * @param string|bool $query_var Optional. Name of the corresponding query variable. Pass `false` to
1722       *                               skip registering a query_var for this endpoint. Defaults to the
1723       *                               value of `$name`.
1724       */
1725  	public function add_endpoint( $name, $places, $query_var = true ) {
1726          global $wp;
1727  
1728          // For backward compatibility, if null has explicitly been passed as `$query_var`, assume `true`.
1729          if ( true === $query_var || null === $query_var ) {
1730              $query_var = $name;
1731          }
1732          $this->endpoints[] = array( $places, $name, $query_var );
1733  
1734          if ( $query_var ) {
1735              $wp->add_query_var( $query_var );
1736          }
1737      }
1738  
1739      /**
1740       * Adds a new permalink structure.
1741       *
1742       * A permalink structure (permastruct) is an abstract definition of a set of rewrite rules;
1743       * it is an easy way of expressing a set of regular expressions that rewrite to a set of
1744       * query strings. The new permastruct is added to the WP_Rewrite::$extra_permastructs array.
1745       *
1746       * When the rewrite rules are built by WP_Rewrite::rewrite_rules(), all of these extra
1747       * permastructs are passed to WP_Rewrite::generate_rewrite_rules() which transforms them
1748       * into the regular expressions that many love to hate.
1749       *
1750       * The `$args` parameter gives you control over how WP_Rewrite::generate_rewrite_rules()
1751       * works on the new permastruct.
1752       *
1753       * @since 2.5.0
1754       *
1755       * @param string $name   Name for permalink structure.
1756       * @param string $struct Permalink structure (e.g. category/%category%)
1757       * @param array  $args   {
1758       *     Optional. Arguments for building rewrite rules based on the permalink structure.
1759       *     Default empty array.
1760       *
1761       *     @type bool $with_front  Whether the structure should be prepended with `WP_Rewrite::$front`.
1762       *                             Default true.
1763       *     @type int  $ep_mask     The endpoint mask defining which endpoints are added to the structure.
1764       *                             Accepts a mask of:
1765       *                             - `EP_ALL`
1766       *                             - `EP_NONE`
1767       *                             - `EP_ALL_ARCHIVES`
1768       *                             - `EP_ATTACHMENT`
1769       *                             - `EP_AUTHORS`
1770       *                             - `EP_CATEGORIES`
1771       *                             - `EP_COMMENTS`
1772       *                             - `EP_DATE`
1773       *                             - `EP_DAY`
1774       *                             - `EP_MONTH`
1775       *                             - `EP_PAGES`
1776       *                             - `EP_PERMALINK`
1777       *                             - `EP_ROOT`
1778       *                             - `EP_SEARCH`
1779       *                             - `EP_TAGS`
1780       *                             - `EP_YEAR`
1781       *                             Default `EP_NONE`.
1782       *     @type bool $paged       Whether archive pagination rules should be added for the structure.
1783       *                             Default true.
1784       *     @type bool $feed        Whether feed rewrite rules should be added for the structure. Default true.
1785       *     @type bool $forcomments Whether the feed rules should be a query for a comments feed. Default false.
1786       *     @type bool $walk_dirs   Whether the 'directories' making up the structure should be walked over
1787       *                             and rewrite rules built for each in-turn. Default true.
1788       *     @type bool $endpoints   Whether endpoints should be applied to the generated rules. Default true.
1789       * }
1790       */
1791  	public function add_permastruct( $name, $struct, $args = array() ) {
1792          // Back-compat for the old parameters: $with_front and $ep_mask.
1793          if ( ! is_array( $args ) ) {
1794              $args = array( 'with_front' => $args );
1795          }
1796          if ( func_num_args() == 4 ) {
1797              $args['ep_mask'] = func_get_arg( 3 );
1798          }
1799  
1800          $defaults = array(
1801              'with_front'  => true,
1802              'ep_mask'     => EP_NONE,
1803              'paged'       => true,
1804              'feed'        => true,
1805              'forcomments' => false,
1806              'walk_dirs'   => true,
1807              'endpoints'   => true,
1808          );
1809          $args     = array_intersect_key( $args, $defaults );
1810          $args     = wp_parse_args( $args, $defaults );
1811  
1812          if ( $args['with_front'] ) {
1813              $struct = $this->front . $struct;
1814          } else {
1815              $struct = $this->root . $struct;
1816          }
1817          $args['struct'] = $struct;
1818  
1819          $this->extra_permastructs[ $name ] = $args;
1820      }
1821  
1822      /**
1823       * Removes a permalink structure.
1824       *
1825       * @since 4.5.0
1826       *
1827       * @param string $name Name for permalink structure.
1828       */
1829  	public function remove_permastruct( $name ) {
1830          unset( $this->extra_permastructs[ $name ] );
1831      }
1832  
1833      /**
1834       * Removes rewrite rules and then recreate rewrite rules.
1835       *
1836       * Calls WP_Rewrite::wp_rewrite_rules() after removing the 'rewrite_rules' option.
1837       * If the function named 'save_mod_rewrite_rules' exists, it will be called.
1838       *
1839       * @since 2.0.1
1840       *
1841       * @param bool $hard Whether to update .htaccess (hard flush) or just update rewrite_rules option (soft flush). Default is true (hard).
1842       */
1843  	public function flush_rules( $hard = true ) {
1844          static $do_hard_later = null;
1845  
1846          // Prevent this action from running before everyone has registered their rewrites.
1847          if ( ! did_action( 'wp_loaded' ) ) {
1848              add_action( 'wp_loaded', array( $this, 'flush_rules' ) );
1849              $do_hard_later = ( isset( $do_hard_later ) ) ? $do_hard_later || $hard : $hard;
1850              return;
1851          }
1852  
1853          if ( isset( $do_hard_later ) ) {
1854              $hard = $do_hard_later;
1855              unset( $do_hard_later );
1856          }
1857  
1858          update_option( 'rewrite_rules', '' );
1859          $this->wp_rewrite_rules();
1860  
1861          /**
1862           * Filters whether a "hard" rewrite rule flush should be performed when requested.
1863           *
1864           * A "hard" flush updates .htaccess (Apache) or web.config (IIS).
1865           *
1866           * @since 3.7.0
1867           *
1868           * @param bool $hard Whether to flush rewrite rules "hard". Default true.
1869           */
1870          if ( ! $hard || ! apply_filters( 'flush_rewrite_rules_hard', true ) ) {
1871              return;
1872          }
1873          if ( function_exists( 'save_mod_rewrite_rules' ) ) {
1874              save_mod_rewrite_rules();
1875          }
1876          if ( function_exists( 'iis7_save_url_rewrite_rules' ) ) {
1877              iis7_save_url_rewrite_rules();
1878          }
1879      }
1880  
1881      /**
1882       * Sets up the object's properties.
1883       *
1884       * The 'use_verbose_page_rules' object property will be set to true if the
1885       * permalink structure begins with one of the following: '%postname%', '%category%',
1886       * '%tag%', or '%author%'.
1887       *
1888       * @since 1.5.0
1889       */
1890  	public function init() {
1891          $this->extra_rules         = array();
1892          $this->non_wp_rules        = array();
1893          $this->endpoints           = array();
1894          $this->permalink_structure = get_option( 'permalink_structure' );
1895          $this->front               = substr( $this->permalink_structure, 0, strpos( $this->permalink_structure, '%' ) );
1896          $this->root                = '';
1897  
1898          if ( $this->using_index_permalinks() ) {
1899              $this->root = $this->index . '/';
1900          }
1901  
1902          unset( $this->author_structure );
1903          unset( $this->date_structure );
1904          unset( $this->page_structure );
1905          unset( $this->search_structure );
1906          unset( $this->feed_structure );
1907          unset( $this->comment_feed_structure );
1908  
1909          $this->use_trailing_slashes = ( '/' === substr( $this->permalink_structure, -1, 1 ) );
1910  
1911          // Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
1912          if ( preg_match( '/^[^%]*%(?:postname|category|tag|author)%/', $this->permalink_structure ) ) {
1913              $this->use_verbose_page_rules = true;
1914          } else {
1915              $this->use_verbose_page_rules = false;
1916          }
1917      }
1918  
1919      /**
1920       * Sets the main permalink structure for the site.
1921       *
1922       * Will update the 'permalink_structure' option, if there is a difference
1923       * between the current permalink structure and the parameter value. Calls
1924       * WP_Rewrite::init() after the option is updated.
1925       *
1926       * Fires the {@see 'permalink_structure_changed'} action once the init call has
1927       * processed passing the old and new values
1928       *
1929       * @since 1.5.0
1930       *
1931       * @param string $permalink_structure Permalink structure.
1932       */
1933  	public function set_permalink_structure( $permalink_structure ) {
1934          if ( $permalink_structure != $this->permalink_structure ) {
1935              $old_permalink_structure = $this->permalink_structure;
1936              update_option( 'permalink_structure', $permalink_structure );
1937  
1938              $this->init();
1939  
1940              /**
1941               * Fires after the permalink structure is updated.
1942               *
1943               * @since 2.8.0
1944               *
1945               * @param string $old_permalink_structure The previous permalink structure.
1946               * @param string $permalink_structure     The new permalink structure.
1947               */
1948              do_action( 'permalink_structure_changed', $old_permalink_structure, $permalink_structure );
1949          }
1950      }
1951  
1952      /**
1953       * Sets the category base for the category permalink.
1954       *
1955       * Will update the 'category_base' option, if there is a difference between
1956       * the current category base and the parameter value. Calls WP_Rewrite::init()
1957       * after the option is updated.
1958       *
1959       * @since 1.5.0
1960       *
1961       * @param string $category_base Category permalink structure base.
1962       */
1963  	public function set_category_base( $category_base ) {
1964          if ( get_option( 'category_base' ) !== $category_base ) {
1965              update_option( 'category_base', $category_base );
1966              $this->init();
1967          }
1968      }
1969  
1970      /**
1971       * Sets the tag base for the tag permalink.
1972       *
1973       * Will update the 'tag_base' option, if there is a difference between the
1974       * current tag base and the parameter value. Calls WP_Rewrite::init() after
1975       * the option is updated.
1976       *
1977       * @since 2.3.0
1978       *
1979       * @param string $tag_base Tag permalink structure base.
1980       */
1981  	public function set_tag_base( $tag_base ) {
1982          if ( get_option( 'tag_base' ) !== $tag_base ) {
1983              update_option( 'tag_base', $tag_base );
1984              $this->init();
1985          }
1986      }
1987  
1988      /**
1989       * Constructor - Calls init(), which runs setup.
1990       *
1991       * @since 1.5.0
1992       */
1993  	public function __construct() {
1994          $this->init();
1995      }
1996  }


Generated: Thu Nov 21 01:00:03 2024 Cross-referenced by PHPXref 0.7.1