Divi WordPress Them — the smartest and most flexible WordPress theme

Back to Top

URL to Post ID for Custom Post Types

Previous Post:

URL to Post ID for Custom Post Types

Three years ago, when I started using WordPress, I was wondering about one thing: “How should we convert a WordPress’s Post URL to its corresponding ID?”. It would be very useful if we could do such a thing, especially when we have to work with AJAX applications.

Fortunately WordPress has a neat function for such purpose, and it’s called url_to_postid1. That function is located in wp-includes/rewrite.php, which does make some sense as we actually want to rewrite a URL so that it becomes an ID.

The magic function above, though, has one major caveat: It does not work with custom post types, which is a shame, really. I do believe that custom post type support will be added in future releases of WordPress, but until then, it’s no harm creating our custom function, right?

We will simply copy url_to_postid from rewrite.php, rename it, and then add a new block of codes. With that in mind, fire up your .php file of choice and add this new conversion function to it:

  1. /* Post URLs to IDs function, supports custom post types - borrowed and modified from url_to_postid() in wp-includes/rewrite.php */
  2. function bwp_url_to_postid($url)
  3. {
  4.     global $wp_rewrite;
  5.  
  6.     $url = apply_filters('url_to_postid', $url);
  7.  
  8.     // First, check to see if there is a 'p=N' or 'page_id=N' to match against
  9.     if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) )   {
  10.         $id = absint($values[2]);
  11.         if ( $id )
  12.             return $id;
  13.     }
  14.  
  15.     // Check to see if we are using rewrite rules
  16.     $rewrite = $wp_rewrite->wp_rewrite_rules();
  17.  
  18.     // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
  19.     if ( empty($rewrite) )
  20.         return 0;
  21.  
  22.     // Get rid of the #anchor
  23.     $url_split = explode('#', $url);
  24.     $url = $url_split[0];
  25.  
  26.     // Get rid of URL ?query=string
  27.     $url_split = explode('?', $url);
  28.     $url = $url_split[0];
  29.  
  30.     // Add 'www.' if it is absent and should be there
  31.     if ( false !== strpos(home_url(), '://www.') && false === strpos($url, '://www.') )
  32.         $url = str_replace('://', '://www.', $url);
  33.  
  34.     // Strip 'www.' if it is present and shouldn't be
  35.     if ( false === strpos(home_url(), '://www.') )
  36.         $url = str_replace('://www.', '://', $url);
  37.  
  38.     // Strip 'index.php/' if we're not using path info permalinks
  39.     if ( !$wp_rewrite->using_index_permalinks() )
  40.         $url = str_replace('index.php/', '', $url);
  41.  
  42.     if ( false !== strpos($url, home_url()) ) {
  43.         // Chop off http://domain.com
  44.         $url = str_replace(home_url(), '', $url);
  45.     } else {
  46.         // Chop off /path/to/blog
  47.         $home_path = parse_url(home_url());
  48.         $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
  49.         $url = str_replace($home_path, '', $url);
  50.     }
  51.  
  52.     // Trim leading and lagging slashes
  53.     $url = trim($url, '/');
  54.  
  55.     $request = $url;
  56.     // Look for matches.
  57.     $request_match = $request;
  58.     foreach ( (array)$rewrite as $match => $query) {
  59.         // If the requesting file is the anchor of the match, prepend it
  60.         // to the path info.
  61.         if ( !empty($url) && ($url != $request) && (strpos($match, $url) === 0) )
  62.             $request_match = $url . '/' . $request;
  63.  
  64.         if ( preg_match("!^$match!", $request_match, $matches) ) {
  65.             // Got a match.
  66.             // Trim the query of everything up to the '?'.
  67.             $query = preg_replace("!^.+\?!", '', $query);
  68.  
  69.             // Substitute the substring matches into the query.
  70.             $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
  71.  
  72.             // Filter out non-public query vars
  73.             global $wp;
  74.             parse_str($query, $query_vars);
  75.             $query = array();
  76.             foreach ( (array) $query_vars as $key => $value ) {
  77.                 if ( in_array($key, $wp->public_query_vars) )
  78.                     $query[$key] = $value;
  79.             }
  80.  
  81.         // Taken from class-wp.php
  82.         foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )
  83.             if ( $t->query_var )
  84.                 $post_type_query_vars[$t->query_var] = $post_type;
  85.  
  86.         foreach ( $wp->public_query_vars as $wpvar ) {
  87.             if ( isset( $wp->extra_query_vars[$wpvar] ) )
  88.                 $query[$wpvar] = $wp->extra_query_vars[$wpvar];
  89.             elseif ( isset( $_POST[$wpvar] ) )
  90.                 $query[$wpvar] = $_POST[$wpvar];
  91.             elseif ( isset( $_GET[$wpvar] ) )
  92.                 $query[$wpvar] = $_GET[$wpvar];
  93.             elseif ( isset( $query_vars[$wpvar] ) )
  94.                 $query[$wpvar] = $query_vars[$wpvar];
  95.  
  96.             if ( !empty( $query[$wpvar] ) ) {
  97.                 if ( ! is_array( $query[$wpvar] ) ) {
  98.                     $query[$wpvar] = (string) $query[$wpvar];
  99.                 } else {
  100.                     foreach ( $query[$wpvar] as $vkey => $v ) {
  101.                         if ( !is_object( $v ) ) {
  102.                             $query[$wpvar][$vkey] = (string) $v;
  103.                         }
  104.                     }
  105.                 }
  106.  
  107.                 if ( isset($post_type_query_vars[$wpvar] ) ) {
  108.                     $query['post_type'] = $post_type_query_vars[$wpvar];
  109.                     $query['name'] = $query[$wpvar];
  110.                 }
  111.             }
  112.         }
  113.  
  114.             // Do the query
  115.             $query = new WP_Query($query);
  116.             if ( !empty($query->posts) && $query->is_singular )
  117.                 return $query->post->ID;
  118.             else
  119.                 return 0;
  120.         }
  121.     }
  122.     return 0;
  123. }
/* Post URLs to IDs function, supports custom post types - borrowed and modified from url_to_postid() in wp-includes/rewrite.php */
function bwp_url_to_postid($url)
{
	global $wp_rewrite;

	$url = apply_filters('url_to_postid', $url);

	// First, check to see if there is a 'p=N' or 'page_id=N' to match against
	if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) )	{
		$id = absint($values[2]);
		if ( $id )
			return $id;
	}

	// Check to see if we are using rewrite rules
	$rewrite = $wp_rewrite->wp_rewrite_rules();

	// Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
	if ( empty($rewrite) )
		return 0;

	// Get rid of the #anchor
	$url_split = explode('#', $url);
	$url = $url_split[0];

	// Get rid of URL ?query=string
	$url_split = explode('?', $url);
	$url = $url_split[0];

	// Add 'www.' if it is absent and should be there
	if ( false !== strpos(home_url(), '://www.') && false === strpos($url, '://www.') )
		$url = str_replace('://', '://www.', $url);

	// Strip 'www.' if it is present and shouldn't be
	if ( false === strpos(home_url(), '://www.') )
		$url = str_replace('://www.', '://', $url);

	// Strip 'index.php/' if we're not using path info permalinks
	if ( !$wp_rewrite->using_index_permalinks() )
		$url = str_replace('index.php/', '', $url);

	if ( false !== strpos($url, home_url()) ) {
		// Chop off http://domain.com
		$url = str_replace(home_url(), '', $url);
	} else {
		// Chop off /path/to/blog
		$home_path = parse_url(home_url());
		$home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
		$url = str_replace($home_path, '', $url);
	}

	// Trim leading and lagging slashes
	$url = trim($url, '/');

	$request = $url;
	// Look for matches.
	$request_match = $request;
	foreach ( (array)$rewrite as $match => $query) {
		// If the requesting file is the anchor of the match, prepend it
		// to the path info.
		if ( !empty($url) && ($url != $request) && (strpos($match, $url) === 0) )
			$request_match = $url . '/' . $request;

		if ( preg_match("!^$match!", $request_match, $matches) ) {
			// Got a match.
			// Trim the query of everything up to the '?'.
			$query = preg_replace("!^.+\?!", '', $query);

			// Substitute the substring matches into the query.
			$query = addslashes(WP_MatchesMapRegex::apply($query, $matches));

			// Filter out non-public query vars
			global $wp;
			parse_str($query, $query_vars);
			$query = array();
			foreach ( (array) $query_vars as $key => $value ) {
				if ( in_array($key, $wp->public_query_vars) )
					$query[$key] = $value;
			}

		// Taken from class-wp.php
		foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )
			if ( $t->query_var )
				$post_type_query_vars[$t->query_var] = $post_type;

		foreach ( $wp->public_query_vars as $wpvar ) {
			if ( isset( $wp->extra_query_vars[$wpvar] ) )
				$query[$wpvar] = $wp->extra_query_vars[$wpvar];
			elseif ( isset( $_POST[$wpvar] ) )
				$query[$wpvar] = $_POST[$wpvar];
			elseif ( isset( $_GET[$wpvar] ) )
				$query[$wpvar] = $_GET[$wpvar];
			elseif ( isset( $query_vars[$wpvar] ) )
				$query[$wpvar] = $query_vars[$wpvar];

			if ( !empty( $query[$wpvar] ) ) {
				if ( ! is_array( $query[$wpvar] ) ) {
					$query[$wpvar] = (string) $query[$wpvar];
				} else {
					foreach ( $query[$wpvar] as $vkey => $v ) {
						if ( !is_object( $v ) ) {
							$query[$wpvar][$vkey] = (string) $v;
						}
					}
				}

				if ( isset($post_type_query_vars[$wpvar] ) ) {
					$query['post_type'] = $post_type_query_vars[$wpvar];
					$query['name'] = $query[$wpvar];
				}
			}
		}

			// Do the query
			$query = new WP_Query($query);
			if ( !empty($query->posts) && $query->is_singular )
				return $query->post->ID;
			else
				return 0;
		}
	}
	return 0;
}

As you can see, the new codeblock is from line 81 to line 112, and that’s where the magic happens.

Basically, what we just did was adding some new query vars to the $query variable, which was formerly designed to support only built-in post types, so that WordPress can query for all types of posts through a typical WP_Query2 request later on (line 115) to get the correct post ID.

With the help of this new function, a test like this:

  1. echo bwp_url_to_postid('http://betterwp.net/wordpress-tips/url_to_postid-for-custom-post-types/');
echo bwp_url_to_postid('http://betterwp.net/wordpress-tips/url_to_postid-for-custom-post-types/');

will return 229, which is exactly the ID of this WordPress tip, pretty nice!

Please note that, regardless of what function you would choose to use, custom or original, you should always use it after the init action has been fired, as a lot of plugins and themes will use such action to register post types. If you fail to do so, you will get 0 as the result, obviously.

References

  1. http://codex.wordpress.org/Function_Reference/url_to_po ... _to_postid []
  2. http://codex.wordpress.org/Function_Reference/WP_Query []
Print Article Trackback Trackback to this Article   Subscribe to Comments RSS Subscribe to Comments RSS

26 Opinions for URL to Post ID for Custom Post Types

  1. User's Gravatar
    25
    Luke January 2, 2014 at 7:08 pm – Permalink

    Hi there,

    Do you know if it is possible to return multiple ID’s from multiple URL’s?

    Thanks,
    Luke

    • User's Gravatar
      26
      Khang Minh January 3, 2014 at 1:09 pm – Permalink

      The above function will only take one URL at a time, but you can write a loop that calls it multiple times. Something like this:

      1. <?php
      2.     $urls = array('url1', 'url2', 'url3', .....);
      3.     foreach ($urls as $url) {
      4.         echo "Post ID for $url is " .  bwp_url_to_postid($url) . "<br />";
      5.     }
      6. ?>
      <?php
      	$urls = array('url1', 'url2', 'url3', .....);
      	foreach ($urls as $url) {
      		echo "Post ID for $url is " .  bwp_url_to_postid($url) . "<br />";
      	}
      ?>

Speak Up Your Mind!

An asterisk (*) indicates a required field and must be filled.




  • Web page and e-mail addresses turn into links automatically.
  • Wrap codes in: <code lang=""></code> or <pre lang="" extra="">
  • Lines and paragraphs break automatically.

Next Post: