Created
July 5, 2012 20:08
-
-
Save danlamanna/3056144 to your computer and use it in GitHub Desktop.
Code is Poetry
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Generate rewrite rules from a permalink structure. | |
* | |
* The main WP_Rewrite function for building the rewrite rule list. The | |
* contents of the function is a mix of black magic and regular expressions, | |
* so best just ignore the contents and move to the parameters. | |
* | |
* @since 1.5.0 | |
* @access public | |
* | |
* @param string $permalink_structure The permalink structure. | |
* @param int $ep_mask Endpoint mask defining what endpoints are added to the structure. Default is EP_NONE. | |
* @param bool $paged Should archive pagination rules be added for the structure? Default is true. | |
* @param bool $feed Should feed rewrite rules be added for the structure? Default is true. | |
* @param bool $forcomments Should the feed rules be a query for a comments feed? Default is false. | |
* @param bool $walk_dirs Should the 'directories' making up the structure be walked over and rewrite rules | |
* built for each in turn? Default is true. | |
* @param bool $endpoints Should endpoints be applied to the generated rewrite rules? Default is true. | |
* @return array Rewrite rule list. | |
*/ | |
function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) { | |
//build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/? | |
$feedregex2 = ''; | |
foreach ( (array) $this->feeds as $feed_name) | |
$feedregex2 .= $feed_name . '|'; | |
$feedregex2 = '(' . trim($feedregex2, '|') . ')/?$'; | |
//$feedregex is identical but with /feed/ added on as well, so URLs like <permalink>/feed/atom | |
//and <permalink>/atom are both possible | |
$feedregex = $this->feed_base . '/' . $feedregex2; | |
//build a regex to match the trackback and page/xx parts of URLs | |
$trackbackregex = 'trackback/?$'; | |
$pageregex = $this->pagination_base . '/?([0-9]{1,})/?$'; | |
$commentregex = 'comment-page-([0-9]{1,})/?$'; | |
//build up an array of endpoint regexes to append => queries to append | |
if ( $endpoints ) { | |
$ep_query_append = array (); | |
foreach ( (array) $this->endpoints as $endpoint) { | |
//match everything after the endpoint name, but allow for nothing to appear there | |
$epmatch = $endpoint[1] . '(/(.*))?/?$'; | |
//this will be appended on to the rest of the query for each dir | |
$epquery = '&' . $endpoint[1] . '='; | |
$ep_query_append[$epmatch] = array ( $endpoint[0], $epquery ); | |
} | |
} | |
//get everything up to the first rewrite tag | |
$front = substr($permalink_structure, 0, strpos($permalink_structure, '%')); | |
//build an array of the tags (note that said array ends up being in $tokens[0]) | |
preg_match_all('/%.+?%/', $permalink_structure, $tokens); | |
$num_tokens = count($tokens[0]); | |
$index = $this->index; //probably 'index.php' | |
$feedindex = $index; | |
$trackbackindex = $index; | |
//build a list from the rewritecode and queryreplace arrays, that will look something like | |
//tagname=$matches[i] where i is the current $i | |
for ( $i = 0; $i < $num_tokens; ++$i ) { | |
if ( 0 < $i ) | |
$queries[$i] = $queries[$i - 1] . '&'; | |
else | |
$queries[$i] = ''; | |
$query_token = str_replace($this->rewritecode, $this->queryreplace, $tokens[0][$i]) . $this->preg_index($i+1); | |
$queries[$i] .= $query_token; | |
} | |
//get the structure, minus any cruft (stuff that isn't tags) at the front | |
$structure = $permalink_structure; | |
if ( $front != '/' ) | |
$structure = str_replace($front, '', $structure); | |
//create a list of dirs to walk over, making rewrite rules for each level | |
//so for example, a $structure of /%year%/%monthnum%/%postname% would create | |
//rewrite rules for /%year%/, /%year%/%monthnum%/ and /%year%/%monthnum%/%postname% | |
$structure = trim($structure, '/'); | |
$dirs = $walk_dirs ? explode('/', $structure) : array( $structure ); | |
$num_dirs = count($dirs); | |
//strip slashes from the front of $front | |
$front = preg_replace('|^/+|', '', $front); | |
//the main workhorse loop | |
$post_rewrite = array(); | |
$struct = $front; | |
for ( $j = 0; $j < $num_dirs; ++$j ) { | |
//get the struct for this dir, and trim slashes off the front | |
$struct .= $dirs[$j] . '/'; //accumulate. see comment near explode('/', $structure) above | |
$struct = ltrim($struct, '/'); | |
//replace tags with regexes | |
$match = str_replace($this->rewritecode, $this->rewritereplace, $struct); | |
//make a list of tags, and store how many there are in $num_toks | |
$num_toks = preg_match_all('/%.+?%/', $struct, $toks); | |
//get the 'tagname=$matches[i]' | |
$query = ( isset($queries) && is_array($queries) && !empty($num_toks) ) ? $queries[$num_toks - 1] : ''; | |
//set up $ep_mask_specific which is used to match more specific URL types | |
switch ( $dirs[$j] ) { | |
case '%year%': | |
$ep_mask_specific = EP_YEAR; | |
break; | |
case '%monthnum%': | |
$ep_mask_specific = EP_MONTH; | |
break; | |
case '%day%': | |
$ep_mask_specific = EP_DAY; | |
break; | |
default: | |
$ep_mask_specific = EP_NONE; | |
} | |
//create query for /page/xx | |
$pagematch = $match . $pageregex; | |
$pagequery = $index . '?' . $query . '&paged=' . $this->preg_index($num_toks + 1); | |
//create query for /comment-page-xx | |
$commentmatch = $match . $commentregex; | |
$commentquery = $index . '?' . $query . '&cpage=' . $this->preg_index($num_toks + 1); | |
if ( get_option('page_on_front') ) { | |
//create query for Root /comment-page-xx | |
$rootcommentmatch = $match . $commentregex; | |
$rootcommentquery = $index . '?' . $query . '&page_id=' . get_option('page_on_front') . '&cpage=' . $this->preg_index($num_toks + 1); | |
} | |
//create query for /feed/(feed|atom|rss|rss2|rdf) | |
$feedmatch = $match . $feedregex; | |
$feedquery = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1); | |
//create query for /(feed|atom|rss|rss2|rdf) (see comment near creation of $feedregex) | |
$feedmatch2 = $match . $feedregex2; | |
$feedquery2 = $feedindex . '?' . $query . '&feed=' . $this->preg_index($num_toks + 1); | |
//if asked to, turn the feed queries into comment feed ones | |
if ( $forcomments ) { | |
$feedquery .= '&withcomments=1'; | |
$feedquery2 .= '&withcomments=1'; | |
} | |
//start creating the array of rewrites for this dir | |
$rewrite = array(); | |
if ( $feed ) //...adding on /feed/ regexes => queries | |
$rewrite = array($feedmatch => $feedquery, $feedmatch2 => $feedquery2); | |
if ( $paged ) //...and /page/xx ones | |
$rewrite = array_merge($rewrite, array($pagematch => $pagequery)); | |
//only on pages with comments add ../comment-page-xx/ | |
if ( EP_PAGES & $ep_mask || EP_PERMALINK & $ep_mask ) | |
$rewrite = array_merge($rewrite, array($commentmatch => $commentquery)); | |
else if ( EP_ROOT & $ep_mask && get_option('page_on_front') ) | |
$rewrite = array_merge($rewrite, array($rootcommentmatch => $rootcommentquery)); | |
//do endpoints | |
if ( $endpoints ) { | |
foreach ( (array) $ep_query_append as $regex => $ep) { | |
//add the endpoints on if the mask fits | |
if ( $ep[0] & $ep_mask || $ep[0] & $ep_mask_specific ) | |
$rewrite[$match . $regex] = $index . '?' . $query . $ep[1] . $this->preg_index($num_toks + 2); | |
} | |
} | |
//if we've got some tags in this dir | |
if ( $num_toks ) { | |
$post = false; | |
$page = false; | |
//check to see if this dir is permalink-level: i.e. the structure specifies an | |
//individual post. Do this by checking it contains at least one of 1) post name, | |
//2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and | |
//minute all present). Set these flags now as we need them for the endpoints. | |
if ( strpos($struct, '%postname%') !== false | |
|| strpos($struct, '%post_id%') !== false | |
|| strpos($struct, '%pagename%') !== false | |
|| (strpos($struct, '%year%') !== false && strpos($struct, '%monthnum%') !== false && strpos($struct, '%day%') !== false && strpos($struct, '%hour%') !== false && strpos($struct, '%minute%') !== false && strpos($struct, '%second%') !== false) | |
) { | |
$post = true; | |
if ( strpos($struct, '%pagename%') !== false ) | |
$page = true; | |
} | |
if ( ! $post ) { | |
// For custom post types, we need to add on endpoints as well. | |
foreach ( get_post_types( array('_builtin' => false ) ) as $ptype ) { | |
if ( strpos($struct, "%$ptype%") !== false ) { | |
$post = true; | |
$page = is_post_type_hierarchical( $ptype ); // This is for page style attachment url's | |
break; | |
} | |
} | |
} | |
//if we're creating rules for a permalink, do all the endpoints like attachments etc | |
if ( $post ) { | |
//create query and regex for trackback | |
$trackbackmatch = $match . $trackbackregex; | |
$trackbackquery = $trackbackindex . '?' . $query . '&tb=1'; | |
//trim slashes from the end of the regex for this dir | |
$match = rtrim($match, '/'); | |
//get rid of brackets | |
$submatchbase = str_replace( array('(', ')'), '', $match); | |
//add a rule for at attachments, which take the form of <permalink>/some-text | |
$sub1 = $submatchbase . '/([^/]+)/'; | |
$sub1tb = $sub1 . $trackbackregex; //add trackback regex <permalink>/trackback/... | |
$sub1feed = $sub1 . $feedregex; //and <permalink>/feed/(atom|...) | |
$sub1feed2 = $sub1 . $feedregex2; //and <permalink>/(feed|atom...) | |
$sub1comment = $sub1 . $commentregex; //and <permalink>/comment-page-xx | |
//add another rule to match attachments in the explicit form: | |
//<permalink>/attachment/some-text | |
$sub2 = $submatchbase . '/attachment/([^/]+)/'; | |
$sub2tb = $sub2 . $trackbackregex; //and add trackbacks <permalink>/attachment/trackback | |
$sub2feed = $sub2 . $feedregex; //feeds, <permalink>/attachment/feed/(atom|...) | |
$sub2feed2 = $sub2 . $feedregex2; //and feeds again on to this <permalink>/attachment/(feed|atom...) | |
$sub2comment = $sub2 . $commentregex; //and <permalink>/comment-page-xx | |
//create queries for these extra tag-ons we've just dealt with | |
$subquery = $index . '?attachment=' . $this->preg_index(1); | |
$subtbquery = $subquery . '&tb=1'; | |
$subfeedquery = $subquery . '&feed=' . $this->preg_index(2); | |
$subcommentquery = $subquery . '&cpage=' . $this->preg_index(2); | |
//do endpoints for attachments | |
if ( !empty($endpoints) ) { | |
foreach ( (array) $ep_query_append as $regex => $ep ) { | |
if ( $ep[0] & EP_ATTACHMENT ) { | |
$rewrite[$sub1 . $regex] = $subquery . $ep[1] . $this->preg_index(2); | |
$rewrite[$sub2 . $regex] = $subquery . $ep[1] . $this->preg_index(2); | |
} | |
} | |
} | |
//now we've finished with endpoints, finish off the $sub1 and $sub2 matches | |
//add a ? as we don't have to match that last slash, and finally a $ so we | |
//match to the end of the URL | |
$sub1 .= '?$'; | |
$sub2 .= '?$'; | |
//post pagination, e.g. <permalink>/2/ | |
$match = $match . '(/[0-9]+)?/?$'; | |
$query = $index . '?' . $query . '&page=' . $this->preg_index($num_toks + 1); | |
} else { //not matching a permalink so this is a lot simpler | |
//close the match and finalise the query | |
$match .= '?$'; | |
$query = $index . '?' . $query; | |
} | |
//create the final array for this dir by joining the $rewrite array (which currently | |
//only contains rules/queries for trackback, pages etc) to the main regex/query for | |
//this dir | |
$rewrite = array_merge($rewrite, array($match => $query)); | |
//if we're matching a permalink, add those extras (attachments etc) on | |
if ( $post ) { | |
//add trackback | |
$rewrite = array_merge(array($trackbackmatch => $trackbackquery), $rewrite); | |
//add regexes/queries for attachments, attachment trackbacks and so on | |
if ( ! $page ) //require <permalink>/attachment/stuff form for pages because of confusion with subpages | |
$rewrite = array_merge($rewrite, array($sub1 => $subquery, $sub1tb => $subtbquery, $sub1feed => $subfeedquery, $sub1feed2 => $subfeedquery, $sub1comment => $subcommentquery)); | |
$rewrite = array_merge(array($sub2 => $subquery, $sub2tb => $subtbquery, $sub2feed => $subfeedquery, $sub2feed2 => $subfeedquery, $sub2comment => $subcommentquery), $rewrite); | |
} | |
} //if($num_toks) | |
//add the rules for this dir to the accumulating $post_rewrite | |
$post_rewrite = array_merge($rewrite, $post_rewrite); | |
} //foreach ($dir) | |
return $post_rewrite; //the finished rules. phew! | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Making fun of WordPress.