Expanded meta key comparison operators in 5.3

WordPress 5.1 introduced the compare_key parameter for WP_Meta_Query, allowing developers to perform LIKE queries against postmeta keys. (See #42409 and the related dev note.) WordPress 5.3 expands the options available to compare_key, so that developers have access to meta-key comparison operators similar to those available for meta values. See #43446 and [46188].

After this change, compare_key accepts the following operators: =, LIKE, !=, IN,NOT IN,NOT LIKE,RLIKE,REGEXP,NOT REGEXP,EXISTS, andNOT EXISTS. Usage notes:

  • For parity with value operators, we’ve added support for EXISTS and NOT EXISTS. In the case of compare_key, these map to = and !=, respectively.
  • MySQL regular expression comparison operators (RLIKE, REGEXP, NOT REGEXP) are case-insensitive by default. To perform case-sensitive regular expression matches, it’s necessary to cast to BINARY. To support this, WP_Meta_Query now accepts a type_key parameter; pass 'BINARY' to force case-sensitive comparisons. (This directly parallels the use of type when using regular expressions to match meta value.)
  • As is the case with other WP_Meta_Query-related parameters, the parameters discussed here are available to WP_Query using the meta_ prefix: meta_compare_key and meta_type_key.

#5-3, #dev-notes, #query

Changes to post globals setup and usage in get_the_content() and related functions in WordPress 5.2

In WordPress 4.5, changes were made to some post template functions (specifically, get_the_excerpt() and related functions) to improve their usability outside of the post loop; see #27246. Since that release, some users have noticed that these changes had certain side effects, specifically when the template functions incorrect used the post-related global variables ($authordata, $page, etc) as fallbacks.

WordPress 5.2 addresses these inconsistencies by relying less on post-related globals in functions like get_the_excerpt(), get_the_content(), and wp_trim_excerpt(). Where possible, these functions now prefer the $post value explicitly passed as a function argument; and the main WordPress loop setup has been updated to pass the correct post to the functions wherever possible. See #36934, #42814, [44941].

Aside from greater predictability when using the functions outside the post loop, it’s not expected that these changes will have any visible effect in the vast majority of cases. Developers should note one potentially breaking change: The 'content_pagination' filter now runs before the post globals are populated, which may be a compatibility break in some cases. Callbacks for 'content_pagination' are thus urged to use the $post parameter passed to the filter rather than relying on the globals. See #47133.

More generally, with respect to template functions that are designed for use inside the WordPress loop, developers should take special care when using them outside the context of a loop. setup_postdata(), as always, sets up many of the post-related globals that may be expected by other template functions, but it does not change the value of the $post global. See https://developer.wordpress.org/reference/functions/setup_postdata/#comment-874. Be sure to test compatibility with plugins and themes you’re using before using these template functions outside the loop.


LIKE support for meta keys in 5.1

WordPress 5.1 introduces limited LIKEsupport for meta keys when using WP_Meta_Query. The new compare_key parameter (meta_compare_key as a top-level WP_Query query arg) accepts a value of LIKE in addition to the default =. See #42409 and [42768]. Here’s an example of how the new parameter is used:

$q = new WP_Query(
    'post_type'  => 'my_custom_post_type',
    'meta_query' => array(
        'compare_key' => 'LIKE',
        'key'         => 'foo',

This will generate a query of the form ... AND meta_key LIKE '%foo%' ....

This enhancement follows from changes in WordPress 4.8.3 to the way that MySQL wildcard characters are handled when building queries. The changes in 4.8.3 broke compatibility for some plugins that passed wildcards into the key parameter of meta queries. The new compare_keyfeature in WP 5.1 is not a full replacement for the previous behavior, but it should allow for most use cases. See #43445 for discussion.

#5-1, #dev-notes, #query

Improved taxonomy metabox sanitization in 5.1

WordPress 5.1 will feature a new parameter for register_taxonomy(), 'meta_box_sanitize_cb', which increases flexibility when using core UI for custom taxonomies.

Custom taxonomies can specify which metabox UI they’d like to use in the Classic Editor by providing a meta_box_cb parameter when registering their taxonomies. This meant, for example, that a hierarchical taxonomy can choose to use the post_tags_meta_box() UI instead of post_categories_meta_box(), which is the default. Prior to 5.1, this customization wasn’t fully functional; while the UI could be swapped out in this way, the data submitted when saving posts was always processed according to whether the taxonomy was registered as hierarchical. This led to scenarios where custom taxonomy values weren’t properly parsed when saving a post.

In 5.1, this behavior is changed in the following ways:

  • The POST controller logic that was previously hardcoded in edit_post() has been abstracted into the new taxonomy_meta_box_sanitize_cb_checkboxes()and taxonomy_meta_box_sanitize_cb_input()functions.
  • WP will try to select an appropriate _sanitize_ callback based on the meta_box_cbassociated with the taxonomy. This will fix the underlying bug for existing taxonomies.
  • Developers who need more control over the processing of their metabox data can provide a custom meta_box_sanitize_cb when registering their taxonomies.

For more information, see #36514 and [42211].

#5-1, #dev-notes, #taxonomy

Comment “allowed” checks in WordPress 4.7

In WP 4.4, comment submission was abstracted so that most of the logic was run in a function that returned a value, rather than inline in wp-comments-post.php. See #34059 for background. This overhaul was incomplete: the process of checking for comment floods and duplicate comments – wp_allow_comment() and friends – contained direct calls to die() and wp_die(), making it impossible to use the results of a failed comment check in the context of unit tests, the REST API, or other clients. See #36901. [38778] introduced an optional parameter for wp_allow_comment() and related functions that lets the caller decide whether to preserve the default behavior (wp_die()) or, instead, to return WP_Error objects in the case of failed comment checks.

There is a small backward-incompatible change in [38778]. Historically [5947] it’s been possible to unhook the default comment flood check as follows:

remove_action( 'check_comment_flood', 'check_comment_flood_db', 10, 3 );

In order to maintain backward compatibility with this usage, while at the same time changing the comment flood check so that it returns a value, we’ve performed a trick: check_comment_flood_db() is still hooked in the same way, but is now a wrapper for an add_filter() call that hooks the actual comment flood checking function to the new 'wp_is_comment_flood' filter.

The backward compatibility break is as follows. Calling check_comment_flood_db() directly, in isolation, will no longer do anything (except to register a filter callback). If you need to run WP’s default comment flood check manually, outside the context of wp_allow_comment(), use the new wp_check_comment_flood() function. I searched GitHub and the wordpress.org plugin repo and didn’t find a single instance check_comment_flood_db() being used this way in the wild – it’s hard to imagine a situation where it’d be done, given its previous reliance on wp_die() – but if you’ve done custom work related to comment floods, it’s worth double-checking your code before 4.7 is released.

#4-7, #comments, #dev-notes

Proposal: Status API for taxonomy terms

Below is a proposal for a Term Status API. Feedback and comments are welcome below.


Of WordPress’s four main content types – posts, comments, users, and terms – only terms does not have the concept of “status”. The wp_posts, wp_comments, and wp_users tables all have status columns. The semantics and implementation details differ across posts, comments, and users. But in each case, the idea of “status” has allowed for new and improved user experience: autosave for posts, pending user registrations, spammed comments, and so on. It’s time for terms to have their own ‘status’ too.

Use cases

The immediate use case for term status comes from the customizer. Recent developments have made it possible to draft nav menus and other content in the customizer before publishing to your site. The post_status API makes this possible in the case of most nav menu items: items are set to ‘auto-draft’ until the changes have been saved, which ensures that they’re never visible in the Dashboard. This corresponds nicely to the driving philosophy of the Customizer project, which is that it should be possible to preview changes to your site, with the confidence that the changes will be discarded if you choose not to save them.

Taxonomy terms, on the other hand, cannot yet have corresponding nav items created in the customizer; see #37915. Without resorting to odd techniques (such as a “shadow taxonomy” that is hidden from normal view), it’s not possible to create a taxonomy term that is not immediately available in all relevant interfaces: metaboxes, term queries, etc. Allowing the creation of auto-draft terms will create parity between term-related nav items and other types of nav items, creating a more consistent experience for users setting up their sites in the customizer.

Once the fundamentals of the term status API have been built, it’s possible to imagine a number of other following user-facing improvements:

  • “Trash” status for terms, including the ability to restore items from the trash
  • Private terms, which can only be seen and assigned by users with the proper capabilities
  • Autosave when editing terms in the Dashboard
  • “Pending” terms, submitted by Contributors but not generally available until approved by an Editor
  • AND SO MUCH MORE!!!!1!

Technical outline and proposed implementation plan

I’ve reviewed the developer-facing parts of the Taxonomy API to catalog those areas that would need adjustment with the introduction of term status. I’ve also reviewed the post_status API to get a better sense of what a relatively well-rounded implementation of “status” has to recommend (and recommend against!). Here’s how I see the implementation process, broken down into phases that may span releases.

  1. Database schema upgrade
    While it’d be possible to implement term status using termmeta, it’d almost certainly result in significant performance problems. A dedicated ‘status’ column in wp_term_taxonomy is fastest, and best parallels the other content types.
  2. Upgrade routine
    The upgrade routine would include the schema update, as well as the filling-in of the new database column for all existing terms.
  3. Status registration and fetching API functions
    The minimal functions needed for ‘auto-draft’ support are probably as follows:
    • register_term_status() (‘publish’ and ‘auto-draft’ would likely be the first two statuses implemented)
    • get_term_status(), _status_object() and _stati(), to be used when whitelisting, etc
  4. ‘Status’ support when creating or updating terms
    Presumably, this will be a ‘status’ argument in wp_update_term() and wp_insert_term(), with checks against a whitelist of registered statuses.
  5. ‘Status’ support when querying terms
    For backward compatibility, get_terms() and wp_get_object_terms() should default to returning only those terms with the ‘publish’ status. A ‘status’ parameter will allow more fine-grained filtering. This parameter will work similarly to other item queries, with support for arrays of statuses as well as magic terms like ‘any’. More specific functions like get_term_by() and term_exists() will either ignore status or presume ‘public’; more discussion is needed. We’ll also need to make decisions about how non-public terms are handled in hierarchical queries – get_pages() and friends may be helpful benchmarks.
  6. Status “transition” logic
    Similar to wp_transition_post_status(), we want hooks that fire on term status transitions.

I take items 1-6 to be a minimal API for term statuses. Next are the details related to the first use case: ‘auto-draft’.

  1. ‘Auto-draft’ and slugs
    Posts with status ‘auto-draft’ do not get slugs, so that they don’t interfere with the creation of other posts. See wp_unique_post_slug(). ‘Auto-draft’ should probably act similarly.
  2. ‘Auto-draft’ terms should be excluded from XHR exports
    Just like posts.
  3. ‘Auto-draft’ deletion on a schedule
    Posts with ‘auto-draft’ are deleted when they’re older than 7 days. This is not likely to be a huge problem for terms, but should probably be addressed anyway.
  4. Protect auto-draft (and other terms from non-public statuses) in canonical, permalinks, and rewrites
    Things that it (probably) shouldn’t be possible to do:
    • Get a permalink of the archive for a non-public term
    • Load the archive of a non-public term on the front-end
  5. Convenience functionsSomething like wp_publish_term() would be convenient. Maybe others.

I believe that 7-11 are pretty close to what we need to support the Customizer use case. There are a few more obvious things that can happen in future releases, independent of any specific feature:

  1. Status interface when editing/creating a term
    Auto-draft won’t be a ‘public’ status, but once another ‘public’ status is available, a dropdown should appear.
  2. Status filters for the Terms list table
    Like we have for posts.
  3. Integration with capabilities
    Work is being done on fine-grained capabilities for terms #35614. Certain statuses will probably integrate with this.

These last items aren’t necessary for the initial use case, but are the kinds of things that developers will expect as they start using term statuses in plugins.

Potential problems

A couple of potential gotchas:

  • Performance. Term queries are currently pretty fast. Adding a status column, and including an additional WHERE clause with every query, is not going to make things faster. We should think about the proper use of indexes, etc.
  • Direct database queries. Plugins making direct queries against the terms tables will not properly exclude non-public terms. Not much we can do about this from a technical point of view, but we should write some good documentation to help avoid problems.
  • How to handle single term-fetching functions. get_term_by(), get_term(), and term_exists() could all be used in ways that expect the returned value to be a public term (since all terms are now, in fact, public). It would be bad if someone got back an ‘auto-draft’ term for get_term_by( ‘name’, ‘foo’ ). We can explicitly blacklist ‘auto-draft’ terms. Or we can always exclude non-public terms. Either way, we probably want to offer flexibility for developers who want to return non-public terms. I’m not sure of the strategy here: we want something that balances developer expectations with backward compatibility.

Next steps

#38227 will be a parent ticket for work on this project.

There will be a meeting for all interested parties at Tuesday, October 11 18:00 in #core on Slack.

WP_Term_Query in WordPress 4.6

WordPress 4.6 will introduce WP_Term_Query. This new class brings parity between taxonomy term queries and WP’s other content type queries: WP_Query, WP_Comment_Query, and WP_User_Query. And – as in the case of posts, comments, and users – the get_terms() function has been converted to a wrapper for the new WP_Term_Query.

Aside from support for querying by term_taxonomy_id #37074, no functional changes to get_terms() will be introduced in 4.6; 100% backward compatibility with previous uses is the goal for this release.

For more background on the change, see #35381.

#4-6, #dev-notes, #taxonomy

Query component bug scrub – May 24

A 90-minute bug scrub for the Query component will be held at Tuesday 24 May 2016, 18:00 UTC in the #core channel on Slack. We’ll spend some time looking through the Awaiting Review milestone, and we’ll have a glance at any specific tickets that attendees might interested in talking about.

#bug-scrub, #query

Taxonomy bug scrub summary, 2016-05-05

We had a taxonomy component bug scrub today. Slack archive: https://wordpress.slack.com/archives/core/p1462464023003714

Present: @boonebgorges, @helen, @barryceelen, @wonderboymusic. @swissspidy and @jans-1 voiced their opinions via emoji reactions.

We cleared 8 items from the Awaiting Review queue, bringing it down to 35 tickets.

Let’s do this again real soon!

#4-6, #bug-scrub, #taxonomy

Taxonomy Component Bug Scrub – May 5, 2016

A bug scrub focused on open tickets in the Taxonomy component will be held in the #core channel on Slack at May 5, 2016 16:00 UTC. We’ll focus on tickets languishing in Awaiting Review, and/or any specific tickets that attendees may want to chat about. People from all categories, however you may term yourself, are invited to attend and help us slug things out.

#bug-scrub, #taxonomy