0

I need to hide "uncategorized" from the list of selectable post categories and wrote some code to achieve that. This function worked fine up to WP 4.7.5, the categories were successfully hidden:

add_filter('list_terms_exclusions', 'myproject_hide_uncategorized',1);
function myproject_hide_uncategorized( $exclude_query ) {
  if (function_exists('get_current_screen')) {
    $currentScreen = get_current_screen();

    if ($currentScreen->base == 'post') {
      $newquery = "";
      $excluidos = array();
      $excluidos[] = get_category_by_slug('sem-categoria')->cat_ID;
      $excluidos[] = get_category_by_slug('uncategorized')->cat_ID;
      foreach ($excluidos as $excluido) {
        $newquery .= " AND t.term_id <> '$excluido' ";
      }
      $exclude_query .= $newquery;
    }
  }
  return $exclude_query;
}

However, on later versions (4.8.1 being the latest so far), whenever I try to edit a post or create a new one (visiting `localhost/wp-admin/post-new.php"), I get the following error message shown:

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 130968 bytes) in /var/www/html/wp-includes/class-wp-term-query.php on line 294

Raising the memory limit just gets me a timeout error. I am able to replicate the behavior in clean installs of both WP 4.8 and 4.8.1

It looks like the crash is caused by these four lines, adding it to the functions.php file of any theme seems to cause a crash:

add_filter('list_terms_exclusions', 'this_will_crash_wordpress');
function this_will_crash_wordpress( $exclude_query ) {
  get_category_by_slug('uncategorized');
}

What changed between 4.7.5 that causes this error? How can I make it go away? Do I have to rewrite my code? If so, how?

  • 2
    If I look at your filter correctly, it doesn't do anything, filters are meant to return their result but yours doesn't, it's the same as return null; – Tom J Nowell Aug 6 '17 at 0:57
  • 1
    Also have you considered simply deleting the 'uncategorized' category? Change your default post category to something else and you'll be able to delete uncategorized – Tom J Nowell Aug 6 '17 at 1:12
  • You may also find this question useful as a different way of filtering out terms wordpress.stackexchange.com/questions/38759/… – Tom J Nowell Aug 6 '17 at 1:18
  • I know I could delete the "uncategorized" category, but it's just that I wanted to force the behaviour in code, in a documented way, instead of relying on a checklist that the client / final user would have to follow. – That Brazilian Guy Aug 6 '17 at 1:22
  • 1
    Besides, now my nerd curiosity has been awakened and I want to know the cause! – That Brazilian Guy Aug 6 '17 at 1:22
2

Because get_category_by calls get_term_by which calls get_terms which creates a WP_Term_Query which runs the list_terms_exclusions filter, which runs your code that calls get_category_by which calls get_term_by which calls get_terms etc etc etc

Your problem is that of an infinite loop, the functions you call in your filter, trigger the filter they're called in. It's not that WordPress has changed, but rather that the filter should never have worked to begin with.

Solutions

Breaking The Loop

Unhook your filter before calling get_category_by, then hook it back in afterwards.

I'm not a big fan of this option

Changing from term ID to term slug/name

You don't need to find which term ID has the name uncategorized, you're already passing in SQL, so use t.slug <> 'uncategorized' or something to that effect

This is a bit better, but it still commits the fundamental sin of giving a NOT selector to MySQL. Those kinds of queries are fundamentally expensive and don't scale, you should ask for what you want, not what you do not want.

Pulling the term out in PHP

When the terms are fetched, manually take out the term you don't want in PHP afterwards. This is the most performant options, and scales the best. It's also much more database friendly

That metabox specifically uses these 2 functions to display the terms:

  • wp_dropdown_categories
  • wp_terms_checklist

wp_dropdown_cats can be used to filter the final HTML output of wp_dropdown_categories although you could reach into get_terms and use its filters to remove the term before that point more efficiently. For details on how to do that, see this question/answer

This is my preferred solution

Remove the metabox and add your own

As long as the form inputs are the same as the standard metabox, you can replace it entirely, and there'll be no need for save code.

I'm not a big fan of this solution as it could interfere with other plugins and has the largest effort required

| improve this answer | |
  • 1
    Oh, great that you found it … exactly where I was done with my dive into core ;) Here goes your upvote. – kaiser Aug 6 '17 at 18:37

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.