home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Blogs / wordpress2.6.exe / wordpress2.6 / wp-includes / rewrite.php < prev    next >
Encoding:
PHP Script  |  2008-08-12  |  33.2 KB  |  1,049 lines

  1. <?php
  2.  
  3. /* WP_Rewrite API
  4. *******************************************************************************/
  5.  
  6. //Add a straight rewrite rule
  7. function add_rewrite_rule($regex, $redirect, $after = 'bottom') {
  8.     global $wp_rewrite;
  9.     $wp_rewrite->add_rule($regex, $redirect, $after);
  10. }
  11.  
  12. //Add a new tag (like %postname%)
  13. //warning: you must call this on init or earlier, otherwise the query var addition stuff won't work
  14. function add_rewrite_tag($tagname, $regex) {
  15.     //validation
  16.     if (strlen($tagname) < 3 || $tagname{0} != '%' || $tagname{strlen($tagname)-1} != '%') {
  17.         return;
  18.     }
  19.  
  20.     $qv = trim($tagname, '%');
  21.  
  22.     global $wp_rewrite, $wp;
  23.     $wp->add_query_var($qv);
  24.     $wp_rewrite->add_rewrite_tag($tagname, $regex, $qv . '=');
  25. }
  26.  
  27. //Add a new feed type like /atom1/
  28. function add_feed($feedname, $function) {
  29.     global $wp_rewrite;
  30.     if (!in_array($feedname, $wp_rewrite->feeds)) { //override the file if it is
  31.         $wp_rewrite->feeds[] = $feedname;
  32.     }
  33.     $hook = 'do_feed_' . $feedname;
  34.     // Remove default function hook
  35.     remove_action($hook, $hook, 10, 1);
  36.     add_action($hook, $function, 10, 1);
  37.     return $hook;
  38. }
  39.  
  40. define('EP_PERMALINK',  1   );
  41. define('EP_ATTACHMENT', 2   );
  42. define('EP_DATE',       4   );
  43. define('EP_YEAR',       8   );
  44. define('EP_MONTH',      16  );
  45. define('EP_DAY',        32  );
  46. define('EP_ROOT',       64  );
  47. define('EP_COMMENTS',   128 );
  48. define('EP_SEARCH',     256 );
  49. define('EP_CATEGORIES', 512 );
  50. define('EP_TAGS', 1024 );
  51. define('EP_AUTHORS',    2048);
  52. define('EP_PAGES',      4096);
  53. //pseudo-places
  54. define('EP_NONE',       0  );
  55. define('EP_ALL',        8191);
  56.  
  57. //and an endpoint, like /trackback/
  58. function add_rewrite_endpoint($name, $places) {
  59.     global $wp_rewrite;
  60.     $wp_rewrite->add_endpoint($name, $places);
  61. }
  62.  
  63. /**
  64.   * _wp_filter_taxonomy_base() - filter the URL base for taxonomies, to remove any manually prepended /index.php/
  65.   * @param string $base the taxonomy base that we're going to filter
  66.   * @return string
  67.   * @author Mark Jaquith 
  68.   */
  69. function _wp_filter_taxonomy_base( $base ) {
  70.     if ( !empty( $base ) ) {
  71.         $base = preg_replace( '|^/index\.php/|', '', $base );
  72.         $base = trim( $base, '/' );
  73.     }
  74.     return $base;
  75. }
  76.  
  77. // examine a url (supposedly from this blog) and try to
  78. // determine the post ID it represents.
  79. function url_to_postid($url) {
  80.     global $wp_rewrite;
  81.  
  82.     $url = apply_filters('url_to_postid', $url);
  83.  
  84.     // First, check to see if there is a 'p=N' or 'page_id=N' to match against
  85.     if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) )    {
  86.         $id = absint($values[2]);
  87.         if ($id)
  88.             return $id;
  89.     }
  90.  
  91.     // Check to see if we are using rewrite rules
  92.     $rewrite = $wp_rewrite->wp_rewrite_rules();
  93.  
  94.     // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
  95.     if ( empty($rewrite) )
  96.         return 0;
  97.  
  98.     // $url cleanup by Mark Jaquith
  99.     // This fixes things like #anchors, ?query=strings, missing 'www.',
  100.     // added 'www.', or added 'index.php/' that will mess up our WP_Query
  101.     // and return a false negative
  102.  
  103.     // Get rid of the #anchor
  104.     $url_split = explode('#', $url);
  105.     $url = $url_split[0];
  106.  
  107.     // Get rid of URL ?query=string
  108.     $url_split = explode('?', $url);
  109.     $url = $url_split[0];
  110.  
  111.     // Add 'www.' if it is absent and should be there
  112.     if ( false !== strpos(get_option('home'), '://www.') && false === strpos($url, '://www.') )
  113.         $url = str_replace('://', '://www.', $url);
  114.  
  115.     // Strip 'www.' if it is present and shouldn't be
  116.     if ( false === strpos(get_option('home'), '://www.') )
  117.         $url = str_replace('://www.', '://', $url);
  118.  
  119.     // Strip 'index.php/' if we're not using path info permalinks
  120.     if ( !$wp_rewrite->using_index_permalinks() )
  121.         $url = str_replace('index.php/', '', $url);
  122.  
  123.     if ( false !== strpos($url, get_option('home')) ) {
  124.         // Chop off http://domain.com
  125.         $url = str_replace(get_option('home'), '', $url);
  126.     } else {
  127.         // Chop off /path/to/blog
  128.         $home_path = parse_url(get_option('home'));
  129.         $home_path = $home_path['path'];
  130.         $url = str_replace($home_path, '', $url);
  131.     }
  132.  
  133.     // Trim leading and lagging slashes
  134.     $url = trim($url, '/');
  135.  
  136.     $request = $url;
  137.  
  138.     // Done with cleanup
  139.  
  140.     // Look for matches.
  141.     $request_match = $request;
  142.     foreach ($rewrite as $match => $query) {
  143.         // If the requesting file is the anchor of the match, prepend it
  144.         // to the path info.
  145.         if ( (! empty($url)) && (strpos($match, $url) === 0) && ($url != $request)) {
  146.             $request_match = $url . '/' . $request;
  147.         }
  148.  
  149.         if ( preg_match("!^$match!", $request_match, $matches) ) {
  150.             // Got a match.
  151.             // Trim the query of everything up to the '?'.
  152.             $query = preg_replace("!^.+\?!", '', $query);
  153.  
  154.             // Substitute the substring matches into the query.
  155.             eval("\$query = \"" . addslashes($query) . "\";");
  156.             // Filter out non-public query vars
  157.             global $wp;
  158.             parse_str($query, $query_vars);
  159.             $query = array();
  160.             foreach ( $query_vars as $key => $value ) {
  161.                 if ( in_array($key, $wp->public_query_vars) )
  162.                     $query[$key] = $value;
  163.             }
  164.             // Do the query
  165.             $query = new WP_Query($query);
  166.             if ( $query->is_single || $query->is_page )
  167.                 return $query->post->ID;
  168.             else
  169.                 return 0;
  170.         }
  171.     }
  172.     return 0;
  173. }
  174.  
  175. /* WP_Rewrite class
  176. *******************************************************************************/
  177.  
  178. class WP_Rewrite {
  179.     var $permalink_structure;
  180.     var $use_trailing_slashes;
  181.     var $category_base;
  182.     var $tag_base;
  183.     var $category_structure;
  184.     var $tag_structure;
  185.     var $author_base = 'author';
  186.     var $author_structure;
  187.     var $date_structure;
  188.     var $page_structure;
  189.     var $search_base = 'search';
  190.     var $search_structure;
  191.     var $comments_base = 'comments';
  192.     var $feed_base = 'feed';
  193.     var $comments_feed_structure;
  194.     var $feed_structure;
  195.     var $front;
  196.     var $root = '';
  197.     var $index = 'index.php';
  198.     var $matches = '';
  199.     var $rules;
  200.     var $extra_rules = array(); //those not generated by the class, see add_rewrite_rule()
  201.     var $extra_rules_top = array(); //those not generated by the class, see add_rewrite_rule()
  202.     var $non_wp_rules = array(); //rules that don't redirect to WP's index.php
  203.     var $extra_permastructs = array();
  204.     var $endpoints;
  205.     var $use_verbose_rules = false;
  206.     var $use_verbose_page_rules = true;
  207.     var $rewritecode =
  208.         array(
  209.                     '%year%',
  210.                     '%monthnum%',
  211.                     '%day%',
  212.                     '%hour%',
  213.                     '%minute%',
  214.                     '%second%',
  215.                     '%postname%',
  216.                     '%post_id%',
  217.                     '%category%',
  218.                     '%tag%',
  219.                     '%author%',
  220.                     '%pagename%',
  221.                     '%search%'
  222.                     );
  223.  
  224.     var $rewritereplace =
  225.         array(
  226.                     '([0-9]{4})',
  227.                     '([0-9]{1,2})',
  228.                     '([0-9]{1,2})',
  229.                     '([0-9]{1,2})',
  230.                     '([0-9]{1,2})',
  231.                     '([0-9]{1,2})',
  232.                     '([^/]+)',
  233.                     '([0-9]+)',
  234.                     '(.+?)',
  235.                     '(.+?)',
  236.                     '([^/]+)',
  237.                     '([^/]+?)',
  238.                     '(.+)'
  239.                     );
  240.  
  241.     var $queryreplace =
  242.         array (
  243.                     'year=',
  244.                     'monthnum=',
  245.                     'day=',
  246.                     'hour=',
  247.                     'minute=',
  248.                     'second=',
  249.                     'name=',
  250.                     'p=',
  251.                     'category_name=',
  252.                     'tag=',
  253.                     'author_name=',
  254.                     'pagename=',
  255.                     's='
  256.                     );
  257.  
  258.     var $feeds = array ( 'feed', 'rdf', 'rss', 'rss2', 'atom' );
  259.  
  260.     function using_permalinks() {
  261.         if (empty($this->permalink_structure))
  262.             return false;
  263.         else
  264.             return true;
  265.     }
  266.  
  267.     function using_index_permalinks() {
  268.         if (empty($this->permalink_structure)) {
  269.             return false;
  270.         }
  271.  
  272.         // If the index is not in the permalink, we're using mod_rewrite.
  273.         if (preg_match('#^/*' . $this->index . '#', $this->permalink_structure)) {
  274.             return true;
  275.         }
  276.  
  277.         return false;
  278.     }
  279.  
  280.     function using_mod_rewrite_permalinks() {
  281.         if ( $this->using_permalinks() && ! $this->using_index_permalinks())
  282.             return true;
  283.         else
  284.             return false;
  285.     }
  286.  
  287.     function preg_index($number) {
  288.         $match_prefix = '$';
  289.         $match_suffix = '';
  290.  
  291.         if (! empty($this->matches)) {
  292.             $match_prefix = '$' . $this->matches . '[';
  293.             $match_suffix = ']';
  294.         }
  295.  
  296.         return "$match_prefix$number$match_suffix";
  297.     }
  298.  
  299.     function page_uri_index() {
  300.         global $wpdb;
  301.  
  302.         //get pages in order of hierarchy, i.e. children after parents
  303.         $posts = get_page_hierarchy($wpdb->get_results("SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'page'"));
  304.         //now reverse it, because we need parents after children for rewrite rules to work properly
  305.         $posts = array_reverse($posts, true);
  306.  
  307.         $page_uris = array();
  308.         $page_attachment_uris = array();
  309.  
  310.         if ( !$posts )
  311.             return array( array(), array() );
  312.  
  313.  
  314.         foreach ($posts as $id => $post) {
  315.             // URL => page name
  316.             $uri = get_page_uri($id);
  317.             $attachments = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_type = 'attachment' AND post_parent = %d", $id ));
  318.             if ( $attachments ) {
  319.                 foreach ( $attachments as $attachment ) {
  320.                     $attach_uri = get_page_uri($attachment->ID);
  321.                     $page_attachment_uris[$attach_uri] = $attachment->ID;
  322.                 }
  323.             }
  324.  
  325.             $page_uris[$uri] = $id;
  326.         }
  327.  
  328.         return array( $page_uris, $page_attachment_uris );
  329.     }
  330.  
  331.     function page_rewrite_rules() {
  332.         $rewrite_rules = array();
  333.         $page_structure = $this->get_page_permastruct();
  334.  
  335.         if ( ! $this->use_verbose_page_rules ) {
  336.             $this->add_rewrite_tag('%pagename%', "(.+?)", 'pagename=');
  337.             $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
  338.             return $rewrite_rules;
  339.         }
  340.  
  341.         $page_uris = $this->page_uri_index();
  342.         $uris = $page_uris[0];
  343.         $attachment_uris = $page_uris[1];
  344.  
  345.  
  346.         if( is_array( $attachment_uris ) ) {
  347.             foreach ($attachment_uris as $uri => $pagename) {
  348.                 $this->add_rewrite_tag('%pagename%', "($uri)", 'attachment=');
  349.                 $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
  350.             }
  351.         }
  352.         if( is_array( $uris ) ) {
  353.             foreach ($uris as $uri => $pagename) {
  354.                 $this->add_rewrite_tag('%pagename%', "($uri)", 'pagename=');
  355.                 $rewrite_rules = array_merge($rewrite_rules, $this->generate_rewrite_rules($page_structure, EP_PAGES));
  356.             }
  357.         }
  358.  
  359.         return $rewrite_rules;
  360.     }
  361.  
  362.     function get_date_permastruct() {
  363.         if (isset($this->date_structure)) {
  364.             return $this->date_structure;
  365.         }
  366.  
  367.         if (empty($this->permalink_structure)) {
  368.             $this->date_structure = '';
  369.             return false;
  370.         }
  371.  
  372.         // The date permalink must have year, month, and day separated by slashes.
  373.         $endians = array('%year%/%monthnum%/%day%', '%day%/%monthnum%/%year%', '%monthnum%/%day%/%year%');
  374.  
  375.         $this->date_structure = '';
  376.         $date_endian = '';
  377.  
  378.         foreach ($endians as $endian) {
  379.             if (false !== strpos($this->permalink_structure, $endian)) {
  380.                 $date_endian= $endian;
  381.                 break;
  382.             }
  383.         }
  384.  
  385.         if ( empty($date_endian) )
  386.             $date_endian = '%year%/%monthnum%/%day%';
  387.  
  388.         // Do not allow the date tags and %post_id% to overlap in the permalink
  389.         // structure. If they do, move the date tags to $front/date/.
  390.         $front = $this->front;
  391.         preg_match_all('/%.+?%/', $this->permalink_structure, $tokens);
  392.         $tok_index = 1;
  393.         foreach ($tokens[0] as $token) {
  394.             if ( ($token == '%post_id%') && ($tok_index <= 3) ) {
  395.                 $front = $front . 'date/';
  396.                 break;
  397.             }
  398.             $tok_index++;
  399.         }
  400.  
  401.         $this->date_structure = $front . $date_endian;
  402.  
  403.         return $this->date_structure;
  404.     }
  405.  
  406.     function get_year_permastruct() {
  407.         $structure = $this->get_date_permastruct($this->permalink_structure);
  408.  
  409.         if (empty($structure)) {
  410.             return false;
  411.         }
  412.  
  413.         $structure = str_replace('%monthnum%', '', $structure);
  414.         $structure = str_replace('%day%', '', $structure);
  415.  
  416.         $structure = preg_replace('#/+#', '/', $structure);
  417.  
  418.         return $structure;
  419.     }
  420.  
  421.     function get_month_permastruct() {
  422.         $structure = $this->get_date_permastruct($this->permalink_structure);
  423.  
  424.         if (empty($structure)) {
  425.             return false;
  426.         }
  427.  
  428.         $structure = str_replace('%day%', '', $structure);
  429.  
  430.         $structure = preg_replace('#/+#', '/', $structure);
  431.  
  432.         return $structure;
  433.     }
  434.  
  435.     function get_day_permastruct() {
  436.         return $this->get_date_permastruct($this->permalink_structure);
  437.     }
  438.  
  439.     function get_category_permastruct() {
  440.         if (isset($this->category_structure)) {
  441.             return $this->category_structure;
  442.         }
  443.  
  444.         if (empty($this->permalink_structure)) {
  445.             $this->category_structure = '';
  446.             return false;
  447.         }
  448.  
  449.         if (empty($this->category_base))
  450.             $this->category_structure = trailingslashit( $this->front . 'category' );
  451.         else
  452.             $this->category_structure = trailingslashit( '/' . $this->root . $this->category_base );
  453.  
  454.         $this->category_structure .= '%category%';
  455.  
  456.         return $this->category_structure;
  457.     }
  458.  
  459.     function get_tag_permastruct() {
  460.         if (isset($this->tag_structure)) {
  461.             return $this->tag_structure;
  462.         }
  463.  
  464.         if (empty($this->permalink_structure)) {
  465.             $this->tag_structure = '';
  466.             return false;
  467.         }
  468.  
  469.         if (empty($this->tag_base))
  470.             $this->tag_structure = trailingslashit( $this->front . 'tag' );
  471.         else
  472.             $this->tag_structure = trailingslashit( '/' . $this->root . $this->tag_base );
  473.  
  474.         $this->tag_structure .= '%tag%';
  475.  
  476.         return $this->tag_structure;
  477.     }
  478.  
  479.     function get_extra_permastruct($name) {
  480.         if ( isset($this->extra_permastructs[$name]) )
  481.             return $this->extra_permastructs[$name];
  482.         return false;
  483.     }
  484.  
  485.     function get_author_permastruct() {
  486.         if (isset($this->author_structure)) {
  487.             return $this->author_structure;
  488.         }
  489.  
  490.         if (empty($this->permalink_structure)) {
  491.             $this->author_structure = '';
  492.             return false;
  493.         }
  494.  
  495.         $this->author_structure = $this->front . $this->author_base . '/%author%';
  496.  
  497.         return $this->author_structure;
  498.     }
  499.  
  500.     function get_search_permastruct() {
  501.         if (isset($this->search_structure)) {
  502.             return $this->search_structure;
  503.         }
  504.  
  505.         if (empty($this->permalink_structure)) {
  506.             $this->search_structure = '';
  507.             return false;
  508.         }
  509.  
  510.         $this->search_structure = $this->root . $this->search_base . '/%search%';
  511.  
  512.         return $this->search_structure;
  513.     }
  514.  
  515.     function get_page_permastruct() {
  516.         if (isset($this->page_structure)) {
  517.             return $this->page_structure;
  518.         }
  519.  
  520.         if (empty($this->permalink_structure)) {
  521.             $this->page_structure = '';
  522.             return false;
  523.         }
  524.  
  525.         $this->page_structure = $this->root . '%pagename%';
  526.  
  527.         return $this->page_structure;
  528.     }
  529.  
  530.     function get_feed_permastruct() {
  531.         if (isset($this->feed_structure)) {
  532.             return $this->feed_structure;
  533.         }
  534.  
  535.         if (empty($this->permalink_structure)) {
  536.             $this->feed_structure = '';
  537.             return false;
  538.         }
  539.  
  540.         $this->feed_structure = $this->root . $this->feed_base . '/%feed%';
  541.  
  542.         return $this->feed_structure;
  543.     }
  544.  
  545.     function get_comment_feed_permastruct() {
  546.         if (isset($this->comment_feed_structure)) {
  547.             return $this->comment_feed_structure;
  548.         }
  549.  
  550.         if (empty($this->permalink_structure)) {
  551.             $this->comment_feed_structure = '';
  552.             return false;
  553.         }
  554.  
  555.         $this->comment_feed_structure = $this->root . $this->comments_base . '/' . $this->feed_base . '/%feed%';
  556.  
  557.         return $this->comment_feed_structure;
  558.     }
  559.  
  560.     function add_rewrite_tag($tag, $pattern, $query) {
  561.         // If the tag already exists, replace the existing pattern and query for
  562.         // that tag, otherwise add the new tag, pattern, and query to the end of
  563.         // the arrays.
  564.         $position = array_search($tag, $this->rewritecode);
  565.         if (FALSE !== $position && NULL !== $position) {
  566.             $this->rewritereplace[$position] = $pattern;
  567.             $this->queryreplace[$position] = $query;
  568.         } else {
  569.             $this->rewritecode[] = $tag;
  570.             $this->rewritereplace[] = $pattern;
  571.             $this->queryreplace[] = $query;
  572.         }
  573.     }
  574.  
  575.     //the main WP_Rewrite function. generate the rules from permalink structure
  576.     function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) {
  577.         //build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
  578.         $feedregex2 = '';
  579.         foreach ($this->feeds as $feed_name) {
  580.             $feedregex2 .= $feed_name . '|';
  581.         }
  582.         $feedregex2 = '(' . trim($feedregex2, '|') .  ')/?$';
  583.         //$feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom
  584.         //and <permalink>/atom are both possible
  585.         $feedregex = $this->feed_base  . '/' . $feedregex2;
  586.  
  587.         //build a regex to match the trackback and page/xx parts of URLs
  588.         $trackbackregex = 'trackback/?$';
  589.         $pageregex = 'page/?([0-9]{1,})/?$';
  590.  
  591.         //build up an array of endpoint regexes to append => queries to append
  592.         if ($endpoints) {
  593.             $ep_query_append = array ();
  594.             foreach ($this->endpoints as $endpoint) {
  595.                 //match everything after the endpoint name, but allow for nothing to appear there
  596.                 $epmatch = $endpoint[1] . '(/(.*))?/?$';
  597.                 //this will be appended on to the rest of the query for each dir
  598.                 $epquery = '&' . $endpoint[1] . '=';
  599.                 $ep_query_append[$epmatch] = array ( $endpoint[0], $epquery );
  600.             }
  601.         }
  602.  
  603.         //get everything up to the first rewrite tag
  604.         $front = substr($permalink_structure, 0, strpos($permalink_structure, '%'));
  605.         //build an array of the tags (note that said array ends up being in $tokens[0])
  606.         preg_match_all('/%.+?%/', $permalink_structure, $tokens);
  607.  
  608.         $num_tokens = count($tokens[0]);
  609.  
  610.         $index = $this->index; //probably 'index.php'
  611.         $feedindex = $index;
  612.         $trackbackindex = $index;
  613.         //build a list from the rewritecode and queryreplace arrays, that will look something like
  614.         //tagname=$matches[i] where i is the current $i
  615.         for ($i = 0; $i < $num_tokens; ++$i) {
  616.             if (0 < $i) {
  617.                 $queries[$i] = $queries[$i - 1] . '&';
  618.             } else {
  619.                 $queries[$i] = '';
  620.             }
  621.  
  622.             $query_token = str_replace($this->rewritecode, $this->queryreplace, $tokens[0][$i]) . $this->preg_index($i+1);
  623.             $queries[$i] .= $query_token;
  624.         }
  625.  
  626.         //get the structure, minus any cruft (stuff that isn't tags) at the front
  627.         $structure = $permalink_structure;
  628.         if ($front != '/') {
  629.             $structure = str_replace($front, '', $structure);
  630.         }
  631.         //create a list of dirs to walk over, making rewrite rules for each level
  632.         //so for example, a $structure of /%year%/%month%/%postname% would create
  633.         //rewrite rules for /%year%/, /%year%/%month%/ and /%year%/%month%/%postname%
  634.         $structure = trim($structure, '/');
  635.         if ($walk_dirs) {
  636.             $dirs = explode('/', $structure);
  637.         } else {
  638.             $dirs[] = $structure;
  639.         }
  640.         $num_dirs = count($dirs);
  641.  
  642.         //strip slashes from the front of $front
  643.         $front = preg_replace('|^/+|', '', $front);
  644.  
  645.         //the main workhorse loop
  646.         $post_rewrite = array();
  647.         $struct = $front;
  648.         for ($j = 0; $j < $num_dirs; ++$j) {
  649.             //get the struct for this dir, and trim slashes off the front
  650.             $struct .= $dirs[$j] . '/'; //accumulate. see comment near explode('/', $structure) above
  651.             $struct = ltrim($struct, '/');
  652.             //replace tags with regexes
  653.             $match = str_replace($this->rewritecode, $this->rewritereplace, $struct);
  654.             //make a list of tags, and store how many there are in $num_toks
  655.             $num_toks = preg_match_all('/%.+?%/', $struct, $toks);
  656.             //get the 'tagname=$matches[i]'
  657.             $query = ( isset($queries) && is_array($queries) ) ? $queries[$num_toks - 1] : '';
  658.  
  659.             //set up $ep_mask_specific which is used to match more specific URL types
  660.             switch ($dirs[$j]) {
  661.                 case '%year%': $ep_mask_specific = EP_YEAR; break;
  662.                 case '%monthnum%': $ep_mask_specific = EP_MONTH; break;
  663.                 case '%day%': $ep_mask_specific = EP_DAY; break;
  664.             }
  665.  
  666.             //create query for /page/xx
  667.             $pagematch = $match . $pageregex;
  668.             $pagequery = $index . '?' . $query . '&paged=' . $this->preg_index($num_toks + 1);
  669.  
  670.             //create query for /feed/(feed|atom|rss|rss2|rdf)
  671.             $feedmatch = $match . $feedregex;
  672.             $feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
  673.  
  674.             //create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex)
  675.             $feedmatch2 = $match . $feedregex2;
  676.             $feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1);
  677.  
  678.             //if asked to, turn the feed queries into comment feed ones
  679.             if ($forcomments) {
  680.                 $feedquery .= '&withcomments=1';
  681.                 $feedquery2 .= '&withcomments=1';
  682.             }
  683.  
  684.             //start creating the array of rewrites for this dir
  685.             $rewrite = array();
  686.             if ($feed) //...adding on /feed/ regexes => queries
  687.                 $rewrite = array($feedmatch => $feedquery, $feedmatch2 => $feedquery2);
  688.             if ($paged) //...and /page/xx ones
  689.                 $rewrite = array_merge($rewrite, array($pagematch => $pagequery));
  690.  
  691.             //do endpoints
  692.             if ($endpoints) {
  693.                 foreach ($ep_query_append as $regex => $ep) {
  694.                     //add the endpoints on if the mask fits
  695.                     if ($ep[0] & $ep_mask || $ep[0] & $ep_mask_specific) {
  696.                         $rewrite[$match . $regex] = $index . '?' . $query . $ep[1] . $this->preg_index($num_toks + 2);
  697.                     }
  698.                 }
  699.             }
  700.  
  701.             //if we've got some tags in this dir
  702.             if ($num_toks) {
  703.                 $post = false;
  704.                 $page = false;
  705.  
  706.                 //check to see if this dir is permalink-level: i.e. the structure specifies an
  707.                 //individual post. Do this by checking it contains at least one of 1) post name,
  708.                 //2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
  709.                 //minute all present). Set these flags now as we need them for the endpoints.
  710.                 if (strpos($struct, '%postname%') !== false || strpos($struct, '%post_id%') !== false
  711.                         || strpos($struct, '%pagename%') !== false
  712.                         || (strpos($struct, '%year%') !== false && strpos($struct, '%monthnum%') !== false && strpos($struct, '%day%') !== false && strpos($struct, '%hour%') !== false && strpos($struct, '%minute%') !== false && strpos($struct, '%second%') !== false)) {
  713.                     $post = true;
  714.                     if (strpos($struct, '%pagename%') !== false)
  715.                         $page = true;
  716.                 }
  717.  
  718.                 //if we're creating rules for a permalink, do all the endpoints like attachments etc
  719.                 if ($post) {
  720.                     $post = true;
  721.                     //create query and regex for trackback
  722.                     $trackbackmatch = $match . $trackbackregex;
  723.                     $trackbackquery = $trackbackindex . '?' . $query . '&tb=1';
  724.                     //trim slashes from the end of the regex for this dir
  725.                     $match = rtrim($match, '/');
  726.                     //get rid of brackets
  727.                     $submatchbase = str_replace(array('(',')'),'',$match);
  728.  
  729.                     //add a rule for at attachments, which take the form of <permalink>/some-text
  730.                     $sub1 = $submatchbase . '/([^/]+)/';
  731.                     $sub1tb = $sub1 . $trackbackregex; //add trackback regex <permalink>/trackback/...
  732.                     $sub1feed = $sub1 . $feedregex; //and <permalink>/feed/(atom|...)
  733.                     $sub1feed2 = $sub1 . $feedregex2; //and <permalink>/(feed|atom...)
  734.                     //add an ? as we don't have to match that last slash, and finally a $ so we
  735.                     //match to the end of the URL
  736.  
  737.                     //add another rule to match attachments in the explicit form:
  738.                     //<permalink>/attachment/some-text
  739.                     $sub2 = $submatchbase . '/attachment/([^/]+)/';
  740.                     $sub2tb = $sub2 . $trackbackregex; //and add trackbacks <permalink>/attachment/trackback
  741.                     $sub2feed = $sub2 . $feedregex;    //feeds, <permalink>/attachment/feed/(atom|...)
  742.                     $sub2feed2 = $sub2 . $feedregex2;  //and feeds again on to this <permalink>/attachment/(feed|atom...)
  743.  
  744.                     //create queries for these extra tag-ons we've just dealt with
  745.                     $subquery = $index . '?attachment=' . $this->preg_index(1);
  746.                     $subtbquery = $subquery . '&tb=1';
  747.                     $subfeedquery = $subquery . '&feed=' . $this->preg_index(2);
  748.  
  749.                     //do endpoints for attachments
  750.                     if (! empty($endpoint) ) { foreach ($ep_query_append as $regex => $ep) {
  751.                         if ($ep[0] & EP_ATTACHMENT) {
  752.                             $rewrite[$sub1 . $regex] = $subquery . '?' . $ep[1] . $this->preg_index(2);
  753.                             $rewrite[$sub2 . $regex] = $subquery . '?' . $ep[1] . $this->preg_index(2);
  754.                         }
  755.                     } }
  756.  
  757.                     //now we've finished with endpoints, finish off the $sub1 and $sub2 matches
  758.                     $sub1 .= '?$';
  759.                     $sub2 .= '?$';
  760.  
  761.                     //allow URLs like <permalink>/2 for <permalink>/page/2
  762.                     $match = $match . '(/[0-9]+)?/?$';
  763.                     $query = $index . '?' . $query . '&page=' . $this->preg_index($num_toks + 1);
  764.                 } else { //not matching a permalink so this is a lot simpler
  765.                     //close the match and finalise the query
  766.                     $match .= '?$';
  767.                     $query = $index . '?' . $query;
  768.                 }
  769.  
  770.                 //create the final array for this dir by joining the $rewrite array (which currently
  771.                 //only contains rules/queries for trackback, pages etc) to the main regex/query for
  772.                 //this dir
  773.                 $rewrite = array_merge($rewrite, array($match => $query));
  774.  
  775.                 //if we're matching a permalink, add those extras (attachments etc) on
  776.                 if ($post) {
  777.                     //add trackback
  778.                     $rewrite = array_merge(array($trackbackmatch => $trackbackquery), $rewrite);
  779.  
  780.                     //add regexes/queries for attachments, attachment trackbacks and so on
  781.                     if ( ! $page ) //require <permalink>/attachment/stuff form for pages because of confusion with subpages
  782.                         $rewrite = array_merge($rewrite, array($sub1 => $subquery, $sub1tb => $subtbquery, $sub1feed => $subfeedquery, $sub1feed2 => $subfeedquery));
  783.                     $rewrite = array_merge(array($sub2 => $subquery, $sub2tb => $subtbquery, $sub2feed => $subfeedquery, $sub2feed2 => $subfeedquery), $rewrite);
  784.                 }
  785.             } //if($num_toks)
  786.             //add the rules for this dir to the accumulating $post_rewrite
  787.             $post_rewrite = array_merge($rewrite, $post_rewrite);
  788.         } //foreach ($dir)
  789.         return $post_rewrite; //the finished rules. phew!
  790.     }
  791.  
  792.     function generate_rewrite_rule($permalink_structure, $walk_dirs = false) {
  793.         return $this->generate_rewrite_rules($permalink_structure, EP_NONE, false, false, false, $walk_dirs);
  794.     }
  795.  
  796.     /* rewrite_rules
  797.      * Construct rewrite matches and queries from permalink structure.
  798.      * Returns an associate array of matches and queries.
  799.      */
  800.     function rewrite_rules() {
  801.         $rewrite = array();
  802.  
  803.         if (empty($this->permalink_structure)) {
  804.             return $rewrite;
  805.         }
  806.  
  807.         // robots.txt
  808.         $robots_rewrite = array('robots.txt$' => $this->index . '?robots=1');
  809.  
  810.         //Default Feed rules - These are require to allow for the direct access files to work with permalink structure starting with %category%
  811.         $default_feeds = array(    '.*wp-atom.php$'    =>    $this->index .'?feed=atom',
  812.                                 '.*wp-rdf.php$'    =>    $this->index .'?feed=rdf',
  813.                                 '.*wp-rss.php$'    =>    $this->index .'?feed=rss',
  814.                                 '.*wp-rss2.php$'    =>    $this->index .'?feed=rss2',
  815.                                 '.*wp-feed.php$'    =>    $this->index .'?feed=feed',
  816.                                 '.*wp-commentsrss2.php$'    =>    $this->index . '?feed=rss2&withcomments=1');
  817.  
  818.         // Post
  819.         $post_rewrite = $this->generate_rewrite_rules($this->permalink_structure, EP_PERMALINK);
  820.         $post_rewrite = apply_filters('post_rewrite_rules', $post_rewrite);
  821.  
  822.         // Date
  823.         $date_rewrite = $this->generate_rewrite_rules($this->get_date_permastruct(), EP_DATE);
  824.         $date_rewrite = apply_filters('date_rewrite_rules', $date_rewrite);
  825.  
  826.         // Root
  827.         $root_rewrite = $this->generate_rewrite_rules($this->root . '/', EP_ROOT);
  828.         $root_rewrite = apply_filters('root_rewrite_rules', $root_rewrite);
  829.  
  830.         // Comments
  831.         $comments_rewrite = $this->generate_rewrite_rules($this->root . $this->comments_base, EP_COMMENTS, true, true, true, false);
  832.         $comments_rewrite = apply_filters('comments_rewrite_rules', $comments_rewrite);
  833.  
  834.         // Search
  835.         $search_structure = $this->get_search_permastruct();
  836.         $search_rewrite = $this->generate_rewrite_rules($search_structure, EP_SEARCH);
  837.         $search_rewrite = apply_filters('search_rewrite_rules', $search_rewrite);
  838.  
  839.         // Categories
  840.         $category_rewrite = $this->generate_rewrite_rules($this->get_category_permastruct(), EP_CATEGORIES);
  841.         $category_rewrite = apply_filters('category_rewrite_rules', $category_rewrite);
  842.  
  843.         // Tags
  844.         $tag_rewrite = $this->generate_rewrite_rules($this->get_tag_permastruct(), EP_TAGS);
  845.         $tag_rewrite = apply_filters('tag_rewrite_rules', $tag_rewrite);
  846.  
  847.         // Authors
  848.         $author_rewrite = $this->generate_rewrite_rules($this->get_author_permastruct(), EP_AUTHORS);
  849.         $author_rewrite = apply_filters('author_rewrite_rules', $author_rewrite);
  850.  
  851.         // Pages
  852.         $page_rewrite = $this->page_rewrite_rules();
  853.         $page_rewrite = apply_filters('page_rewrite_rules', $page_rewrite);
  854.  
  855.         // Extra permastructs
  856.         foreach ( $this->extra_permastructs as $permastruct )
  857.             $this->extra_rules_top = array_merge($this->extra_rules_top, $this->generate_rewrite_rules($permastruct, EP_NONE));
  858.  
  859.         // Put them together.
  860.         if ( $this->use_verbose_page_rules )
  861.             $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $page_rewrite, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $this->extra_rules);
  862.         else
  863.             $this->rules = array_merge($this->extra_rules_top, $robots_rewrite, $default_feeds, $root_rewrite, $comments_rewrite, $search_rewrite, $category_rewrite, $tag_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules);
  864.  
  865.         do_action_ref_array('generate_rewrite_rules', array(&$this));
  866.         $this->rules = apply_filters('rewrite_rules_array', $this->rules);
  867.  
  868.         return $this->rules;
  869.     }
  870.  
  871.     function wp_rewrite_rules() {
  872.         $this->rules = get_option('rewrite_rules');
  873.         if ( empty($this->rules) ) {
  874.             $this->matches = 'matches';
  875.             $this->rewrite_rules();
  876.             update_option('rewrite_rules', $this->rules);
  877.         }
  878.  
  879.         return $this->rules;
  880.     }
  881.  
  882.     function mod_rewrite_rules() {
  883.         if ( ! $this->using_permalinks()) {
  884.             return '';
  885.         }
  886.  
  887.         $site_root = parse_url(get_option('siteurl'));
  888.         $site_root = trailingslashit($site_root['path']);
  889.  
  890.         $home_root = parse_url(get_option('home'));
  891.         $home_root = trailingslashit($home_root['path']);
  892.  
  893.         $rules = "<IfModule mod_rewrite.c>\n";
  894.         $rules .= "RewriteEngine On\n";
  895.         $rules .= "RewriteBase $home_root\n";
  896.  
  897.         //add in the rules that don't redirect to WP's index.php (and thus shouldn't be handled by WP at all)
  898.         foreach ($this->non_wp_rules as $match => $query) {
  899.             // Apache 1.3 does not support the reluctant (non-greedy) modifier.
  900.             $match = str_replace('.+?', '.+', $match);
  901.  
  902.             // If the match is unanchored and greedy, prepend rewrite conditions
  903.             // to avoid infinite redirects and eclipsing of real files.
  904.             if ($match == '(.+)/?$' || $match == '([^/]+)/?$' ) {
  905.                 //nada.
  906.             }
  907.  
  908.             $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
  909.         }
  910.  
  911.         if ($this->use_verbose_rules) {
  912.             $this->matches = '';
  913.             $rewrite = $this->rewrite_rules();
  914.             $num_rules = count($rewrite);
  915.             $rules .= "RewriteCond %{REQUEST_FILENAME} -f [OR]\n" .
  916.                 "RewriteCond %{REQUEST_FILENAME} -d\n" .
  917.                 "RewriteRule ^.*$ - [S=$num_rules]\n";
  918.  
  919.             foreach ($rewrite as $match => $query) {
  920.                 // Apache 1.3 does not support the reluctant (non-greedy) modifier.
  921.                 $match = str_replace('.+?', '.+', $match);
  922.  
  923.                 // If the match is unanchored and greedy, prepend rewrite conditions
  924.                 // to avoid infinite redirects and eclipsing of real files.
  925.                 if ($match == '(.+)/?$' || $match == '([^/]+)/?$' ) {
  926.                     //nada.
  927.                 }
  928.  
  929.                 if (strpos($query, $this->index) !== false) {
  930.                     $rules .= 'RewriteRule ^' . $match . ' ' . $home_root . $query . " [QSA,L]\n";
  931.                 } else {
  932.                     $rules .= 'RewriteRule ^' . $match . ' ' . $site_root . $query . " [QSA,L]\n";
  933.                 }
  934.             }
  935.         } else {
  936.             $rules .= "RewriteCond %{REQUEST_FILENAME} !-f\n" .
  937.                 "RewriteCond %{REQUEST_FILENAME} !-d\n" .
  938.                 "RewriteRule . {$home_root}{$this->index} [L]\n";
  939.         }
  940.  
  941.         $rules .= "</IfModule>\n";
  942.  
  943.         $rules = apply_filters('mod_rewrite_rules', $rules);
  944.         $rules = apply_filters('rewrite_rules', $rules);  // Deprecated
  945.  
  946.         return $rules;
  947.     }
  948.  
  949.     //Add a straight rewrite rule
  950.     function add_rule($regex, $redirect, $after = 'bottom') {
  951.         //get everything up to the first ?
  952.         $index = (strpos($redirect, '?') == false ? strlen($redirect) : strpos($redirect, '?'));
  953.         $front = substr($redirect, 0, $index);
  954.         if ($front != $this->index) { //it doesn't redirect to WP's index.php
  955.             $this->add_external_rule($regex, $redirect);
  956.         } else {
  957.             if ( 'bottom' == $after)
  958.                 $this->extra_rules = array_merge($this->extra_rules, array($regex => $redirect));
  959.             else
  960.                 $this->extra_rules_top = array_merge($this->extra_rules_top, array($regex => $redirect));
  961.             //$this->extra_rules[$regex] = $redirect;
  962.         }
  963.     }
  964.  
  965.     //add a rule that doesn't redirect to index.php
  966.     function add_external_rule($regex, $redirect) {
  967.         $this->non_wp_rules[$regex] = $redirect;
  968.     }
  969.  
  970.     //add an endpoint, like /trackback/, to be inserted after certain URL types (specified in $places)
  971.     function add_endpoint($name, $places) {
  972.         global $wp;
  973.         $this->endpoints[] = array ( $places, $name );
  974.         $wp->add_query_var($name);
  975.     }
  976.  
  977.     function add_permastruct($name, $struct, $with_front = true) {
  978.         if ( $with_front )
  979.             $struct = $this->front . $struct;
  980.         $this->extra_permastructs[$name] = $struct;
  981.     }
  982.  
  983.     function flush_rules() {
  984.         delete_option('rewrite_rules');
  985.         $this->wp_rewrite_rules();
  986.         if ( function_exists('save_mod_rewrite_rules') )
  987.             save_mod_rewrite_rules();
  988.     }
  989.  
  990.     function init() {
  991.         $this->extra_rules = $this->non_wp_rules = $this->endpoints = array();
  992.         $this->permalink_structure = get_option('permalink_structure');
  993.         $this->front = substr($this->permalink_structure, 0, strpos($this->permalink_structure, '%'));
  994.         $this->root = '';
  995.         if ($this->using_index_permalinks()) {
  996.             $this->root = $this->index . '/';
  997.         }
  998.         $this->category_base = get_option( 'category_base' );
  999.         $this->tag_base = get_option( 'tag_base' );
  1000.         unset($this->category_structure);
  1001.         unset($this->author_structure);
  1002.         unset($this->date_structure);
  1003.         unset($this->page_structure);
  1004.         unset($this->search_structure);
  1005.         unset($this->feed_structure);
  1006.         unset($this->comment_feed_structure);
  1007.         $this->use_trailing_slashes = ( substr($this->permalink_structure, -1, 1) == '/' ) ? true : false;
  1008.  
  1009.         // Enable generic rules for pages if permalink structure doesn't begin with a wildcard.
  1010.         $structure = ltrim($this->permalink_structure, '/');
  1011.         if ( $this->using_index_permalinks() )
  1012.             $structure = ltrim($this->permalink_structure, $this->index . '/');
  1013.         if ( 0 === strpos($structure, '%postname%') ||
  1014.              0 === strpos($structure, '%category%') ||
  1015.              0 === strpos($structure, '%tag%') ||
  1016.              0 === strpos($structure, '%author%') )
  1017.              $this->use_verbose_page_rules = true;
  1018.         else
  1019.             $this->use_verbose_page_rules = false;
  1020.     }
  1021.  
  1022.     function set_permalink_structure($permalink_structure) {
  1023.         if ($permalink_structure != $this->permalink_structure) {
  1024.             update_option('permalink_structure', $permalink_structure);
  1025.             $this->init();
  1026.         }
  1027.     }
  1028.  
  1029.     function set_category_base($category_base) {
  1030.         if ($category_base != $this->category_base) {
  1031.             update_option('category_base', $category_base);
  1032.             $this->init();
  1033.         }
  1034.     }
  1035.  
  1036.     function set_tag_base( $tag_base ) {
  1037.         if ( $tag_base != $this->tag_base ) {
  1038.             update_option( 'tag_base', $tag_base );
  1039.             $this->init();
  1040.         }
  1041.     }
  1042.  
  1043.     function WP_Rewrite() {
  1044.         $this->init();
  1045.     }
  1046. }
  1047.  
  1048. ?>
  1049.