I tried this in a fresh install of WP locally so I'd assume this is expected behavior, but I don't understand it. I'm not in the loop,
No but that doesn't mean a main query doesn't exist, it just means your code to display it hasn't run yet
and I haven't called anything to start a query,
But the main query has started and completed, it needs to in order for conditionals used to decide which template to load to work.
why would get_post() return the first post? Same result if I use global $post.
Why wouldn't it? Note that you get the same result because they are literally the same thing.
When you load a URL several things happen:
- the pretty URL gets matched against a rule that turns it into an ugly URL aka
index.php?foo=bar
- those URL parameters get whitelisted and fed into the main query
WP_Query
, which is what the main loop functions operate on.
- that query is used to power functions like
is_single
or is_archive
etc
- WP uses those functions to make a decision about which theme template or block template to load.
So the main query has already fetched the posts, and the first post is already available. If it wasn't, there'd be no way to catch 404s or load page templates for pages.
Ofcourse, you've not called the_post()
yet which might be why there's some confusion, but that makes more sense if you know these things:
- if you don't tell
get_post
which post to get, it defaults to the current post, aka global $post
. It's the very first thing it does in the function
- if you call this too early it can return
null
- In the main WP class there's a function named
register_globals
that sets up initial values for major globals, including $post
, which at the time of writing has a line that looks like this: $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null;
- it too will set it to
null
if no posts were found to avoid problems
Remember, all frontend pages have a main query, even if you use get_posts
or new WP_Query
in your templates.