get_page_by_path()WP 2.1.0

Gets the post by the specified path of this page: parent-page/sub-page. The result is cached.

You can specify a slug (name, post_name, slug) for non-hierarchical post types. That is, you can get a post by its name.

You can specify which post type to work with, by default page.

For hierarchical post types, you need to specify the full path in the first parameter! For example, if you only specify the slug and it is a child page, the function will not find the page. For example, if a post has the URL /parent_name/child_name, if you only specify the name child_name, the function will not find the post.

This function is the basis for recognizing the current request for permanent pages based on pretty URLs: requests of the form: index.php?pagename=parent-page/sub-page.

To get a post by title, use get_page_by_title()

1 time — 0.000868 sec (slow) | 50000 times — 0.59 sec (very fast) | PHP 7.1.2, WP 4.7.3

No Hooks.

Returns

WP_Post|Array|null.

  • WP_Post or array on success.
  • null on error.

Usage

get_page_by_path( $page_path, $output, $post_type );
$page_path(string) (required)
Path of the page. For non-hierarchical posts, you can specify a slug. Slashes / at the ends are trimmed.
$output(string)

In what format to get the result:

  • OBJECT - return as an object;
  • ARRAY_N - return as a numeric array;
  • ARRAY_A - return as an associative array.

Default: OBJECT

$post_type(string/array)

Name of the post type. For pages, it is 'page'.

  • If you specify multiple post types or one in an array: ['post', 'page'] or ['post'], the search will be conducted specifically for the specified post types.
  • If you specify the post type as a string, the post type attachment will also be added to it.

Default: 'page' (later becomes 'page, attachment')

Examples

0

#1 Get the page in it's path

Suppose we have a child page which we access at URL example.com/parent-page/sub-page: parent-page/sub-page is the page path. Now, somewhere in the code we will get this page.

$page = get_page_by_path( 'parent-page/sub-page' );

//or like this
$page = get_page_by_path( '/parent-page/sub-page/' );

print_r( $page );

IMPORTANT: If you specify just a page name, the function will not return anything

$page = get_page_by_path( 'sub-page' ); //> NULL

However, if the page is not a child, then you should specify just the name of the page - the slug.

$page = get_page_by_path( 'page-name' ); //> WP_Post{ ... }
-1

#2 Use with custom post types

There is a new post type, the path to such a post type is usually translated by the name of the post type. For example, the post type is called car, the URL of the particular post is example.com/car/lada. To get such a post, you need to use only its shortcut:

// Correct option
$page = get_page_by_path( 'lada', OBJECT, 'car' );

// NOT correct (will not return anything)
$page = get_page_by_path( 'car/lada', OBJECT, 'car' );
Auto-add attachment type

This function will add the attachment post type to any instances where the post type is passed as a string. Due to the following code:

if ( is_array( $post_type ) ) {
	$post_types = $post_type;
} else {
	$post_types = array( $post_type, 'attachment' );
}

If you truly only want to return the item path from only the post type supplied. It needs to be passed as an array like this:

// the query will be: post_type IN ( 'car' )
$page = get_page_by_path( 'lada', OBJECT, ['car'] );

// the query will be: post_type IN ( 'car', 'attachment' )
$page = get_page_by_path( 'lada', OBJECT, 'car' );

This issue was found when an attachment stored in the database had the same path as the page we wanted to retrieve. The attachment having a lower Post ID it is returned first.

Getting the last part of the path

You can use basename() and untrailingslashit() to get the last part of the path:

$page_path = 'car/lada/';
$post_name = basename( untrailingslashit( $page_path ) );

$page = get_page_by_path( $post_name , OBJECT, 'car');

Notes

  • Global. wpdb. $wpdb WordPress database abstraction object.

Changelog

Since 2.1.0 Introduced.

get_page_by_path() code WP 6.8.1

function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
	global $wpdb;

	$last_changed = wp_cache_get_last_changed( 'posts' );

	$hash      = md5( $page_path . serialize( $post_type ) );
	$cache_key = "get_page_by_path:$hash:$last_changed";
	$cached    = wp_cache_get( $cache_key, 'post-queries' );
	if ( false !== $cached ) {
		// Special case: '0' is a bad `$page_path`.
		if ( '0' === $cached || 0 === $cached ) {
			return;
		} else {
			return get_post( $cached, $output );
		}
	}

	$page_path     = rawurlencode( urldecode( $page_path ) );
	$page_path     = str_replace( '%2F', '/', $page_path );
	$page_path     = str_replace( '%20', ' ', $page_path );
	$parts         = explode( '/', trim( $page_path, '/' ) );
	$parts         = array_map( 'sanitize_title_for_query', $parts );
	$escaped_parts = esc_sql( $parts );

	$in_string = "'" . implode( "','", $escaped_parts ) . "'";

	if ( is_array( $post_type ) ) {
		$post_types = $post_type;
	} else {
		$post_types = array( $post_type, 'attachment' );
	}

	$post_types          = esc_sql( $post_types );
	$post_type_in_string = "'" . implode( "','", $post_types ) . "'";
	$sql                 = "
		SELECT ID, post_name, post_parent, post_type
		FROM $wpdb->posts
		WHERE post_name IN ($in_string)
		AND post_type IN ($post_type_in_string)
	";

	$pages = $wpdb->get_results( $sql, OBJECT_K );

	$revparts = array_reverse( $parts );

	$found_id = 0;
	foreach ( (array) $pages as $page ) {
		if ( $page->post_name === $revparts[0] ) {
			$count = 0;
			$p     = $page;

			/*
			 * Loop through the given path parts from right to left,
			 * ensuring each matches the post ancestry.
			 */
			while ( 0 !== (int) $p->post_parent && isset( $pages[ $p->post_parent ] ) ) {
				++$count;
				$parent = $pages[ $p->post_parent ];
				if ( ! isset( $revparts[ $count ] ) || $parent->post_name !== $revparts[ $count ] ) {
					break;
				}
				$p = $parent;
			}

			if ( 0 === (int) $p->post_parent
				&& count( $revparts ) === $count + 1
				&& $p->post_name === $revparts[ $count ]
			) {
				$found_id = $page->ID;
				if ( $page->post_type === $post_type ) {
					break;
				}
			}
		}
	}

	// We cache misses as well as hits.
	wp_cache_set( $cache_key, $found_id, 'post-queries' );

	if ( $found_id ) {
		return get_post( $found_id, $output );
	}

	return null;
}