Let me try to explain what happened to me :

I have a shortcode plugin which display child pages of the current page (a mod of http://wpist.me/wp/child-pages-shortcode/, very useful).

In the display function, it uses get_posts(), with very basic code, conform to the codex examples (http://codex.wordpress.org/Function_Reference/get_posts) :

global $post;
foreach ($pages as $post) {
    setup_postdata($post);
    $html = $post->post_title;
    $html .= get_the_excerpt();
    $html .= get_the_content();
}

With PHP 5.2, it works as expected. I updated to PHP 5.4, and get_the_content() is returning the parent content, instead of the current child. Previous lines are still returning the correct data, but it seems that the first call to a get_the_* function changes the content of the global $post.

It works fine if I use $post->* instead of get_the_*(), but get_the_*() has useful automatic treatment.

I found a way to make it work, so I post it here, so maybe someone can explain me why it's behave like this.

  1. I changed the name of the loop var to avoid messing the global $post (I don't understand why codex example use $post in their loop, by the way…)

  2. I added a setup_postdata call after each get_the_*() calls to reinit the global to the correct post :

working code :

global $post;
foreach ($pages as $mypost) {
    setup_postdata($mypost);
    $html = $mypost->post_title;
    $html .= get_the_excerpt();
    setup_postdata($mypost);
    $html .= get_the_content();
}

thanks

Edit : the full code of my function (this is working correctly) in case the simplified version confuses you :

private function display($param, $block_template)
{      
global $post;

$html = '';
// defining the content of $template
// cut
// end defining $template

$args = array(
    'post_status' => 'publish',
    'post_type' => 'page',
    'post_parent' => $param['id'],
    'orderby' => 'menu_order',
    'order' => 'ASC',
    'nopaging' => true,
);
$args = apply_filters('child-pages-shortcode-query', $args, $param);

$pages = get_posts($args);
foreach ($pages as $post2) {
    setup_postdata($post2);
    $post = apply_filters('child_pages_shortcode_post', $post2);
    $url = get_permalink($post2->ID);
    $img = get_the_post_thumbnail($post2->ID, $param['size']);
    $img = preg_replace( '/(width|height)="\d*"\s/', "", $img);
    $tpl = $template;
    $tpl = str_replace('%width%', esc_attr($param['width']), $tpl);
    $tpl = str_replace('%post_id%', intval($post2->ID), $tpl);
    $tpl = str_replace('%post_title%', $post2->post_title, $tpl);
    $tpl = str_replace('%post_subtitle%', get_post_meta($post2->ID, "Sous titre", true), $tpl);
    $tpl = str_replace('%post_url%', esc_url($url), $tpl);
    $tpl = str_replace('%post_thumb%', $img, $tpl);
    if (isset($param['disabled_excerpt_filters']) && $param['disabled_excerpt_filters']) {
        $tpl = str_replace('%post_excerpt%', $post2->post_excerpt, $tpl);
    } else {
        $tpl = str_replace('%post_excerpt%', get_the_excerpt(), $tpl);
        setup_postdata($post2);
    }
    if (isset($param['disabled_excerpt_filters']) && $param['disabled_excerpt_filters']) {
        $tpl = str_replace('%post_content%', $post2->post_content, $tpl);
    } else {
        $tpl = str_replace('%post_content%', get_the_content(), $tpl);
    }
    $html .= $tpl;
}

wp_reset_postdata();

if (!$block_template) {
    $html .= '<hr style="border:0px; clear:both;"></div>';
}

return apply_filters("child-pages-shortcode-output", $html);
}
  • 2
    you're redefining $html with every operation. Do you mean to be concatenating? $html .= 'something else'; – GhostToast Jan 14 '14 at 15:04
  • No, it's a very simplified version of my code. You can ignore this. – Matthieu Jan 14 '14 at 15:42
  • Seems this has a lot to do with this plugin: WordPress › Child Pages Shortcode « WordPress Plugins -- tried running your code without the shortcode/plugin reference? – GhostToast Jan 14 '14 at 16:38

With all of the get_the_* flavor of functions, you have the option of passing an ID. Why not do:

$html .= get_the_excerpt($post->ID);

Otherwise it's implying the originating post ID.

You actually need to name var $post for everything to work smoothly. setup_postdata() is not actually setting $post's data, is just uses it for rest of global stuff.

Either you are not showing enough code to see what might be going wrong or get_ function has something hooked inside of it that messes up global.

  • Along these lines, I think it odd that $post is globalized prior to this foreach – GhostToast Jan 14 '14 at 16:16
  • Me too. But it's in the third example of the codex : codex.wordpress.org/Function_Reference/get_posts – Matthieu Jan 14 '14 at 16:28
  • I can't make it work by using $post in the loop. But it works fine with $mypost. – Matthieu Jan 14 '14 at 16:29
  • Globalized $post is correct for custom loop. If this is going wrong it's due to something not part of code displayed. – Rarst Jan 14 '14 at 16:33
  • I don't have the time to made a simple test case, but I'm pretty sure that it doesn't work in PHP 5.4 when using $post in the loop. It looks like a change in the context behavior of PHP... – Matthieu Jan 14 '14 at 16:43

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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