Determine WP_Query parameters from URL

I’m wondering if it’s possible to determine WP_Query parameters by only using the given page URL. The reason I’m trying to do this because I’m developing a SPA theme (and ultimately a JSON response) and would need to be able to determine a query without reloading the page.

Thought it’s pretty hacky, I’ve been successful in pairing a post URL with the Permalink structure in order to determine what a single post query should be:

<?php

function api_query_url() {

  // Check that the URL is set and is in fact a string
  if ( ! isset( $_POST['url'] ) || gettype( $_POST['url'] ) !== 'string' ) {
    api_response( false, 400, "'url' is a required parameter!" );
  }

  // Parse the URL for parameters and path
  $query_url = parse_url( $_POST['url'] );

  // Pass query-based URL directly to Single API request
  if ( isset( $query_url['query'] ) ) {
    api_single( $query_url['query'] );
  }

  // Map Permalink structure tags to WP_Query parameters
  $structure_refs = array(
    'year'     => 'year',
    'monthnum' => 'monthnum',
    'day'      => 'day',
    'hour'     => 'hour',
    'minute'   => 'minute',
    'second'   => 'second',
    'post_id'  => 'p',
    'postname' => 'name',
    'category' => 'category_name',
    'author'   => 'author_name'
  );

  // Retreive the site's Permalink Structure and break it in to an array
  $structure = array_filter( explode( "https://wordpress.stackexchange.com/", get_option( 'permalink_structure' ) ) );

  // Break our query parameters in to an array
  $query_params = array_filter( explode( "https://wordpress.stackexchange.com/", $query_url['path'] ) );

  // Set up an empty query array
  $query = array();

  // For each query parameter received
  foreach ($query_params as $param) {

    // Using the parameter value's position, locate its corresponding structure key
    $key_position  = array_search( $param, $query_params );
    $structure_key = str_replace( '%', '', $structure[$key_position] );

    // If the structure key exists in the structure references, add to to the query
    if ( array_key_exists( $structure_key, $structure_refs) ) {
      $query[$structure_refs[$structure_key]] = $param;
    }
  }

  // Pass off to Query handler to create JSON response
  api_single( $query );

}
add_action( 'wp_ajax_nopriv_api_query_url', 'api_query_url' );

Using the above function I’m able to pass url as a parameter to it to perform an ajax call, and it will parse it to find the WP_Query parameters.

So, a URL like http://example.com/blog/my-first-post/ will be parsed to extract name => 'my-first-post' and a URL like http://example.com/?p=124 will be parsed to extract p => 124. This then gets passed to a WP_Query and so forth. FTR, I also remove any blacklisted parameters, like post_status or has_password in an attempt to keep things secure.

In order to parse a pretty URL structure I take the components of the path, so for /2014/07/hello-world/ we get 2014, 07, and hello-world, and then I compare them to the set permalink structure, which in this case would be /%year%/%monthnum%/%postname%/ – and since some permalink tags don’t work with WP_Query, I do some conversion; for example, postname would be name.

Anyhow… it felt like I was off to a good start, until I ran in to pages. With my above code a page URL like http://example.com/about/ and the above permalink structure the parameters extracted would be year => 'about'. Not good.

If anyone has any guidance as to how I should approach this it would be much appreciated. Also, please let me know if I’m approaching this all wrong. I just need to determine a query without reloading the page.

2 Answers
2

Edited Jul, 07 2014 at 6:28


There are a lot of gotchas with your approach. It will not work if there are any custom rewrite rule, it (probably) will not work if server is IIS and pretty permalink are active, it will not work for custom post types, it will not work for archive or search urls and so on.

To obtain a sing post/page url from a url in a affordable way, there is the function url_to_postid, e.g.

$id = url_to_postid( $query_url['path'] );

if ( is_numeric( $id ) && (int) $id > 0 ) {
  // the required post id is: $id
} else {
  // the url is not for a single post, may be an archive url, a search url or even
  // a non-valid 404 url
}

If you are interested only is single post (even CPT) or page urls, then probably the few lines above are the only thing you need, but if you need to parse all kind of WordPress urls, then you need something else, and is not something really easy.

The only way to obtain query vars from a url in an affordable way, is to reproduce the way WordPress uses to parse an url.

That is done in core by the method parse_request of the WP class.

However, using that method for our purpose explained above is hard/discouraged because:

  • it directly accesses to $_SERVER, $_POST and $_GET variables, making hard to parse arbitrary urls not related with current HTTP request
  • it triggers some action hooks strictly related to current HTTP request parsing, that makes no sense to trigger for arbitrary urls
  • it accesses and modifies properties of global $wp variable that should not be changed after request is parsed or very likely things will break.

What we can do is starting from the code there, remove the parts we don’t need, and make the code use an arbitrary url instead of the one from current HTTP request.

Core parse_request method is 214 lines long (v. 3.9.1) and do that work can be such a pain, but luckily someone has already done the hard work, see here.

Leave a Comment