[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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


Generated : Fri Mar 29 08:20:02 2024 Cross-referenced by PHPXref