函数文档

get_page_by_path()

💡 云策文档标注

概述

get_page_by_path() 函数用于根据页面路径检索页面,支持自定义返回类型和指定文章类型。它通过路径解析和数据库查询实现,并包含缓存机制以提高性能。

关键要点

  • 函数接受三个参数:$page_path(必需,页面路径)、$output(可选,返回类型,默认为 OBJECT)、$post_type(可选,文章类型,默认为 'page')。
  • 返回 WP_Post 对象、数组或 null,取决于 $output 参数设置。
  • 默认情况下,函数会搜索 'page' 和 'attachment' 文章类型,除非 $post_type 以数组形式指定仅包含所需类型。
  • 使用缓存(wp_cache_get_salted 和 wp_cache_set_salted)来优化查询性能。
  • 路径处理包括 URL 编码解码和 sanitize_title_for_query 清理,确保安全查询。

代码示例

// 检索路径为 'parent-page/sub-page' 的页面,返回 WP_Post 对象
get_page_by_path('parent-page/sub-page');

// 对于非层级自定义文章类型,仅使用 slug 并指定 post_type
get_page_by_path('cat', OBJECT, 'animal');

// 仅搜索 'page' 文章类型,避免包含附件
get_page_by_path('example', OBJECT, ['page']);

注意事项

  • 路径参数应与 pagename 查询等效,例如 index.php?pagename=parent-page/sub-page。
  • 使用 basename() 和 untrailingslashit() 辅助函数处理 URL 路径,提取最后部分。
  • 注意默认包含 'attachment' 文章类型可能导致意外结果,建议通过数组参数精确控制。
  • 函数性能可能受大量附件影响,优化查询时应限制文章类型范围。

📄 原文内容

Retrieves a page given its path.

Parameters

$page_pathstringrequired
Page path.
$outputstringoptional
The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to a WP_Post object, an associative array, or a numeric array, respectively.

Default:OBJECT

$post_typestring|arrayoptional
Post type or array of post types. Default 'page'.

Return

WP_Post|array|null WP_Post (or array) on success, or null on failure.

Source

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";
	$cached    = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed );
	if ( false !== $cached ) {
		// Special case: '0' is a bad `$page_path`.
		if ( '0' === $cached || 0 === $cached ) {
			return null;
		} 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_salted( $cache_key, $found_id, 'post-queries', $last_changed );

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

	return null;
}

Changelog

Version Description
2.1.0 Introduced.

User Contributed Notes

  1. Skip to note 4 content

    Page Path
    This is the equivalent of the pagename query, as in: index.php?pagename=parent-page/sub-page.

    Code for the above could be written as (assuming parent-page/sub-page is actually the path to a page):

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

    For non-hierarchical custom post types, you need to use just the slug in tandem with the post_type parameter.

    //Returns nothing, assumes animals is the rewrite slug for the animal CPT
    get_page_by_path('animals/cat', OBJECT, 'animal');
    
    //Returns the animal with the slug 'cat'
    get_page_by_path('cat', OBJECT, 'animal');

    The functions basename() and untrailingslashit() are handy for grabbing the last part of the URL for this:

    $page_path = 'animals/cat/';
    get_page_by_path( basename( untrailingslashit( $page_path ) ) , OBJECT, 'animal');

  2. Skip to note 5 content

    As per scottb79, it is correct that the function checks for the post type supplied (Page by default) and Attachment by default.

    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:

    $page = get_page_by_path( 'example', OBJECT, [ 'page' ] );

    This issue was found when an attachment sotred 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.

  3. Skip to note 6 content

    If you don’t specify a post_type, it searches both page and attachment.

    If you want only page, pass it via the third parameter.:

    get_page_by_path( '/about/', OBJECT, 'page' );