3

I am trying to create a modular plugin that includes action hooks for developers to add content before and after the main shortcode content. I'm having some trouble because anything I do in the function called by the action hook is always echoed out at the top of the shortcode instead of inside the shortcode where it belongs.

I've been searching and I did come across this response from Pippin Williams on a recent ThemeForest thread, where he recommends using output buffering. I haven't gotten this to work properly for me, and I've read elsewhere that output buffering should only be used as a last resort, so I'm still looking for a clean solution.

The simplest shortcode ever:

add_shortcode('shortcode','example_shortcode');

function example_shortcode( $atts ) {

  $shortcode_output = "<p>Some shortcode content.</p>";
  $shortcode_output .= "<p>More shortcode content.</p>";

  return $shortcode_output;

}

Now lets add an action:

add_shortcode('shortcode','example_shortcode');

function example_shortcode( $atts ) {

  $shortcode_output = "<p>Some shortcode content.</p>";
  $shortcode_output .= "<p>More shortcode content.</p>";

  do_action('below_shortcode');

  return $shortcode_output;
}

add_action('below_shortcode', 'example_action_output');

function example_action_output() {
    echo "<p>This should be output at the end.</p>";
}

The contents of example_action_output() are returned above the shortcode content because of the echo statement. I tried output buffering as recommended by Pippin:

add_shortcode('shortcode','example_shortcode');

function example_shortcode( $atts ) {

  $shortcode_output = "<p>Some shortcode content.</p>";
  $shortcode_output .= "<p>More shortcode content.</p>";

  ob_start();
  do_action('below_shortcode');
  return ob_get_clean();

  return $shortcode_output;
}

add_action('below_shortcode', 'example_action_output');

function example_action_output() {
    echo "<p>This should be output at the end.</p>";
}

This returned the contents of example_action_output(), but wiped out the rest of my shortcode. Any suggestions?

Thanks, Dalton

4
  • 1
    Is anything else hooked to below_shortcode? Could you not implement it as a filter & return the output instead? Jul 25, 2012 at 12:57
  • I was planning to use the hooks to build the plugin internally, and then make those hooks available to developers as well, so yes, theoretically more than one function could be attached to the same hook. Jul 25, 2012 at 13:03
  • Just FYI, your version using my output buffer method is incorrect. You have two return statements, meaning only the first one will actually return anything. You should use $shortcode_output .= ob_get_clean(); instead of `return ob_get_clean();'.
    – Pippin
    Jul 25, 2012 at 14:01
  • @Pippin: thanks, yeah, figured that out after seeing Kalengi's answer. It all makes sense now. Jul 25, 2012 at 14:44

2 Answers 2

5

Try this:

function example_shortcode( $atts ) {

    $shortcode_output = "<p>Some shortcode content.</p>";
    $shortcode_output .= "<p>More shortcode content.</p>";

    ob_start();
        do_action('below_shortcode');
        $below_shortcode = ob_get_contents();
    ob_end_clean();

    $shortcode_output .= $below_shortcode

    return $shortcode_output;
}
5
  • 1
    As the OP mentioned, he wants to avoid output buffering.
    – kaiser
    Jul 25, 2012 at 13:15
  • I'd be willing to use output buffering if that's the best way to do it. I guess I need to do more research into output buffering and consider the implications. Jul 25, 2012 at 13:19
  • 1
    The good news is that definitely works, so thank you. Now I have a bunch of follow up questions about output buffering and whether I should be using a filter instead of an action to accomplish this. :-) Jul 25, 2012 at 13:29
  • The suggestion by @TheDeadMedic is actually a better way to go. The WP way is to use filters if you want users of your plugin to add content to the plugin's output pipeline.
    – KalenGi
    Jul 25, 2012 at 15:05
  • Use ob_get_clean() and save yourself a step
    – jgraup
    Aug 27, 2016 at 17:38
1

My answer would include a filter function that adds the text like this:

add_shortcode('shortcode','example_shortcode');

function example_shortcode( $atts ) {

    $shortcode_output = "<p>Some shortcode content.</p>";
    $shortcode_output .= "<p>More shortcode content.</p>";

    $shortcode_output .= apply_filter('below_shortcode', $shortcode_output);

    return $shortcode_output;
}

add_filter('below_shortcode', 'example_action_output', 10, 1);

function example_action_output( $text = '' ) {
    return $text . "<p>This should be output at the end.</p>";
}
2
  • A filter hook is a very clean way of doing it. The catch here being that OP wanted a solution that supports an action hook
    – KalenGi
    Aug 28, 2016 at 15:24
  • This worked very well for my purpose. apply_filter() should be apply_filters() ref though (with an s). Mar 6, 2020 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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