PageRenderTime 217ms CodeModel.GetById 100ms app.highlight 70ms RepoModel.GetById 31ms app.codeStats 1ms

/wp-includes/rewrite.php

https://bitbucket.org/aqge/deptandashboard
PHP | 1974 lines | 785 code | 227 blank | 962 comment | 142 complexity | 9200b2ed4d3ea671c27333a551ee56d1 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file