[ Index ]

PHP Cross Reference of Wordpress 2.7.1

title

Body

[close]

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

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


Generated: Mon Mar 23 16:23:02 2009 Cross-referenced by PHPXref 0.7