6

I have the following code running in a plugin:

 add_filter('the_content','thousand_pay');

//Callback function
function thousand_pay($content)
{
    echo $content;

    if( !in_category( 'Stories') )

    {
        return;
    }
    ?>
<hr></hr>
[Some HTML]
<?php

    return
}

For some reason, on individual posts' pages the HTML gets printed multiple times:

Bug where HTML is printed multiple times

Can anyone think of why this would be? I read here that I might have to add to the conditional to check is_singular() and is_main_query(), so it would look like if(!in_category('Stories') || !is_singular() || !is_main_query(), but that just seems to stop the HTML getting printed at all on a post page. Any ideas?

3 Answers 3

7

It is normal for content to be accessed multiple times. For example SEO plugins need to do this to access it and generate meta data from.

Also it is a filter hook. Filters should never echo anything to the page, they are meant to modify passed value and return it.

If you do want to do something at that point, but only within the loop then in_the_loop() is condition you need.

2
  • Thanks! What kind of hook would I want to use to add something?
    – Ed King
    May 5, 2016 at 10:21
  • You can use this to add something, you just need to modify the passed value and return it instead of just echoing things.
    – Rarst
    May 5, 2016 at 10:22
12

I was having the same issue. My the_content filter was being called multiple times and this was slowing down page load as my the_content filter was calling an external API. So in my case, the API was being queried multiple times for the same data.

I tried using in_the_loop(), is_singular() and is_main_query() but sometimes, depending on the theme, those failed to limit calls to my filter to one time.

So I added a constant to my filter and that seems to have fixed the issue.

Here's an example of how to limit your calls to the the_content filter to one time:

add_filter( 'the_content', 'se225721_the_content' );

function se225721_the_content( $content ) {

    if ( ! in_the_loop() ) {
        return $content;
    }

    if ( ! is_singular() ) {
        return $content;
    }

    if ( ! is_main_query() ) {
        return $content;
    }

    $content = ucwords( $content );

    remove_filter( 'the_content', 'se225721_the_content' );

    return $content;
}

Hope that helps!

Eric

3
  • I suppose this works. You could also remove the filter when the function is called. Feb 17, 2017 at 19:32
  • 2
    You're right! That is simpler. I updated the code to use the remove_filter() option instead of defining a constant.
    – Eric
    Feb 23, 2017 at 15:38
  • Thanks, you can even shorten it by doing following if ( !in_the_loop() or! is_singular() or !is_main_query()) { return $content; }
    – Sid
    Oct 14, 2021 at 10:42
1

I have found another way to achieve this by using the did_action() function and a custom action hook.

Here is the solution:

$custom_hook = 'my_plugin_prefix_a_custom_action_hook';
function my_plugin_prefi_my_filter_callback($content){
  if(did_action($custom_hook) == 0){
    do_action($custom_hook);
    //your filter action code goes here: modification of $content
  }
  return $content;
}

function my_plugin_prefix_custom_action_hook_callback(){
  //do nothing
}
add_action($custom_hook, 'my_plugin_prefix_custom_action_hook_callback');

At first the run count of action hook $custom_hook will be 0 and do_action($custom_hook); statement will make the run count of $custom_hook to 1 and this will prevent subsequent execution of the body of the if statement.

Your Answer

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

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