wppaste
WordPress

get_post( int|object|null $post = null, string $output = OBJECT, string $filter = 'raw' ): WP_Post|array|null

Since
1.5.1
Source
wp-includes/post.php:1138

Resolve a post ID, a post object, or the current global $post into a single WP_Post, an associative array, or a numeric array. The $post argument is falsey-tolerant: 0, null and false all fall back to the global post, which is why calls outside the loop return null rather than an error. Fields come back raw by default, so anything destined for a page still needs its display filters applied.

Retrieves post data given a post ID or post object.

Description

See sanitize_post() for optional $filter values. Also, the parameter $post, must be given as a variable, since it is passed by reference.

Compatibility

WordPress
since 1.5.1
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$postint|object|nulloptional
Post ID or post object. null, false, 0 and other PHP falsey values return the current global post inside the loop. A numerically valid post ID that points to a non-existent post returns null. Defaults to global $post.Default: null
$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.Default: OBJECT
$filterstringoptional
Type of filter to apply. Accepts 'raw', 'edit', 'db', or 'display'. Default 'raw'.Default: 'raw'

Return value

WP_Post|array|null
Type corresponding to $output on success or null on failure.
When $output is OBJECT, a WP_Post instance is returned.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

Get a post by ID as an associative array

Passing ARRAY_A is the quickest way to read post fields without touching WP_Post property syntax.

$post = get_post( 2, ARRAY_A );

if ( $post ) {
	echo esc_html( $post['post_title'] ), "\n";
	echo esc_html( $post['post_status'] ), "\n";
} else {
	echo 'No post with that ID.';
}

ARRAY_N returns the same fields keyed numerically, which is rarely what you want.

Read the current post without passing an ID

With no arguments the function falls back to the global $post, so template parts can stay self-contained.

// Stand in for the loop by setting the global the way a template would.
$GLOBALS['post'] = get_post( 3 );

$post = get_post();

echo $post ? esc_html( $post->post_title ) : 'No global post is set.';

Outside the loop there is no global $post, so guard the return value.

Render post content with the display filters applied

get_post() returns raw database values, so content read this way is not what the front end shows.

$post = get_post( 1 );

if ( $post ) {
	echo "RAW:\n", esc_html( $post->post_content ), "\n\n";
	echo "FILTERED:\n", esc_html( apply_filters( 'the_content', $post->post_content ) );
}

Applying 'the_content' also runs shortcodes, oEmbed and block rendering.

Common problems and fixes · 4

Why does get_post() return null?

Two different paths end in null. A falsey $post (0, null, false, '') makes the function fall back to $GLOBALS['post'], so calling it outside the loop, in an admin screen, or in a REST or cron request returns null because no global post is set. A numeric ID that has no matching row also returns null, because WP_Post::get_instance() fails and the function returns early. Always test the return value before reading a property from it.

Why is the post content I get back not filtered?

$filter defaults to 'raw', which returns the values exactly as stored. Paragraph tags, shortcodes, blocks and embeds are all applied by the the_content filter at render time, not by this function. Run apply_filters( 'the_content', $post->post_content ) when you need the rendered output, and remember that doing so executes shortcodes.

What is the difference between OBJECT, ARRAY_A and ARRAY_N?

They select the return shape only, never which fields are loaded. OBJECT (the default) returns a WP_Post instance with magic properties and lazy loading for fields like post_category. ARRAY_A returns the same fields as a string-keyed array and ARRAY_N as a numerically indexed one. Only the OBJECT form gives you WP_Post behavior, so array callers lose the lazy properties.

Does calling get_post() repeatedly hit the database each time?

No. Numeric IDs go through WP_Post::get_instance(), which reads from the posts object cache group and only queries when the post is not cached. Calling it several times in one request for the same ID is cheap. Passing a plain stdClass object is the expensive path, because it is sanitized and wrapped on every call.

Alternatives and related functions

get_posts
When you need more than one post, or want to select by post type, status, author or meta rather than by a known ID.
WP_Query
When you need pagination, a found-rows count, or the full range of query arguments that get_posts() suppresses.
get_post_field
When you only want a single field and would rather have it filtered for a given context than handle a whole object.
get_page_by_path
When you have a slug or a hierarchical path instead of an ID.

Performance profile

How much work a call to get_post() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Heavy

Reaches the database via get_post().

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
11–45

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 79.

Plugin surface
None

Nothing here hands control to plugin code.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What it touches

  • querycontent queryget_post()this function does it

Further down the call graph this can also reach hook, option, cache, serialize and transient. Those are the worst case, several calls deep and usually down an error path, not what a normal call pays.

What one call costs · 12 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost get_post() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
always11–25none
always16–36->filter()
!($post instanceof)19–29::WP_Post()
$output === false21–35->filter(), ->to_array()
!($post instanceof) && is_object($post) && empty($post)23–27sanitize_post()
!($post instanceof)24–40::WP_Post(), ->filter()
always27–41->filter(), ->to_array(), array_values()
!($post instanceof) && is_object($post) && empty($post)28–38sanitize_post(), ->filter()
!($post instanceof) && $output === false29–39::WP_Post(), ->filter(), ->to_array()
!($post instanceof) && is_object($post) && empty($post) && $output === false33–37sanitize_post(), ->filter(), ->to_array()
!($post instanceof)35–45::WP_Post(), ->filter(), ->to_array(), array_values()
!($post instanceof) && is_object($post) && empty($post)39–43sanitize_post(), ->filter(), ->to_array(), array_values()

Across PHP versions

PHPCompiledExecutedBranchesNotes
8.6-dev7911–4512
8.57911–4512
8.47911–45122 fewer instructions than PHP 8.3
8.38111–4512
8.28111–4512
8.18111–45122 fewer instructions than PHP 7.4
7.48311–4712

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Uses · 3

Used by · 50

Show all 50

Source code

function get_post( $post = null, $output = OBJECT, $filter = 'raw' ) {	if ( empty( $post ) && isset( $GLOBALS['post'] ) ) {		$post = $GLOBALS['post'];	} 	if ( $post instanceof WP_Post ) {		$_post = $post;	} elseif ( is_object( $post ) ) {		/** @var stdClass $post */		if ( empty( $post->filter ) ) {			$_post = sanitize_post( $post, 'raw' );			$_post = new WP_Post( $_post );		} elseif ( 'raw' === $post->filter ) {			$_post = new WP_Post( $post );		} elseif ( isset( $post->ID ) ) {			$_post = WP_Post::get_instance( (int) $post->ID );		} else {			$_post = null;		}	} elseif ( is_numeric( $post ) ) {		$_post = WP_Post::get_instance( (int) $post );	} else {		$_post = null;	} 	if ( ! $_post ) {		return null;	} 	$_post = $_post->filter( $filter );	if ( ! $_post ) {		return null;	} 	if ( ARRAY_A === $output ) {		return $_post->to_array();	} elseif ( ARRAY_N === $output ) {		return array_values( $_post->to_array() );	} 	return $_post;}

Changelog

Introduced in 1.5.1. One change between 6.7.7 and 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

7.0.4
Parameter $post retyped from int|WP_Post|null to int|object|null.verified against source
1.5.1
Introduced.from the docblock

About this page

Parsed data
Generated from the wordpress-develop 7.1.0 tag, from src/wp-includes/post.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.