New User Related Short Circuit Filters

WordPress 5.1 introduces new short circuit filters to WP_User_Query and count_users().

The users_pre_query filter was introduced in #44373 and runs before the database query takes place. This enables short-circuiting the database query entirely to return custom results. Returning a non-null value from the filter will bypass WordPress’s default user query (similar to the posts_pre_query filter for WP_Query added in #36687).

Developers should note that filtering functions that require pagination information are encouraged to set the total_users property of the WP_User_Query object (which is passed to the filter by reference). If WP_User_Query does not perform a database query, it will not have enough information to generate these values itself.

Using the users_pre_query Filter

Below is a rough example of how a plugin can use the filter to replace the default behavior of WP_User_Query with a call to a remote data store.

function myplugin_do_external_users_query( $users, $user_query ) {
	$response = wp_remote_get( 'https://my-remote-data-store/foo/bar' );

	if ( 200 === wp_remote_response_code( $response ) ) {
		$response           = json_decode( wp_remote_retrieve_body( $response ) );
		$users              = array_map( 'intval', $response->user_ids );
		$query->found_users = (int) $response->found_users;
	}

	return $users;
}
add_filter( 'users_pre_query', 'myplugin_do_external_users_query', 10, 2 );

Similar to posts_pre_query, callbacks must be careful about fields – the external API may have to return user IDs or quasi-WP_User objects, depending on the value of fields.

A more in-depth example of how this filter can be utilized can be found in this project, which enables user query caching.

Filtering the count_users() Function

In addition, a pre_count_users filter was added in #43693 which enables short-circuiting the count_users function before the database query takes place. Returning a non-null value from the filter will bypass the default queries.

Using the pre_count_users Filter

Here is a sample code snippet showing how to use this filter:

function myplugin_do_external_users_count( $user_count) {
	$response = wp_remote_get( 'https://my-remote-data-store/foo/bar' );

	if ( 200 === wp_remote_response_code( $response ) ) {
		$response = json_decode( wp_remote_retrieve_body( $response ) );
		$user_count    = $response->user_count;
	}

	return $user_count;
}
add_filter( 'users_pre_query', 'myplugin_do_external_users_count', 10, 2 );

More Filters Coming Soon…

Several additional filters for similar query objects are currently being explored and worked on, and are currently slated for a future release:

  • A short circuit for WP_Comment_Query (#45800).
  • A short circuit for WP_Site_Query and WP_Network_Query with a plan to add a sites_pre_query filter (#45749).
  • A short circuit for WP_Term_Query with a plan to add a terms_pre_query filter (#41246).

Why Add These Filters?

These query pre-filters enable plugins to use an alternate database store for queries, for example returning results from an external service such as an Elasticsearch server. The process started with the main post query, and these are now being expanded that to other queries in WordPress.

#5-1, #dev-notes

Dev Chat Agenda: January 16th

Do you like devchat? I like devchat. This is the agenda for the weekly devchat meeting on Wednesday, January 16 2019 at 2100 UTC:

If you have anything to propose to add to the agenda or specific items related to those listed above, please leave a comment below. Either way, we look forward to seeing you at the devchat this week!

This meeting is held in the #core channel in the Making WordPress Slack.

#5-1, #agenda, #core, #dev-chat

WordPress 5.1 Schedule Updates

Good news, everyone! WordPress 5.1 is on schedule, and progressing nicely. At the time of writing this post, we have 123 open tickets milestoned for WordPress 5.1, down from nearly 500 a week ago. Thank you to everyone who’s helped triage these tickets! 💖

To keep things running smoothly as we head towards the Release Candidate and WordPress 5.1, I have some intermediate targets that I’d like us to aim for.

Beta 2: January 21, 2019

There’s still a moderate amount of ticket triage to do, including working through the 87 tickets reported against trunk that haven’t been milestoned. Many of these are reported against the wrong version, but they do need to be triaged prior to beta 2.

After beta 2 is released, any bug reports against versions prior to 5.1 will be move to the 5.2 or Future Release milestones.

Beta 3: January 29, 2019

This is the soft string freeze release. All string changes (except for the About page) will be moved to the 5.2 milestone.

Release Candidate 1: February 7, 2019

There should be no open tickets in the 5.1 milestone by RC1, apart from release-related tasks.

5.1 will be branched with RC1, and work can begin in trunk on WordPress 5.2. As we’re back to standard commit review practice, any WordPress 5.1 fixes will need to be reviewed by two committers before porting to the 5.1 branch.

WordPress 5.1: February 21, 2019

🎉

#5-1

New function: human_readable_duration

In WordPress 5.1, the new human_readable_duration function will be introduced. This function accepts a duration in hours, minutes and seconds and returns a “human readable” string appropriate for screen readers.

The function takes a string representing a time duration, with hours, minutes and seconds separated by a : (colon). For example, calling human_readable_duration( '3:10' ); will return 3 minutes, 10 seconds. Minutes and seconds are required. For 10 seconds, the call would be human_readable_duration( '0:10' );.

This new function was added as part of the work to improve the display of media metadata in #39667 where, in addition to adding units, aria-labels with plain language descriptions were added to improve accessibility:

https://cldup.com/P8-Sq1n6Bv.png
Improved time duration accessibility in the media library using the human_readable_duration() function.


#5-1, #dev-notes

PHP Site Health Mechanisms in 5.1

Update: A few critical naming changes have been made regarding the fatal error protection since the post was originally published. Most importantly, the drop-in to override the new shutdown handler is now called fatal-error-handler.php, and the class to override is called WP_Fatal_Error_Handler. Furthermore, a new WP_DISABLE_FATAL_ERROR_HANDLER constant allows entirely disabling the feature via wp-config.php. The rest of the post is also updated to use these new terms, however you might need to update your code if you built anything based on the previous information. For a history of what exactly has changed since the original post date, please refer to the umbrella ticket #44458.


WordPress 5.1 will start showing notices to administrators of sites that run on long outdated PHP versions. This part of the Servehappy and more globally Site Health projects paves the way to a more secure and performant web and, more specifically to WordPress, to a bump of the minimum required PHP version.

The current threshold for which PHP versions to display the notice will be anything below 5.6. While the lowest PHP version still receiving security updates is currently 7.1, the idea is to not go all the way there at the beginning to limit the support load. PHP 5.6 is the intended version to bump WordPress requirements to, and from then the threshold for the PHP notice will increase granularly, with the goal to over time catch up with the actual PHP version progress. The threshold is managed via a new Servehappy API endpoint on wordpress.org, so the version numbers can be modified independently of a WordPress release.

Warning about outdated PHP version in WordPress backend

The link from the button points to a new support page about updating PHP which briefly explains the problem and then dives into how to prepare for an update and perform it. The link URL in WordPress is a translatable string so that locales can provide their own versions of the page. In order for that process to be straightforward, the content is managed through a page template, with the translation strings thus being available in GlotPress (see https://meta.trac.wordpress.org/ticket/4004). Furthermore, the link can be adjusted via either an environment variable WP_UPDATE_PHP_URL intended for hosting providers or a filter wp_update_php_url for a more dynamic approach on the code level. Replacing the URL should happen in cases where a more specific guide to update PHP on the given environment exists. The hosting provider is the preferred source to set this, so plugins are recommended to honor this priority and not unconditionally override it. Furthermore, if the URL is changed in any way, the URL to the original WordPress resource is still maintained as an additional link, which you can see in the following screenshot:

Warning about outdated PHP version with custom URL and related annotation

For further background information on these changes, please refer to the respective tickets #41191 and #45686.

Fatal Error Protection

To help the process of recovering errors that result from updating the PHP version, a mechanism has been implemented to detect fatal errors and, in certain designated areas of WordPress, recover from them. While updating PHP is mostly fairly straightforward and popular plugins and themes are typically maintained well, not necessarily all extensions are compatible with the latest PHP versions yet. So unfortunately there might be cases where a plugin or theme causes the WordPress site to no longer be accessible after the PHP update by causing a fatal error.

With the so-called WSOD protection (white-screen-of-death protection), WordPress will recognize when a fatal error occurs and which plugin or theme is causing it. When accessing the admin backend, the respective extension will be paused, allowing users to still log in to their site so that they can (at least temporarily) fix the problem. Once the extension is known to be fixed, it can be resumed from the backend. While this does not necessarily make updating PHP easier, it lowers the burden of possibly running into the case where you are completely locked out of your site when you do not have access to the codebase.

Extensions are only paused in the admin backend and a few other areas while for example the frontend stays unaffected, and thus broken. Since it is impossible to predict which functionality the broken extension is responsible for, it would be dangerous to have it disabled in the frontend. It is more clear to a user accessing the site to see a message that it is currently not accessible rather than, without notice, no longer having access to a certain part of the key functionality. In the frontend, a message that the site is currently inaccessible will be displayed via a regular wp_die() call, with a link to the admin backend. Sites that would like to modify this output can use a new php-error.php drop-in that should send the necessary HTTP header as well as the output.

Default output in the frontend when WSOD protection detects has detected an error

Note that, while the primary reason for implementing the fatal error protection mechanism was making the process of updating PHP less “dangerous”, it is technically not tied to the update at all. In fact, it will be enabled permanently and discover fatal errors under any circumstances.

For sites that prefer to alter this behavior or sites that would like to omit the WSOD protection altogether, it is possible to add a new drop-in file fatal-error-handler.php. That file should declare a custom shutdown handler class for treating fatal errors that extends the default WP_Fatal_Error_Handler, and then return the instance of it to use. The default class’s functionality is split into distinct methods so that it is easy to reuse and override as granularly as necessary in a child class. The functionality can also be disabled entirely if that is preferred, via a new constant WP_DISABLE_FATAL_ERROR_HANDLER or, more dynamically, a corresponding wp_fatal_error_handler_enabled filter.

For further background information on these changes, please refer to #44458.

Honoring Plugin PHP Version Requirements

When browsing plugins to install, WordPress 5.1 will display a warning for those plugins that require a higher PHP version than the one currently active. While that screen previously already included such information about WordPress version compatibility, it now does the same for PHP. Furthermore, for both of these potential issues, WordPress will now enforce these requirements, disabling the button to install such plugins.

This is only a first step in enforcing version requirements of plugins and themes. In a future WordPress version, these restrictions will expand also to updating or activating plugins and eventually cover themes as well.

For further background information on these changes, please refer to #43986.

#5-1, #dev-notes, #servehappy

New Styling for Admin Table Pagination Links in WordPress 5.1

In WordPress 5.1 the admin table pagination links have had their CSS styling modified.

New styling of admin table pagination links in WordPress 5.1

This CSS change improves the color contrast ratio for better accessibility and improves consistency across the admin screens. The pagination links are now styled like the standard WordPress buttons. By default, they have a lighter background color. The background color will no longer change to blue on hover and focus.

Plugins and themes should not be affected by this change unless they are re-using the .tablenav and .tablenav-pages CSS classes. All plugin and theme developers should verify that this change does not affect them.

You can read more information about this change in the related Trac ticket, #41858

#5-1, #dev-notes

New REST API Notice in 5.1

Edit: On January 14, 2019, the Good and Bad Practices section was added to show both correct and incorrect code examples.

Starting in WordPress 5.1, if register_rest_route() is not called on the rest_api_init action hook, a “doing it wrong” notice will be triggered. This notice is being added in an effort to encourage best practices when registering REST API endpoints.

First, let’s look at what happens when WordPress loads to set up the REST API and explore a few reasons why this pattern is beneficial.

REST API Bootstrap Process

WordPress does its best to ensure that the REST API is only loaded when a REST request is being performed. To do this, the rest_api_loaded() function is run on the parse_request action and checks for a value in the rest_route query argument. This argument is populated with a value when the Rewrite API matches a WordPress REST API URL. When a value is present, the rest_get_server() function is called to instantiate the WP_REST_Server class, store it for use across WordPress, and to run the rest_api_init action hook.

Performance

When register_rest_route() is called, it invokes rest_get_server() to retrieve the global WP_REST_Server instance created in the bootstrap process. But, if the instance has not been set up yet, it is instantiated then and the rest_api_init action hook is run. This means that every function added to the rest_api_init hook will fire at that time. This could result in a large performance hit.

For example, say register_rest_route() is called on the init action, or, just called in a theme’s functions.php file. The REST API server would be set up for every WordPress request, even those that are not actually aimed at the REST API.

Missing Endpoints

If register_rest_route() is called too early, it’s also possible that endpoints will go missing and never be registered. This happens when other plugins are not given the chance to register their rest_api_init hooks.

For example, say register_rest_route() is called directly in an mu-plugin file. This will cause the REST API to be set up before regular plugins are run, so their rest_api_init hooks will not be registered.

Good and Bad Practices

Let’s look at a few code examples and detail why they are good or bad.

Bad Practice

register_rest_route(
	'hello-world/v1',
	'/phrase',
	array(
		'method'   => 'GET',
		'callback' => 'myplugin_get_endpoint_phrase',
	)
);

In this example, register_rest_route() is called directly in a file without being attached to an action hook. This means the function will be called as soon as the file is loaded by WordPress. All of the potential issues detailed above are possible, and a _doing_it_wrong() notice will be triggered.

Good Practice

function myplugin_register_endpoints() {
	register_rest_route(
		'hello-world/v1',
		'/phrase',
		array(
			'method'   => 'GET',
			'callback' => 'myplugin_get_endpoint_phrase',
		)
	);
}
add_action( 'rest_api_ini', `myplugin_register_endpoints` );

In this example, register_rest_route() is correctly placed inside of a function that is added to the rest_api_init action hook. It will only execute when the rest_api_init action hook is executed. The potential issues detailed above are avoided, and no _doing_it_wrong() notice is triggered.

Changes Required

Plugins and Themes

All plugins and themes should double check that their REST API endpoints are being registered correctly using the rest_api_init action hook. This best practice is also mentioned in the Routes and Endpoints section of the REST API Handbook. If this was a resource used when developing, chances are you won’t have to change anything!

Unit Tests

Because some unit tests require custom endpoints to exist, it is not uncommon for a test method to call register_rest_route() directly. If a test method calls the function before rest_api_init, a previously passing test method may now fail. This can be fixed in two ways.

The first way is to use rest_get_server() to create the WP_Rest_Server instance for your tests. Since rest_api_init is run within that function, this will prevent the notice. This approach can be seen in the Tests_REST_Server class. The wp_rest_server_class filter still allows you to replace the default WP_Rest_Server class with your own for testing purposes with this method.

The second way is to call do_action( 'rest_api_init' ); directly in your test method or setUp() method. This method is for scenarios where complete control is needed over the REST server setup process. This can approach can be seen in the Tests_REST_API class.

You can read more information about this change in the ticket on Trac.

#5-1, #dev-notes, #rest-api

Cron Improvements with PHP-FPM in WordPress 5.1

A new change in 5.1 Beta 1 introduces a change in behaviour for cron spawning on servers running fastcgi and PHP 7.0.16+.

When available, requests to wp-cron.php will now return a repsonse immediately, before processing lengthy cron jobs, allowing the request that spawned the cron process to end without blocking regardless of the transport method used to trigger it. See #18738 for discussion and research.

This should work well on PHP-FPM installs and cause no issues on other servers. We could use some testing and reports to confirm this, since a similar method could be used in other contexts such as shutdown hooks if it proves to be safe and reliable (see #41358, #27669, #27122).

#5.1 #dev-notes

#5-1

Dev Chat Summary: December 12th

Dev Chat Scheduling

As many folks will be away over the Christmas/New Year period, the next few meetings will be as follows:

  • December 19: Normal meeting.
  • December 26: Normal meeting will not be happening. There are likely to be core folks around to answer questions for an open floor session.
  • January 2: Normal meeting.

5.0.2 Schedule and Scope

Please note that WordPress 5.0.1 has just been released, so any previous mentions of scope or schedule for WordPress 5.0.1 should now be read as applying to WordPress 5.0.2.

WordPress 5.0.2 is intended to be released two weeks after WordPress 5.0, which would make the release date December 20. To give a little more space before the Christmas/New Year holiday period, I’ve proposed that it be released December 19.

Milestone Dates

  • Release Candidate 1: December 14, 2018
  • Release Candidate 2 (if needed): December 17, 2018.
  • General Release: December 19, 2018.

The following items are in scope for the 5.0.2 release:

  • Gutenberg 4.7 was released today, the fixes in this plugin release will also be in WordPress 5.0.2.
  • Twenty Nineteen bugs and visual issues.
  • There are a few PHP 7.3 compatibility fixes to be made.

Any other tickets currently milestoned for 5.0.2 will be considered on a case-by-case basis, priority will be given to tickets with patches, testing, screenshots, and any other relevant information to show that they’re ready to land immediately.

5.1 Schedule and Scope

As there are already over 200 tickets fixed in WordPress 5.1, I’d like to propose that WordPress 5.1 has a relatively short release cycle.

Milestone Dates

  • Beta 1: January 10, 2019
  • Release Candidate 1: February 7, 2019
  • General Release: February 21, 2019

A key point from the WordPress 5.0 cycle was that it demonstrated the value of having a hard feature freeze at beta 1, as well as string freezes and strict bug fixing policies during the release candidate phase. With that in mind, I’d like to propose that we retain these policies for the WordPress 5.1 cycle.

The tickets already fixed in WordPress 5.1 need to be reviewed, to ensure they’re all stable for release in this cycle.

Apart from that, the PHP upgrade warnings and the White Screen of Death protection from the Site Health Check project are currently the only uncommitted features scheduled for WordPress 5.1. The PHP upgrade warnings are currently soft warnings, ahead of the minimum PHP version bump proposed for April 2019.

@matt will be continuing his role as release lead into WordPress 5.1. Any other feature proposals will need to be approved by him.

Please leave feedback on this post, so the scope and schedule can be confirmed in the next day or two.

Focus and Component Updates

REST API

The REST API group will be re-opening discussion around authentication solutions. They’ll be posting further information about this project on make/core.

Core JS

The Core JS group didn’t meet this week, due to many folks travelling home from WCUS. They’ll be resuming normal meetings next week.

Core Themes

The current themes focus is on triaging Twenty Nineteen issues for 5.0.2, as well as preparing to move activity from GitHub into Trac. This move will likely happen immediately after 5.0.2.

#5-0-1, #5-0, #5-1, #core, #dev-chat #summary