6

I'm writing a widget plugin and need access to post information.

I've seen people using 3 different approaches:

global $post;
if ( isset( $GLOBALS['post'] ) ) $post = $GLOBALS['post'];
$post = get_post();

This post explains the difference between the first two solutions. Can you please confirm that a call to get_post() without args does the same thing (for read only purposes)?

Also, regarding the if ( isset( $GLOBALS['post'] ) ): I guess this means post can be undefined. Could someone explain in which context?

In my case, that's a widget in pages (not post). So is it safe to assume that the widget code being executed after the loop, post is always defined as the page content in such cases?

7

In essence, for all technical purposes,

get_post() == $post == $GLOBALS['post']

As @tosho already explained in the linked post, $post === $GLOBALS['post'], so I will not go into that.

What we are interested in is, how is get_post() the same. For this, we will need to look at the source code. As you can see, we get our answer in the first two lines of the function when we do not pass anything to get_post()

if ( empty( $post ) && isset( $GLOBALS['post'] ) )
    $post = $GLOBALS['post'];

regarding the if ( isset( $GLOBALS['post'] ) ): I guess this means post can be undefined.

Yes, it can, and it depends on context. Also, just like any global, it can be unset as well, delibritely or unknowingly which can have disasterous effects. That is what make globals such a nasty evil thing to create and use. Unknowingly using globals as local variables is the number one reason (IMO) for unexpected, non debuggable code failures.

The $post global is set by WP_Query::the_post(), but it can be changes by any custom code, so never rely on that, specially outside of the loop.

is it safe to assume that the widget code being executed after the loop

NEVER EVER assume anything. That is the most dangerous thing you can ever do when coding. Simply assuming something leads to terrible bugs, security loopholes, maybe leak very personal info which can land you in jail, etc etc. Treat everything you code in such a manner to safely handle a specific thing or behavior should something fail, and alwyas code with a mindset that your code will fail and it will be hacked.

Again, widgets can run before the loop or after it, and even inside the loop, so do not assume it will behave in a certain manner.

If this is a true page (created in the back end pages section), then you can use get_queried_object() to get the current page object. It is much more reliable than the $post global. You should take your time and read through this answer by @gmazzap to my question here

EDIT - Better alternative to get_queried_object()

From your comment to my answer

I understand $post can have been modified before I access it. But isn't that true also for $wp_query (modified through "custom" or "secondary" queries)? If so, shouldn't I test is_main_query() before relying on get_queried_object()? I'm assuming the main query is that based on the requested url, right? And if is_main_query() is false, what can I use then?

Very true, the main query object is stored in the $wp_query global variable. Using $wp_query as a local variable breaks the main query object and sets it to whatever you are using the global for. Also, query_posts sets the main query object to the current custom query, which also breaks it.

It is true that the queried object relies on the integrity of the main query object, which does make get_queried_object() vulnerable. In general, get_queried_object() is still much more reliable than $post because any custom query using the_post() or setup_postdata( $post ) sets the $post global to the current post in the current loop. Forgetting to reset a custom query with wp_reset_postdata() will leave you with the wrong post object inside $post.

As for get_queried_object(), if any one is still using query_posts, it is up to them if they want to suffer the consequences.

I like the idea that you would really want and are really looking at more reliable alternatives to $post AND get_queried_object(), that is quite evident from your comment. Your comment actually sparked something I was working on a while ago and completely forgot about, I think most of all, including me, are forgetting one very important global which is never modified (except by the filters and actions inside WP_Query itself), and that global is $GLOBALS['wp_the_query']. $GLOBALS['wp_the_query'] holds the actual main query object. $GLOBALS['wp_query'] (AKA $wp_query) is only a copy of $GLOBALS['wp_the_query'].

Lets quickly look at where this is set. In the current version (Wordpress 4.4.2), you will find the following code on lines 291 - 304 in `wp-settings.php

/**
 * WordPress Query object
 * @global WP_Query $wp_the_query
 * @since 2.0.0
 */
$GLOBALS['wp_the_query'] = new WP_Query();
/**
 * Holds the reference to @see $wp_the_query
 * Use this global for WordPress queries
 * @global WP_Query $wp_query
 * @since 1.5.0
 */
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];

I did not know WordPress way back when, but this was most probably done to accommodate query_posts, because if you look at wp_reset_query() which you should use after query_posts, it resets $wp_query back to $GLOBALS['wp_the_query'].

This all means that, even if we break the main query object ($wp_query), we still have a fully vaild copy left in $GLOBALS['wp_the_query'].

With all of this in mind, if you really need a 99.99% reliable way to get the current queried object, it would be $GLOBALS['wp_the_query']->get_queried_object(). That would be the ultimate reliable way without having to rerun the main query again by yourself.

Before I conclude, you also spoke about the is_main_query() check (which in essence checks if the current WP_Query instance is equal to $GLOBALS['wp_the_query']). In a case like this, it would not really work, as it simply return a boolean value.

CONCLUSION - wrapping up

To reliably get the current queried object on a single post page, or for that matter on any singular page and archive pages, use the queried object from the $GLOBALS['wp_the_query'] global

$GLOBALS['wp_the_query']->get_queried_object()
  • Thanks for your anwser. First part is clear. I'll look into the details of the last part and will get back. – Mat Feb 16 '16 at 11:23
  • My pleasure, glad my answer is useful for the first part. Feel free to leave a comment if you are unclear of something ;-) Enjoy – Pieter Goosen Feb 16 '16 at 11:25
  • Went through the post you referenced and some other material, and I'm not sure I get it. I confess I'm still not familiar with all the intricacies of WP queries. I understand $post can have been modified before I access it. But isn't that true also for $wp_query (modified through "custom" or "secondary" queries)? If so, shouldn't I test is_main_query() before relying on get_queried_object()? I'm assuming the main query is that based on the requested url, right? And if is_main_query() is false, what can I use then? – Mat Feb 17 '16 at 8:12
  • @Mat see my update. Your comment above actually sparked my mind again into something I did a while ago. My update should answer your question perfectly ;-) – Pieter Goosen Feb 17 '16 at 9:03
  • Thanks. I had seen wp_the_query while looking into your previous answer but hadn't thought of the use you suggest. So am I correct if I summarize as follows: $post: not safe, unreliable content. get_queried_object(): safer as long as no other code uses query_post() without reseting the query or manually changes $wp_query. $GLOBALS['wp_the_query']->get_queried_object(): safest, unless something goes real wrong. As for your comment about is_main_query(), I meant something like if ( is_main_query() { $foo=get_queried_object();} else ... but your final suggestion is even better. – Mat Feb 17 '16 at 9:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.