timber/starter-theme

Issue with $post usage and third party plugins (Yoast, Learndash)

stephenniemans opened this issue · 6 comments

Description of the issue

In the starter theme and documentation, the following code is included:

$post = new TimberPost();
$context['post'] = $post;

This seems to override the global $post object (codex reference). I ran into issues with various issues with third party plugins that perform safety checks of $post to see if the class of it is equal to WP_Post, where it will fail because $post is now of class Timber\Post. Examples of this include:

(is_a($post, 'WP_Post'))

and

($post instanceof WP_Post)

This causes errors and erratic behavior, as reported here for example:

Example 1
Example 2
Example 3
Example 4

Proposed change

Please correct me if I'm mistaken, but since Timber templating tends to refer to the $context['post'] rather than the $post, we could probably rewrite the code in the doc examples and starter theme as:

$timber_post = new Timber\Post();
$context['post'] = $timber_post;

This would save issues for user of these plugins and any others that expect $post to be the global variable described in the WP Codex.

I've created a pull request for this, hope it helps!

Thanks @stephenniemans! I had followed those issues, but had determined these to be on Yoast's side of the ball.

That said, if a small tweak to template code can mitigate, I'm all for it.

What doesn't quite make sense to me though is that in the starter theme files, (single.php, etc.) there's no global declaration. The $post that those PHP files is using is therefore a local variable that shouldn't interact with the global $post var.

That said, this is WordPress and PHP so there are plenty of ghosts in the machine.

Have you confirmed that this tweak resolves the issue with Yoast?

PhpStorm/Xdebug combo would easily illuminate the situation details (i.e. not about ghosts), but I'm not equipped w/ Twig to do this work (just a lurker here). Maybe someone else?

What doesn't quite make sense to me though is that in the starter theme files, (single.php, etc.) there's no global declaration. The $post that those PHP files is using is therefore a local variable that shouldn't interact with the global $post var.

I wanted to check this for myself as well, so I did another clean install with only timber and the starter theme installed. I generated the 'unit tested plugin' with WP-CLI and had it output the class of global $post through a shortcode:

// Your code starts here.
function timber_test_shortcode(){
	global $post;

	echo '$post class: ' . get_class($post);
}
add_shortcode('timber_test', 'timber_test_shortcode');

This is a recording of the behavior I observed. You can see the output of get_class() change when I rename $post. I observed that renaming it to $timber_post made the variable $timber_post available within the dummy plugin, too. I'm afraid that I am not familiar enough with the inner workings of Timber to say why this is the case.

Have you confirmed that this tweak resolves the issue with Yoast?

I don't use Yoast so I am unfortunately unable to test it properly, they have a number of separate issues related to this, with some partial fixes throughout their version history. This makes it hard to pin down to what extent their team has resolved it.

Myself, I ran into my issues using Learndash for a site, and googling the issue brought up the Yoast issue so I thought to include it in my description. I can confirm at least that the change solved my Learndash issues. Niche as my case may be, I suspect it could pop up unexpectedly in other situations if plugin devs implement checks against the class of $post.

That said, if a small tweak to template code can mitigate, I'm all for it.

I think the docs show the same example, if it helps I'd be happy to submit a PR for that as well (I updated my previous because I overlooked changing the line of code below).

Wrt $post value in page.php, please keep in mind that include() calls will have "parent" function variable values in scope.

So if the controller rendering function has $post set, it will have a value in the immediate include()-d template, even though this variable isn't explicitly declared there.

And as we all probably know, in The Loop, global $post indeed gets set by the_post(), so the_*() functions can work their magic w/o passing in specific ID-s.

Closing this as outdated