home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-includes / rewrite.php < prev    next >
Encoding:
PHP Script  |  2017-10-06  |  17.1 KB  |  608 lines

  1. <?php
  2. /**
  3.  * WordPress Rewrite API
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Rewrite
  7.  */
  8.  
  9. /**
  10.  * Endpoint Mask for default, which is nothing.
  11.  *
  12.  * @since 2.1.0
  13.  */
  14. define('EP_NONE', 0);
  15.  
  16. /**
  17.  * Endpoint Mask for Permalink.
  18.  *
  19.  * @since 2.1.0
  20.  */
  21. define('EP_PERMALINK', 1);
  22.  
  23. /**
  24.  * Endpoint Mask for Attachment.
  25.  *
  26.  * @since 2.1.0
  27.  */
  28. define('EP_ATTACHMENT', 2);
  29.  
  30. /**
  31.  * Endpoint Mask for date.
  32.  *
  33.  * @since 2.1.0
  34.  */
  35. define('EP_DATE', 4);
  36.  
  37. /**
  38.  * Endpoint Mask for year
  39.  *
  40.  * @since 2.1.0
  41.  */
  42. define('EP_YEAR', 8);
  43.  
  44. /**
  45.  * Endpoint Mask for month.
  46.  *
  47.  * @since 2.1.0
  48.  */
  49. define('EP_MONTH', 16);
  50.  
  51. /**
  52.  * Endpoint Mask for day.
  53.  *
  54.  * @since 2.1.0
  55.  */
  56. define('EP_DAY', 32);
  57.  
  58. /**
  59.  * Endpoint Mask for root.
  60.  *
  61.  * @since 2.1.0
  62.  */
  63. define('EP_ROOT', 64);
  64.  
  65. /**
  66.  * Endpoint Mask for comments.
  67.  *
  68.  * @since 2.1.0
  69.  */
  70. define('EP_COMMENTS', 128);
  71.  
  72. /**
  73.  * Endpoint Mask for searches.
  74.  *
  75.  * @since 2.1.0
  76.  */
  77. define('EP_SEARCH', 256);
  78.  
  79. /**
  80.  * Endpoint Mask for categories.
  81.  *
  82.  * @since 2.1.0
  83.  */
  84. define('EP_CATEGORIES', 512);
  85.  
  86. /**
  87.  * Endpoint Mask for tags.
  88.  *
  89.  * @since 2.3.0
  90.  */
  91. define('EP_TAGS', 1024);
  92.  
  93. /**
  94.  * Endpoint Mask for authors.
  95.  *
  96.  * @since 2.1.0
  97.  */
  98. define('EP_AUTHORS', 2048);
  99.  
  100. /**
  101.  * Endpoint Mask for pages.
  102.  *
  103.  * @since 2.1.0
  104.  */
  105. define('EP_PAGES', 4096);
  106.  
  107. /**
  108.  * Endpoint Mask for all archive views.
  109.  *
  110.  * @since 3.7.0
  111.  */
  112. define( 'EP_ALL_ARCHIVES', EP_DATE | EP_YEAR | EP_MONTH | EP_DAY | EP_CATEGORIES | EP_TAGS | EP_AUTHORS );
  113.  
  114. /**
  115.  * Endpoint Mask for everything.
  116.  *
  117.  * @since 2.1.0
  118.  */
  119. define( 'EP_ALL', EP_PERMALINK | EP_ATTACHMENT | EP_ROOT | EP_COMMENTS | EP_SEARCH | EP_PAGES | EP_ALL_ARCHIVES );
  120.  
  121. /**
  122.  * Adds a rewrite rule that transforms a URL structure to a set of query vars.
  123.  *
  124.  * Any value in the $after parameter that isn't 'bottom' will result in the rule
  125.  * being placed at the top of the rewrite rules.
  126.  *
  127.  * @since 2.1.0
  128.  * @since 4.4.0 Array support was added to the `$query` parameter.
  129.  *
  130.  * @global WP_Rewrite $wp_rewrite WordPress Rewrite Component.
  131.  *
  132.  * @param string       $regex Regular expression to match request against.
  133.  * @param string|array $query The corresponding query vars for this rewrite rule.
  134.  * @param string       $after Optional. Priority of the new rule. Accepts 'top'
  135.  *                            or 'bottom'. Default 'bottom'.
  136.  */
  137. function add_rewrite_rule( $regex, $query, $after = 'bottom' ) {
  138.     global $wp_rewrite;
  139.  
  140.     $wp_rewrite->add_rule( $regex, $query, $after );
  141. }
  142.  
  143. /**
  144.  * Add a new rewrite tag (like %postname%).
  145.  *
  146.  * The $query parameter is optional. If it is omitted you must ensure that
  147.  * you call this on, or before, the {@see 'init'} hook. This is because $query defaults
  148.  * to "$tag=", and for this to work a new query var has to be added.
  149.  *
  150.  * @since 2.1.0
  151.  *
  152.  * @global WP_Rewrite $wp_rewrite
  153.  * @global WP         $wp
  154.  *
  155.  * @param string $tag   Name of the new rewrite tag.
  156.  * @param string $regex Regular expression to substitute the tag for in rewrite rules.
  157.  * @param string $query Optional. String to append to the rewritten query. Must end in '='. Default empty.
  158.  */
  159. function add_rewrite_tag( $tag, $regex, $query = '' ) {
  160.     // validate the tag's name
  161.     if ( strlen( $tag ) < 3 || $tag[0] != '%' || $tag[ strlen($tag) - 1 ] != '%' )
  162.         return;
  163.  
  164.     global $wp_rewrite, $wp;
  165.  
  166.     if ( empty( $query ) ) {
  167.         $qv = trim( $tag, '%' );
  168.         $wp->add_query_var( $qv );
  169.         $query = $qv . '=';
  170.     }
  171.  
  172.     $wp_rewrite->add_rewrite_tag( $tag, $regex, $query );
  173. }
  174.  
  175. /**
  176.  * Removes an existing rewrite tag (like %postname%).
  177.  *
  178.  * @since 4.5.0
  179.  *
  180.  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
  181.  *
  182.  * @param string $tag Name of the rewrite tag.
  183.  */
  184. function remove_rewrite_tag( $tag ) {
  185.     global $wp_rewrite;
  186.     $wp_rewrite->remove_rewrite_tag( $tag );
  187. }
  188.  
  189. /**
  190.  * Add permalink structure.
  191.  *
  192.  * @since 3.0.0
  193.  *
  194.  * @see WP_Rewrite::add_permastruct()
  195.  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
  196.  *
  197.  * @param string $name   Name for permalink structure.
  198.  * @param string $struct Permalink structure.
  199.  * @param array  $args   Optional. Arguments for building the rules from the permalink structure,
  200.  *                       see WP_Rewrite::add_permastruct() for full details. Default empty array.
  201.  */
  202. function add_permastruct( $name, $struct, $args = array() ) {
  203.     global $wp_rewrite;
  204.  
  205.     // Back-compat for the old parameters: $with_front and $ep_mask.
  206.     if ( ! is_array( $args ) )
  207.         $args = array( 'with_front' => $args );
  208.     if ( func_num_args() == 4 )
  209.         $args['ep_mask'] = func_get_arg( 3 );
  210.  
  211.     $wp_rewrite->add_permastruct( $name, $struct, $args );
  212. }
  213.  
  214. /**
  215.  * Removes a permalink structure.
  216.  *
  217.  * Can only be used to remove permastructs that were added using add_permastruct().
  218.  * Built-in permastructs cannot be removed.
  219.  *
  220.  * @since 4.5.0
  221.  *
  222.  * @see WP_Rewrite::remove_permastruct()
  223.  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
  224.  *
  225.  * @param string $name Name for permalink structure.
  226.  */
  227. function remove_permastruct( $name ) {
  228.     global $wp_rewrite;
  229.  
  230.     $wp_rewrite->remove_permastruct( $name );
  231. }
  232.  
  233. /**
  234.  * Add a new feed type like /atom1/.
  235.  *
  236.  * @since 2.1.0
  237.  *
  238.  * @global WP_Rewrite $wp_rewrite
  239.  *
  240.  * @param string   $feedname Feed name.
  241.  * @param callable $function Callback to run on feed display.
  242.  * @return string Feed action name.
  243.  */
  244. function add_feed( $feedname, $function ) {
  245.     global $wp_rewrite;
  246.  
  247.     if ( ! in_array( $feedname, $wp_rewrite->feeds ) ) {
  248.         $wp_rewrite->feeds[] = $feedname;
  249.     }
  250.  
  251.     $hook = 'do_feed_' . $feedname;
  252.  
  253.     // Remove default function hook
  254.     remove_action( $hook, $hook );
  255.  
  256.     add_action( $hook, $function, 10, 2 );
  257.  
  258.     return $hook;
  259. }
  260.  
  261. /**
  262.  * Remove rewrite rules and then recreate rewrite rules.
  263.  *
  264.  * @since 3.0.0
  265.  *
  266.  * @global WP_Rewrite $wp_rewrite
  267.  *
  268.  * @param bool $hard Whether to update .htaccess (hard flush) or just update
  269.  *                      rewrite_rules transient (soft flush). Default is true (hard).
  270.  */
  271. function flush_rewrite_rules( $hard = true ) {
  272.     global $wp_rewrite;
  273.     $wp_rewrite->flush_rules( $hard );
  274. }
  275.  
  276. /**
  277.  * Add an endpoint, like /trackback/.
  278.  *
  279.  * Adding an endpoint creates extra rewrite rules for each of the matching
  280.  * places specified by the provided bitmask. For example:
  281.  *
  282.  *     add_rewrite_endpoint( 'json', EP_PERMALINK | EP_PAGES );
  283.  *
  284.  * will add a new rewrite rule ending with "json(/(.*))?/?$" for every permastruct
  285.  * that describes a permalink (post) or page. This is rewritten to "json=$match"
  286.  * where $match is the part of the URL matched by the endpoint regex (e.g. "foo" in
  287.  * "[permalink]/json/foo/").
  288.  *
  289.  * A new query var with the same name as the endpoint will also be created.
  290.  *
  291.  * When specifying $places ensure that you are using the EP_* constants (or a
  292.  * combination of them using the bitwise OR operator) as their values are not
  293.  * guaranteed to remain static (especially `EP_ALL`).
  294.  *
  295.  * Be sure to flush the rewrite rules - see flush_rewrite_rules() - when your plugin gets
  296.  * activated and deactivated.
  297.  *
  298.  * @since 2.1.0
  299.  * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
  300.  *
  301.  * @global WP_Rewrite $wp_rewrite
  302.  *
  303.  * @param string      $name      Name of the endpoint.
  304.  * @param int         $places    Endpoint mask describing the places the endpoint should be added.
  305.  * @param string|bool $query_var Name of the corresponding query variable. Pass `false` to skip registering a query_var
  306.  *                               for this endpoint. Defaults to the value of `$name`.
  307.  */
  308. function add_rewrite_endpoint( $name, $places, $query_var = true ) {
  309.     global $wp_rewrite;
  310.     $wp_rewrite->add_endpoint( $name, $places, $query_var );
  311. }
  312.  
  313. /**
  314.  * Filters the URL base for taxonomies.
  315.  *
  316.  * To remove any manually prepended /index.php/.
  317.  *
  318.  * @access private
  319.  * @since 2.6.0
  320.  *
  321.  * @param string $base The taxonomy base that we're going to filter
  322.  * @return string
  323.  */
  324. function _wp_filter_taxonomy_base( $base ) {
  325.     if ( !empty( $base ) ) {
  326.         $base = preg_replace( '|^/index\.php/|', '', $base );
  327.         $base = trim( $base, '/' );
  328.     }
  329.     return $base;
  330. }
  331.  
  332.  
  333. /**
  334.  * Resolve numeric slugs that collide with date permalinks.
  335.  *
  336.  * Permalinks of posts with numeric slugs can sometimes look to WP_Query::parse_query()
  337.  * like a date archive, as when your permalink structure is `/%year%/%postname%/` and
  338.  * a post with post_name '05' has the URL `/2015/05/`.
  339.  *
  340.  * This function detects conflicts of this type and resolves them in favor of the
  341.  * post permalink.
  342.  *
  343.  * Note that, since 4.3.0, wp_unique_post_slug() prevents the creation of post slugs
  344.  * that would result in a date archive conflict. The resolution performed in this
  345.  * function is primarily for legacy content, as well as cases when the admin has changed
  346.  * the site's permalink structure in a way that introduces URL conflicts.
  347.  *
  348.  * @since 4.3.0
  349.  *
  350.  * @param array $query_vars Optional. Query variables for setting up the loop, as determined in
  351.  *                          WP::parse_request(). Default empty array.
  352.  * @return array Returns the original array of query vars, with date/post conflicts resolved.
  353.  */
  354. function wp_resolve_numeric_slug_conflicts( $query_vars = array() ) {
  355.     if ( ! isset( $query_vars['year'] ) && ! isset( $query_vars['monthnum'] ) && ! isset( $query_vars['day'] ) ) {
  356.         return $query_vars;
  357.     }
  358.  
  359.     // Identify the 'postname' position in the permastruct array.
  360.     $permastructs   = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) );
  361.     $postname_index = array_search( '%postname%', $permastructs );
  362.  
  363.     if ( false === $postname_index ) {
  364.         return $query_vars;
  365.     }
  366.  
  367.     /*
  368.      * A numeric slug could be confused with a year, month, or day, depending on position. To account for
  369.      * the possibility of post pagination (eg 2015/2 for the second page of a post called '2015'), our
  370.      * `is_*` checks are generous: check for year-slug clashes when `is_year` *or* `is_month`, and check
  371.      * for month-slug clashes when `is_month` *or* `is_day`.
  372.      */
  373.     $compare = '';
  374.     if ( 0 === $postname_index && ( isset( $query_vars['year'] ) || isset( $query_vars['monthnum'] ) ) ) {
  375.         $compare = 'year';
  376.     } elseif ( '%year%' === $permastructs[ $postname_index - 1 ] && ( isset( $query_vars['monthnum'] ) || isset( $query_vars['day'] ) ) ) {
  377.         $compare = 'monthnum';
  378.     } elseif ( '%monthnum%' === $permastructs[ $postname_index - 1 ] && isset( $query_vars['day'] ) ) {
  379.         $compare = 'day';
  380.     }
  381.  
  382.     if ( ! $compare ) {
  383.         return $query_vars;
  384.     }
  385.  
  386.     // This is the potentially clashing slug.
  387.     $value = $query_vars[ $compare ];
  388.  
  389.     $post = get_page_by_path( $value, OBJECT, 'post' );
  390.     if ( ! ( $post instanceof WP_Post ) ) {
  391.         return $query_vars;
  392.     }
  393.  
  394.     // If the date of the post doesn't match the date specified in the URL, resolve to the date archive.
  395.     if ( preg_match( '/^([0-9]{4})\-([0-9]{2})/', $post->post_date, $matches ) && isset( $query_vars['year'] ) && ( 'monthnum' === $compare || 'day' === $compare ) ) {
  396.         // $matches[1] is the year the post was published.
  397.         if ( intval( $query_vars['year'] ) !== intval( $matches[1] ) ) {
  398.             return $query_vars;
  399.         }
  400.  
  401.         // $matches[2] is the month the post was published.
  402.         if ( 'day' === $compare && isset( $query_vars['monthnum'] ) && intval( $query_vars['monthnum'] ) !== intval( $matches[2] ) ) {
  403.             return $query_vars;
  404.         }
  405.     }
  406.  
  407.     /*
  408.      * If the located post contains nextpage pagination, then the URL chunk following postname may be
  409.      * intended as the page number. Verify that it's a valid page before resolving to it.
  410.      */
  411.     $maybe_page = '';
  412.     if ( 'year' === $compare && isset( $query_vars['monthnum'] ) ) {
  413.         $maybe_page = $query_vars['monthnum'];
  414.     } elseif ( 'monthnum' === $compare && isset( $query_vars['day'] ) ) {
  415.         $maybe_page = $query_vars['day'];
  416.     }
  417.     // Bug found in #11694 - 'page' was returning '/4'
  418.     $maybe_page = (int) trim( $maybe_page, '/' );
  419.  
  420.     $post_page_count = substr_count( $post->post_content, '<!--nextpage-->' ) + 1;
  421.  
  422.     // If the post doesn't have multiple pages, but a 'page' candidate is found, resolve to the date archive.
  423.     if ( 1 === $post_page_count && $maybe_page ) {
  424.         return $query_vars;
  425.     }
  426.  
  427.     // If the post has multiple pages and the 'page' number isn't valid, resolve to the date archive.
  428.     if ( $post_page_count > 1 && $maybe_page > $post_page_count ) {
  429.         return $query_vars;
  430.     }
  431.  
  432.     // If we've gotten to this point, we have a slug/date clash. First, adjust for nextpage.
  433.     if ( '' !== $maybe_page ) {
  434.         $query_vars['page'] = intval( $maybe_page );
  435.     }
  436.  
  437.     // Next, unset autodetected date-related query vars.
  438.     unset( $query_vars['year'] );
  439.     unset( $query_vars['monthnum'] );
  440.     unset( $query_vars['day'] );
  441.  
  442.     // Then, set the identified post.
  443.     $query_vars['name'] = $post->post_name;
  444.  
  445.     // Finally, return the modified query vars.
  446.     return $query_vars;
  447. }
  448.  
  449. /**
  450.  * Examine a URL and try to determine the post ID it represents.
  451.  *
  452.  * Checks are supposedly from the hosted site blog.
  453.  *
  454.  * @since 1.0.0
  455.  *
  456.  * @global WP_Rewrite $wp_rewrite
  457.  * @global WP         $wp
  458.  *
  459.  * @param string $url Permalink to check.
  460.  * @return int Post ID, or 0 on failure.
  461.  */
  462. function url_to_postid( $url ) {
  463.     global $wp_rewrite;
  464.  
  465.     /**
  466.      * Filters the URL to derive the post ID from.
  467.      *
  468.      * @since 2.2.0
  469.      *
  470.      * @param string $url The URL to derive the post ID from.
  471.      */
  472.     $url = apply_filters( 'url_to_postid', $url );
  473.  
  474.     $url_host      = str_replace( 'www.', '', parse_url( $url, PHP_URL_HOST ) );
  475.     $home_url_host = str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) );
  476.  
  477.     // Bail early if the URL does not belong to this site.
  478.     if ( $url_host && $url_host !== $home_url_host ) {
  479.         return 0;
  480.     }
  481.  
  482.     // First, check to see if there is a 'p=N' or 'page_id=N' to match against
  483.     if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) )    {
  484.         $id = absint($values[2]);
  485.         if ( $id )
  486.             return $id;
  487.     }
  488.  
  489.     // Get rid of the #anchor
  490.     $url_split = explode('#', $url);
  491.     $url = $url_split[0];
  492.  
  493.     // Get rid of URL ?query=string
  494.     $url_split = explode('?', $url);
  495.     $url = $url_split[0];
  496.  
  497.     // Set the correct URL scheme.
  498.     $scheme = parse_url( home_url(), PHP_URL_SCHEME );
  499.     $url = set_url_scheme( $url, $scheme );
  500.  
  501.     // Add 'www.' if it is absent and should be there
  502.     if ( false !== strpos(home_url(), '://www.') && false === strpos($url, '://www.') )
  503.         $url = str_replace('://', '://www.', $url);
  504.  
  505.     // Strip 'www.' if it is present and shouldn't be
  506.     if ( false === strpos(home_url(), '://www.') )
  507.         $url = str_replace('://www.', '://', $url);
  508.  
  509.     if ( trim( $url, '/' ) === home_url() && 'page' == get_option( 'show_on_front' ) ) {
  510.         $page_on_front = get_option( 'page_on_front' );
  511.  
  512.         if ( $page_on_front && get_post( $page_on_front ) instanceof WP_Post ) {
  513.             return (int) $page_on_front;
  514.         }
  515.     }
  516.  
  517.     // Check to see if we are using rewrite rules
  518.     $rewrite = $wp_rewrite->wp_rewrite_rules();
  519.  
  520.     // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
  521.     if ( empty($rewrite) )
  522.         return 0;
  523.  
  524.     // Strip 'index.php/' if we're not using path info permalinks
  525.     if ( !$wp_rewrite->using_index_permalinks() )
  526.         $url = str_replace( $wp_rewrite->index . '/', '', $url );
  527.  
  528.     if ( false !== strpos( trailingslashit( $url ), home_url( '/' ) ) ) {
  529.         // Chop off http://domain.com/[path]
  530.         $url = str_replace(home_url(), '', $url);
  531.     } else {
  532.         // Chop off /path/to/blog
  533.         $home_path = parse_url( home_url( '/' ) );
  534.         $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
  535.         $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( $url ) );
  536.     }
  537.  
  538.     // Trim leading and lagging slashes
  539.     $url = trim($url, '/');
  540.  
  541.     $request = $url;
  542.     $post_type_query_vars = array();
  543.  
  544.     foreach ( get_post_types( array() , 'objects' ) as $post_type => $t ) {
  545.         if ( ! empty( $t->query_var ) )
  546.             $post_type_query_vars[ $t->query_var ] = $post_type;
  547.     }
  548.  
  549.     // Look for matches.
  550.     $request_match = $request;
  551.     foreach ( (array)$rewrite as $match => $query) {
  552.  
  553.         // If the requesting file is the anchor of the match, prepend it
  554.         // to the path info.
  555.         if ( !empty($url) && ($url != $request) && (strpos($match, $url) === 0) )
  556.             $request_match = $url . '/' . $request;
  557.  
  558.         if ( preg_match("#^$match#", $request_match, $matches) ) {
  559.  
  560.             if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
  561.                 // This is a verbose page match, let's check to be sure about it.
  562.                 $page = get_page_by_path( $matches[ $varmatch[1] ] );
  563.                 if ( ! $page ) {
  564.                     continue;
  565.                 }
  566.  
  567.                 $post_status_obj = get_post_status_object( $page->post_status );
  568.                 if ( ! $post_status_obj->public && ! $post_status_obj->protected
  569.                     && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
  570.                     continue;
  571.                 }
  572.             }
  573.  
  574.             // Got a match.
  575.             // Trim the query of everything up to the '?'.
  576.             $query = preg_replace("!^.+\?!", '', $query);
  577.  
  578.             // Substitute the substring matches into the query.
  579.             $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
  580.  
  581.             // Filter out non-public query vars
  582.             global $wp;
  583.             parse_str( $query, $query_vars );
  584.             $query = array();
  585.             foreach ( (array) $query_vars as $key => $value ) {
  586.                 if ( in_array( $key, $wp->public_query_vars ) ){
  587.                     $query[$key] = $value;
  588.                     if ( isset( $post_type_query_vars[$key] ) ) {
  589.                         $query['post_type'] = $post_type_query_vars[$key];
  590.                         $query['name'] = $value;
  591.                     }
  592.                 }
  593.             }
  594.  
  595.             // Resolve conflicts between posts with numeric slugs and date archive queries.
  596.             $query = wp_resolve_numeric_slug_conflicts( $query );
  597.  
  598.             // Do the query
  599.             $query = new WP_Query( $query );
  600.             if ( ! empty( $query->posts ) && $query->is_singular )
  601.                 return $query->post->ID;
  602.             else
  603.                 return 0;
  604.         }
  605.     }
  606.     return 0;
  607. }
  608.