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