New wp_get_environment_type() function in WordPress 5.5

Edit: 7/28/2020: Previously, this feature included filters to modify the environment type and available environment types. These were removed in [48662] as the function is called too early for any plugins to properly hook into these filters. This post was updated to remove mention of those filters appropriately.

Edit: 8/28/2020: Previously, this feature a way to modify the allowed environment types. This option was removed in [48895] as this made it unreliable to use. This post was updated to remove the mention of the previously available constants. Read the post.

WordPress 5.5 introduces a new wp_get_environment_type() function that retrieves a site’s current environment type. This allows pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party and theme authors to more easily differentiate how they handle specific functionality between production and development sites in a standardized way.

By default, this function will return production. Other values supported are local, development and staging.

Below is an example of how this new function can be used to determine a site’s environment type.

switch ( wp_get_environment_type() ) {
    case 'local':
    case 'development':
        do_nothing();
        break;
    
    case 'staging':
        do_staging_thing();
        break;
    
    case 'production':
    default:
        do_production_thing();
        break;
}

Of course care should be taken with this kind of functionality to test all the possible routes.

Setting the environment type

There are two ways to set the environment type for a site. They are processed in the following order with each sequential option overriding any previous values: the WP_ENVIRONMENT_TYPE PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher environment variable, and the WP_ENVIRONMENT_TYPE constant.

For both of them, if the environment value is not in the list of allowed environments, the default production value will be returned.

The simplest way is probably through defining the constant:

define( 'WP_ENVIRONMENT_TYPE', 'staging' );

Note: When development is returned by wp_get_environment_type(), WP_DEBUG will be set to true if it is not defined in the site’s wp-config.php file.

Request to all hosts and development environments

All hosts that support setting up staging environments are requested to set this feature to staging on those staging environments. Similarly we ask all developers with development environments to set this value to development appropriately.

Plugin and theme developers should also add support for this feature and adjust functionality appropriately based on the specified environment.


For reference, see the related ticketticket Created for both bug reports and feature development on the bug tracker. on TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.: #33161

Props @desrosj, @audrasjb, and @sergeybiryukov for reviewing.

#5-5, #dev-notes

Codebase language improvements in 5.5

Edit: 7/23/2020: A note was added to make developers aware that one of the option keys renamed prior to betaBeta A pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. 1 was renamed again after beta 3 to avoid confusion with the blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor. – @desrosj

In version 5.5, WordPress will see several changes aimed at maintaining a welcoming environment for all contributors while improving the clarity and inclusivity of the codebase. This is a separate but related effort to a recent proposal to update the default branches for all of the project’s GIT repositories.

The WordPress open sourceOpen Source Open Source denotes software for which the original source code is made freely available and may be redistributed and modified. Open Source **must be** delivered via a licensing model, see GPL. community cares about diversity. We strive to maintain a welcoming environment where everyone can feel included, by keeping communication free of discrimination, incitement to violence, promotion of hate, and unwelcoming behavior.

WordPress Etiquette

“Whitelist” and “Blacklist”

Whitelist and blacklist are terms that specify allowed and disallowed or blocked things. However, many times when these two words are used, what is being allowed or blocked is not clear. This ambiguity can make for poor readability, and can also make it harder for non-native English speakers to understand the purpose of the variable, or how to properly translate a string.

In WordPress 5.5, these words have been removed from CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. and only remain where absolutely necessary for backwards compatibility. A large percentage of the occurrences existed within inline documentation and the names of locally scoped variable, but a few instances were found in function names, option keys, action/filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same., and global variables.

While most of the changes made do not require any action to be taken, a few may need some small adjustments to plugins, themes, or custom code.

Here is what you need to know!

Renamed Options

The following options have been renamed:

  • blacklist_keys has been renamed to disallowed_keys.
  • comment_whitelist has been renamed to comment_previously_approved.

Upon upgrading to WordPress 5.5, these options will be renamed as part of the normal upgrade process and no action is required.

However, when using the Options APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. (add_option(), delete_option(), update_option(), get_option()), specifying one of the deprecated options will trigger a deprecated warning but the value for the new option name will be correctly returned.

Example

These two examples will return the same values.

$option_value = get_option( 'disallowed_keys' );

// This produces the same result, but will also trigger a deprecated warning.
$option_value = get_option( 'blacklist_keys' );

Any Options API function calls should be adjusted to look for the new option name whenever possible. For code that still needs to support support WordPress < 5.5, the following snippet will do the trick:

if ( false === get_option( 'disallowed_keys' ) ) {
    // Assume this is WP < 5.5. Option does not exist.
} else {
    // Assume this is WP >= 5.5
}

If disallowed_keys exists in the database, the stored value will be returned (which is an empty string by default, or a space separated list of keys and/or IP addresses). get_option() will return explicit false by default, which is a good indicator that the option does not exist.

There are a few things to be aware of, though.

  • Options API related filters associated with deprecated option keys will not be triggered. For example, the pre_option_blacklist_keys filter. Some basic text searches of the pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party directory found that the usage of any Options API hooks specific to the two changed option keys was almost none.
  • Direct Cache API calls for these options will not be reliable. For example, wp_cache_get( 'blacklist_keys', 'options ). Instead of accessing the option directly from the Cache API, get_option() should be used instead.
  • Queries directly modifying, updating, retrieving these options form the database will potentially cause issues. This is incorrect usage and the Options API functions should always be used instead.
  • Prior to 5.5 beta 1, blacklist_keys was renamed to blocklist_keys. However, to avoid any potential confusion with block editor related features or functionality, it was renamed to disallowed_keys after 5.5 beta 3. This will be fixed automatically when updating to the next development version or installing 5.5, but any code that was changed to use blocklist_keys will need to be updated once more.

In 5.6, adding a more proper _deprecate_option() will be explored to handle future occurrences like this.

For more information, see #50413 on TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress..

Deprecated functions

The following functions have been deprecated in 5.5. Though this should not cause any problems in your code, the following functions will now trigger a _deprecated_function() warning:

  • add_option_whitelist() has been replaced with add_allowed_options().
  • remove_option_whitelist() has been replaced with remove_allowed_options().
  • wp_blacklist_check() has been replaced with wp_check_comment_disallowed_list().
  • _wp_register_meta_args_whitelist() has been replaced with _wp_register_meta_args_allowed_list() (this is a private function, so should not be used outside of Core).
  • wp.passwordStrength.userInputBlacklist() was replaced with wp.passwordStrength.userInputDisallowedList() (JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/.)

For more information, see #50413 on Trac.

Deprecated actions and filters

The following action and filter hooks have been deprecated in 5.5. Any code hooked to them will still be executed. However, a deprecated warning will now be triggered. The replacement hooks will run after the deprecated hooks.

  • The whitelist_options filter hook has been replaced with allowed_options.
  • The wp_blacklist_check action hook has been replaced with wp_check_comment_disallowed_list.

For more information, see #50413 on Trac.

Deprecated global Variables

  • $whitelist_options has been replaced with $allowed_options.
  • $new_whitelist_options has been replaced with $new_allowed_options (more information below)

A scan of the plugin directory showed that only 2 plugins are explicitly accessing the $whitelist_options global and performing no modifications.

A second scan of the plugin directory showed a small number of plugins with a low amount of active installs potentially manipulating the value of $new_whitelist_options. However, it was enough to require consideration.

Because of this, the new $new_allowed_options variable is now passed by reference to the old $new_whitelist_options variable.

Unless a plugin is intentionally destroying this global variable and recreating it from scratch, this will create a seamless transition to a more inclusive name until a standardized way to deprecate a global variable can be created (such as _deprecate_global()), or the implementation can be revised to not require a global variable at all.

For more information on this change, see #50434 and #50413 on Trac.

“Webmaster”

In addition to potentially having racist undertones, the word “webmaster” has become dated and quite cryptic. One person managing all aspects of a site is, for the most part, no longer the norm. The term has also become ambiguous, potentially referring to anyone fulfilling any of the following duties involved with managing a website: web architects, web developers, site authors, website administrators, website owners, etc..

The two occurrences of this string have been replaced with alternatives that better communicate who is being referenced.

See #50394 on Trac for more information.

Props @cbringmann and @jorbin for pre-publishing review.

#5-5, #dev-notes

New XML Sitemaps Functionality in WordPress 5.5

In WordPress 5.5, a new feature is being introduced that adds basic, extensibleExtensible This is the ability to add additional functionality to the code. Plugins extend the WordPress core software. XML sitemaps functionality into WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress..

While web crawlers are able to discover pages from links within the site and from other sites, sitemaps supplement this approach by allowing crawlers to quickly and comprehensively identify all URLs included in the sitemap and learn other signals about those URLs using the associated metadata.

For more background information on this new feature, check out the merge announcement, or the corresponding TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #50117.

This article explains in detail the various ways in which this new feature can be customized by developers. For example, if you are developing a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party with some similar functionality, this post will show you how you can integrate it with the core’s new sitemaps feature.

Key Takeways

With version 5.5., WordPress will expose a sitemap index at /wp-sitemap.xml. This is the main XML file that contains the listing of all the sitemap pages exposed by a WordPress site.

The sitemap index can hold a maximum of 50000 sitemaps, and a single sitemap can hold a (filterable) maximum of 2000 entries.

By default, sitemaps are created for all public and publicly queryable post types and taxonomies, as well as for author archives and of course the homepage of the site.

The robots.txt file exposed by WordPress will reference the sitemap index so that i can be easily discovered by search engines.

Technical Requirements

Rendering sitemaps on the frontend requires the SimpleXML PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher extension. If this extension is not available, an error message will be displayed instead of the sitemap. The HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. status code 501 (“Not implemented”) will be sent accordingly.

Configuring Sitemaps Behavior

Adding Custom Sitemaps

WordPress provides sitemaps for built-in content types like pages and author archives out of the box. If you are developing a plugin that adds custom features beyond those standard ones, or just want to include some custom URLs on your site, it might make sense to add a custom sitemap provider.

To do so, all you need to do is create a custom PHP class that extends the abstract WP_Sitemaps_Provider class in core. Then, you can use the wp_register_sitemap_provider() function to register it. Here’s an example:

add_filter(
	'init',
	function() {
		$provider = new Awesome_Plugin_Sitemaps_Provider();
		wp_register_sitemap_provider( 'awesome-plugin', $provider );
	}
);

The provider will be responsible for getting all sitemaps and sitemap entries, as well as determining pagination.

Removing Certain Sitemaps

There are three existing sitemaps providers for WordPress object types like posts, taxonomies, and users. If you want to remove one of them, let’s say the “users” provider, you can leverage the wp_sitemaps_add_provider filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. to do so. Here’s an example:

add_filter(
	'wp_sitemaps_add_provider',
	function( $provider, $name ) {
		if ( 'users' === $name ) {
			return false;
		}

		return $provider;
	},
	10,
	2
);

If instead you want to disable sitemap generation for a specific post type or taxonomyTaxonomy A taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies., use the wp_sitemaps_post_types or wp_sitemaps_taxonomies filter, respectively.

Example: Disabling sitemaps for the page post type

add_filter(
	'wp_sitemaps_post_types',
	function( $post_types ) {
		unset( $post_types['page'] );
		return $post_types;
	}
);

Example: Disabling sitemaps for the post_tag taxonomy

add_filter(
	'wp_sitemaps_taxonomies',
	function( $taxonomies ) {
		unset( $taxonomies['post_tag'] );
		return $taxonomies;
	}
);

Adding Additional Tags to Sitemap Entries

The sitemaps protocol specifies a certain set of supported attributes for sitemap entries. Of those, only the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org (loc) tagtag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.) is required. All others (e.g. changefreq and priority) are optional tags in the sitemaps protocol and not typically consumed by search engines, which is why WordPress only lists the URL itself. Developers can still add those tags if they really want to.

You can use the wp_sitemaps_posts_entry / wp_sitemaps_users_entry / wp_sitemaps_taxonomies_entry filters to add additional tags like changefreq, priority, or lastmod to single items in the sitemap.

Example: Adding the last modified date for posts

add_filter(
    'wp_sitemaps_posts_entry',
    function( $entry, $post ) {
        $entry['lastmod'] = gmdate( DATE_W3C, strtotime( $post->post_modified_gmt );
        return $entry;
    },
    10,
    2
);

Similarly, you can use the wp_sitemaps_index_entry filter to add lastmod on the sitemap index. Note: the sitemaps protocal does not support on the sitemap index.

Trying to add any unsupported tags will result in a _doing_it_wrong notice.

Excluding a Single Post from the Sitemap

If you are developing a plugin that allows setting specific posts or pages to noindex, it’s a good idea to exclude those from the sitemap too.

The wp_sitemaps_posts_query_args filter can be used to exclude specific posts from the sitemap. Here’s an example:

add_filter(
	'wp_sitemaps_posts_query_args',
	function( $args, $post_type ) {
		if ( 'post' !== $post_type ) {
			return $args;
		}

		$args['post__not_in'] = isset( $args['post__not_in'] ) ? $args['post__not_in'] : array();
		$args['post__not_in'][] = 123; // 123 is the ID of the post to exclude.
		return $args;
	},
	10,
	2
);

Disabling Sitemaps Functionality Completely

If you update the Site Visibility settings in WordPress adminadmin (and super admin) to discourage search engines from indexing your site, sitemaps will be disabled. You can use the wp_sitemaps_enabled filter to override the default behavior.

Here’s an example of how to disable sitemaps completely, no matter what:

add_filter( 'wp_sitemaps_enabled', '__return_false' );

Note: Doing that will not remove the rewrite rules used for the sitemaps, as they are needed in order to send appropriate responses when sitemaps are disabled.

Want to know whether sitemaps are currently enabled or not? Use wp_sitemaps_get_server()->sitemaps_enabled().

Image/Video/News Sitemaps

WordPress currently implements and supports the core sitemaps format as defined on sitemaps.org. Sitemap extensions like image, video, and news sitemaps are not covered by this feature, as these are usually only useful for a small number of websites. In future versions of WordPress, filters and hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. may be added to enable adding such functionality. For now this will still be left to plugins to implement.

New Classes and Functions

As of this writing, this is the full list of new classes and functions introduced with this feature.

Functions:

  • wp_sitemaps_get_server – Retrieves the current Sitemaps server instance.
  • wp_get_sitemap_providers – Gets an array of sitemap providers.
  • wp_register_sitemap_provider – Registers a new sitemap provider.
  • wp_sitemaps_get_max_urls – Gets the maximum number of URLs for a sitemap.

Classes:

  • WP_Sitemaps – Main class responsible for setting up rewrites and registering all providers.
  • WP_Sitemaps_Index – Builds the sitemap index page that lists the links to all of the sitemaps.
  • WP_Sitemaps_Provider – Base class for other sitemap providers to extend and contains shared functionality.
  • WP_Sitemaps_Registry – Handles registering sitemap providers.
  • WP_Sitemaps_Renderer – Responsible for rendering Sitemaps data to XML in accordance with sitemap protocol.
  • WP_Sitemaps_Stylesheet – This class provides the XSL stylesheets to style all sitemaps.
  • WP_Sitemaps_Posts – Builds the sitemaps for the ‘post’ object type and its sub types (custom post types).
  • WP_Sitemaps_Taxonomies – Builds the sitemaps for the ‘taxonomy’ object type and its sub types (custom taxonomies).
  • WP_Sitemaps_Users – Builds the sitemaps for the ‘user’ object type.

Available Hooks and Filters

As of this writing, this is the full list of available hooks and filters.

General:

  • wp_sitemaps_enabled – Filters whether XML Sitemaps are enabled or not.
  • wp_sitemaps_max_urls – Filters the maximum number of URLs displayed on a sitemap.
  • wp_sitemaps_init – Fires when initializing sitemaps.
  • wp_sitemaps_index_entry – Filters the sitemap entry for the sitemap index.

Providers:

  • wp_sitemaps_add_provider – Filters the sitemap provider before it is added.
  • wp_sitemaps_post_types – Filters the list of post types to include in the sitemaps.
  • wp_sitemaps_posts_entry – Filters the sitemap entry for an individual post.
  • wp_sitemaps_posts_show_on_front_entry – Filters the sitemap entry for the home page when the ‘show_on_front’ option equals ‘posts’.
  • wp_sitemaps_posts_query_args – Filters the query arguments for post type sitemap queries.
  • wp_sitemaps_posts_pre_url_list – Filters the posts URL list before it is generated (short-circuit).
  • wp_sitemaps_posts_pre_max_num_pages – Filters the max number of pages before it is generated (short-circuit).
  • wp_sitemaps_taxonomies – Filters the list of taxonomies to include in the sitemaps.
  • wp_sitemaps_taxonomies_entry – Filters the sitemap entry for an individual term.
  • wp_sitemaps_taxonomies_query_args – Filters the query arguments for taxonomy terms sitemap queries.
  • wp_sitemaps_taxonomies_pre_url_list – Filters the taxonomies URL list before it is generated (short-circuit).
  • wp_sitemaps_taxonomies_pre_max_num_pages – Filters the max number of pages before it is generated (short-circuit).
  • wp_sitemaps_users_entry – Filters the sitemap entry for an individual user.
  • wp_sitemaps_users_query_args – Filters the query arguments for user sitemap queries.
  • wp_sitemaps_users_pre_url_list – Filters the users URL list before it is generated (short-circuit).
  • wp_sitemaps_users_pre_max_num_pages – Filters the max number of pages before it is generated (short-circuit).

Stylesheets:

  • wp_sitemaps_stylesheet_css – Filters the CSSCSS Cascading Style Sheets. for the sitemap stylesheet.
  • wp_sitemaps_stylesheet_url – Filters the URL for the sitemap stylesheet.
  • wp_sitemaps_stylesheet_content – Filters the content of the sitemap stylesheet.
  • wp_sitemaps_stylesheet_index_url – Filters the URL for the sitemap index stylesheet.
  • wp_sitemaps_stylesheet_index_content – Filters the content of the sitemap index stylesheet.

#5-5, #dev-notes, #sitemaps, #xml-sitemaps

REST API changes in WordPress 5.5

The REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. will see a lot of changes in WordPress 5.5. In an effort to explain each change adequately, a number of these were split out and covered by other dev notesdev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase..

Below are some other noteworthy changes that deserve a call out.

Discoverable REST resource links

To aid automated and human discovery of the REST API, a link was added in the <head> of the document and as a Link headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. to the REST route for the currently queried document in [48273].

For example, in the <head> of this post, the following <link> appears.

<link rel="alternate" type="application/json" href="https://make.wordpress.org/core/wp-json/wp/v2/posts/82428">

Links are added for post, pages, and other custom post types, as well as terms and author pages. Links are not currently output for post archives or search results.

See #49116 for more information.

APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways.

Three new functions are introduced.

  • rest_get_route_for_post() retrieves the route for the given post. For instance, /wp/v2/posts/1.
  • rest_get_route_for_term() retrieves the route for the given term. For instance, /wp/v2/tags/1.
  • rest_get_queried_resource_route() retrieves the route for the currently queried resource. For instance, /wp/v2/users/1 when on the author page for the user with ID of 1.

All three functions return an empty string if a REST API route cannot be determined. To convert the route to a link, pass the result to the rest_url() function.

For more information, see #49116.

Customization

For custom post types, only routes using the built-in WP_REST_Posts_Controller controller class will work by default. For custom controllers, the rest_route_for_post filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. can be used to supply the correct route.

function my_plugin_rest_route_for_post( $route, $post ) {
	if ( $post->post_type === 'my-cpt' ) {
		$route = '/wp/v2/my-cpt/' . $post->ID;
	}

	return $route;
}
add_filter( 'rest_route_for_post', 'my_plugin_rest_route_for_post', 10, 2 );

Similar logic applies to taxonomies, only the built in WP_REST_Terms_Controller is supported by default. The rest_route_for_term filter can be used for custom controller classes.

function my_plugin_rest_route_for_term( $route, $term ) {
	if ( $term->taxonomy === 'my-tax' ) {
		$route = '/wp/v2/my-tax/' . $term->term_id;
	}

	return $route;
}
add_filter( 'rest_route_for_term', 'my_plugin_rest_route_for_term', 10, 2 );

The rest_get_queried_resource_route() function is filterable to allow for identification of custom resources.

function my_plugin_rest_queried_resource_route( $route ) {
	$id = get_query_var( 'my-route' );
	if ( ! $route && $id ) {
		$route = '/my-ns/v1/items/' . $id;
	}

	return $route;
}
add_filter( 'rest_queried_resource_route', 'my_plugin_rest_queried_resource_route' );

These links are output using the existing rest_output_link_wp_head and rest_output_link_header functions. As such, if API discovery has already been disabled, these links will not be rendered.

For more information, see #49116.

CORS changes

In [48112], the Link header was added to the list of exposed CORS response headers. Previously, only the X-WP-Total and X-WP-TotalPages headers were exposed which means that clients would have to manually construct the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org to implement pagination instead of using the prev and next Links.

In [48452], the Content-Disposition, Content-MD5 and X-WP-Nonce were added to the list of allowed cors request headers. The Content-Disposition and Content-MD5 headers allow for easier file uploading across domains by using a File/Blob object directly. The X-WP-Nonce header is allowed for making authenticated cross-origin and same-origin requests consistently.

Two filters are introduced, rest_exposed_cors_headers and rest_allowed_cors_headers to simplify the process of plugins modifying the list of cors headers.

For more information, see #50369 and #41696.

Miscellaneous

Warn when omitting a permission_callback

The REST API treats routes without a permission_callback as public. Because this happens without any warning to the user, if the permission callback is unintentionally omitted or misspelled, the endpoint can end up being available to the public. Such a scenario has happened multiple times in the wild, and the results can be catastrophic when it occurs.

In [48526] a _doing_it_wrong notice has been added when a permission callback is omitted. For REST API routes that are intended to be public, it is recommended to set the permission callback to the __return_true built in function.

So for instance, this route registration will cause the following warning to appear.

register_rest_route(
	'my-ns',
	'echo',
	array(
		'methods'  => WP_REST_Server::EDITABLE,
		'callback' => function ( WP_REST_Request $request ) {
			return new WP_REST_Response( $request->get_param( 'echo' ) );
		},
	)
);
The REST API route definition for my-ns/echo is missing the required permission_callback argument. For REST API routes that are intended to be public, use __return_true as the permission callback.

If it was intended for this endpoint to be public, you could fix it like this.

register_rest_route(
	'my-ns',
	'echo',
	array(
		'methods'  => WP_REST_Server::EDITABLE,
		'callback' => function ( WP_REST_Request $request ) {
			return new WP_REST_Response( $request->get_param( 'echo' ) );
		},
		'permission_callback' => '__return_true',
	)
);

If you wanted to check the user’s capabilities, you could do it like this.

register_rest_route(
	'my-ns',
	'echo',
	array(
		'methods'  => WP_REST_Server::EDITABLE,
		'callback' => function ( WP_REST_Request $request ) {
			return new WP_REST_Response( $request->get_param( 'echo' ) );
		},
		'permission_callback' => function( WP_REST_Request $request ) {
			return current_user_can( 'manage_options' );
		},
	)
);

For more information, see #50075.

Using wp_send_json() is doing it wrong

When building a REST API route, it is important for the route callback (and permission_callback) to return data instead of directly sending it to the browser. This ensures that the additional processing that the REST API server does, like handling linking/embedding, sending headers, etc… takes place.

// This is incorrect.
echo wp_json_encode( $data );
die;

// And this.
wp_send_json( $data );

// And this.
wp_send_json_error( 'My Error' );

// Instead do this.
return new WP_REST_Response( $data );

// Or this.
return new WP_Error( 'my_error_code', 'My Error Message' );

After [48361], a _doing_it_wrong notice is now issued when any of the wp_send_json family of functions are used during a REST API request.

For more information, see #36271.

Preloading silenced deprecation errors

When using rest_preload_api_request any deprecation errors encountered when processing the request were silenced because the REST API handling took over as soon as the API server was booted.

After [48150], the REST API handling will only apply for actual REST API requests.

For more information, see #50318.

Props @desrosj and @justinahinon for proofreading.

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

New esc_xml() function in WordPress 5.5

As part of the development for the new XML Sitemaps feature in WordPress 5.5, a new esc_xml() function has been added to coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. that filters a string cleaned and escaped for output in XML. This joins the existing set of functions like esc_html() and esc_js().

While all contents in XML sitemaps are already escaped using this new function, existing code in WordPress core can be updated to leverage it in future releases.

wp_kses_normalize_entities() has been updated accordingly to support this, and now can distinguish between HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. and XML context.

Note: l10nL10n Localization, or the act of translating code into one's own language. Also see internationalization. Often written with an uppercase L so it is not confused with the capital letter i or the numeral 1. WordPress has a capable and dynamic group of polyglots who take WordPress to more than 70 different locales. helpers like esc_xml__() and esc_xml_e() are being proposed separately in #50551, and are not part of this release.

#5-5, #dev-notes, #sitemaps, #xml-sitemaps

Editing Images in the Block Editor

As most people that follow coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. development already know, a new feature in WordPress 5.5 is editing of images right in the block editor.

The new image editor looks a lot better and is much easier to use. It comes with several presets allowing the users to quickly adjust the aspect ratio, zoom level, and position.

Editing an image in the image block, aspect ration drop-down open.

There are two notable changes:

  • Image editing is done through the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/.. The new functionality is in WP_REST_Attachments_Controller and introduces wp_edited_image_metadata filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.. Of course all other actions and filters triggered when image sub-sizes are created and saved still work.
  • After editing, the image is saved separately as a new attachment. This lets the users have full control: see the edited image in the Media Library, edit either the original or the edited image again, or delete any of them if not used. (In the old image editor the edited images are saved in the image metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. reusing the same attachment, and the users cannot access the original “parent” image).

When creating new attachments for edited images the post_title, post_content (image description), post_excerpt (caption stored in the database), and the alt text (stored in post meta in the database) are copied (if present) from the parent image to the edited image. Also the EXIF data stored in image meta is merged.

For plugins, the new wp_edited_image_metadata filter can also be used to migrate any other meta data a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party may be adding to image attachments, or to save new data.

#5-5, #dev-notes

Introducing createInterpolateElement

With the release of WordPress 5.5 there will be a new function available on the wp.element package, createInterpolateElement.

This function creates an interpolated element from a passed in string with specific tags matching how the string should be converted to an element or ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/. node given a conversion map value.

This function offers the ability to handle a couple use-cases:

  • When you have a template string with placeholders for where you inject known htmlHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. elements or React components.
  • When you have a translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. string where you want to communicate placeholders for html elements to those translating the string.

One problem with the above use-cases in the past has been preserving the security of html string rendering within react without using RawHTML or dangerouslySetInnerHTML.

Of course the second use-case above is the primary one for which this function was created. As an example, for the given string:

"This is a <span class="special-text">string</span> with a <a href="https://make.wordpress.org">link</a> and a self-closing <CustomComponent />."

You could make the above translatable via doing something like this:

import { __ } from '@wordpress/i18n';
import { createInterpolateElement } from '@wordpress/element';
import { CustomComponent } from '../custom-component.js';

const translatedString = createInterpolateElement(
  __( 'This is a <span>string</span> with a <a>link</a> and a self-closing <custom_component/>.' ),
  {
    span: <span className="special-text" />,
    a: <a href="https://make.wordpress.org"/>,
    custom_component: <CustomComponent />,
  }
);

#5-5, #core-js, #dev-notes

Passing arguments to template files in WordPress 5.5

For years, theme developers wishing to pass data to template files have had to use less than ideal workarounds. This included the use of global variables, set_query_var(), the include( locate_template() ) pattern, or own version of get_template_part(), and so on.

Starting in WordPress 5.5, the template loading functions will now allow additional arguments to be passed through to the matched template file using a new $args parameter.

Affected functions

  1. get_header()
  2. get_footer()
  3. get_sidebar()
  4. get_template_part()
  5. locate_template()
  6. load_template()

To provide proper context, the related action hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. have also been updated to pass this new $args parameter.

  • get_header
  • get_footer
  • get_sidebar
  • get_template_part_{$slug}
  • get_template_part

Note: get_search_form() already accepts and passes additional arguments to search form templates as of [44956]. However, the $args parameter was added to the related hooks at the same time as the ones above. These are:

  • pre_get_search_form (action)
  • search_form_format (filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.)
  • get_search_form (filter)

Example

<?php
get_template_part( 
    'foo', 
    null, 
    array( 
        'class'          => 'user',
        'arbitrary_data' => array(
            'foo' => 'baz',
            'bar' => true,
        ),
        ...
    )
);

In the above example, the additional data passed to get_template_part() through the $args variable can be accessed within the foo.php template through the locally scoped $args variable.

<?php
// Example foo.php template.

// Set defaults.
$args = wp_parse_args(
    $args,
    array(
        'class'          => '',
        'arbitrary_data' => array(
            'foo' => 'fooval',
            'bar' => false,
        ),
        ...
    )
);
?>

<div class="widget <?php echo esc_html_class( $args['class'] ); ?>">
    <?php echo esc_html( $args['arbitrary_data']['foo'] ); ?>
</div>

Note: Any template file that currently contains an $args variable should take care when utilizing this new feature. Any modifications to $args in the loaded template files will override any values passed through using the above functions.

This enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. ticketticket Created for both bug reports and feature development on the bug tracker. was created 8 years ago, and is a long awaited addition. Thank you to all that helped discuss and refine this change.

For reference, see the related TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticket: #21676.

Props @audrasjb and @desrosj for technical review and proofreading.

#5-5, #dev-notes

New and modified REST API endpoints in WordPress 5.5

In WordPress 5.5, a handful of entirely new REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. endpoints have been introduced and several others will see new features or enhancements. Let’s look at a breakdown of these changes.

BlockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. Types

In [48173] REST API endpoints were introduced to return all of the block types registered on the server.

  • GET /wp/v2/block-types will return all registered block types.
  • GET /wp/v2/block-types/core will return all blocks within the core namespace.
  • GET /wp/v2/block-types/core/quote will return the definition specifically for the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. quote block.

This endpoint is accessible to users that have edit permission for any post type that is included in the REST API. In other words, if the user can edit posts in the Block Editor, they can access the block types endpoint.

Response Format

The response format for this endpoint closely follows the Block Type Registration RFC. The JSON Schema documents the full response type. The following is an example of that in practice for the core/quote block.

{
  "attributes": {
    "value": {
      "type": "string",
      "source": "html",
      "selector": "blockquote",
      "multiline": "p",
      "default": ""
    },
    "citation": {
      "type": "string",
      "source": "html",
      "selector": "cite",
      "default": ""
    },
    "align": {
      "type": "string"
    }
  },
  "is_dynamic": false,
  "name": "core/quote",
  "title": "",
  "description": "",
  "icon": null,
  "category": "text",
  "keywords": [],
  "parent": null,
  "provides_context": [],
  "uses_context": [],
  "supports": {
    "anchor": true
  },
  "styles": [],
  "textdomain": null,
  "example": null,
  "editor_script": null,
  "script": null,
  "editor_style": null,
  "style": null
}

For more information, refer to the relevant ticketticket Created for both bug reports and feature development on the bug tracker. on TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. (#48173).

Plugins

In [48242] REST API endpoints were introduced for managing plugins. These endpoints facilitate the Block Directory Inserter feature in the block editor.

  • GET /wp/v2/plugins will return a list of all plugins installed on a site.
  • GET /wp/v2/plugins/akismet/akismet will return information about the installed Akismet pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. The first akismet refers to the plugin’s folder, the second akismet is the main plugin file without the .php file extension.
  • POST /wp/v2/plugins { slug: "akismet" } installs the plugin with the slug aksimet from the WordPress.orgWordPress.org The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/ plugin directory. The endpoint does not support uploading a plugin zip.
  • PUT /wp/v2/plugins/akismet/akismet { status: "active" } activates the selected plugin. The status can be set to network-active to networknetwork (versus site, blog) activate the plugin on Multisitemultisite Used to describe a WordPress installation with a network of multiple blogs, grouped by sites. This installation type has shared users tables, and creates separate database tables for each blog (wp_posts becomes wp_0_posts). See also network, blog, site. To deactivate the plugin set the status to inactive. There is not a separate network-inactive status, inactive will perform a network deactivation if the plugin was network activated.
  • DELETE /wp/v2/plugins/akismet/akismet uninstalls the selected plugin. The plugin must be inactive before deleting it.

The endpoint is accessible to authenticated users with the activate_plugins capability. Specific actions have their own additional permission checks. For instance, the install_plugins capability is required to install new plugins.

Response Format

The JSON Schema for the endpoint documents the full response type. The following is an example for the Akismet plugin.

{
  "plugin": "akismet/akismet",
  "status": "inactive",
  "name": "Akismet Anti-Spam",
  "plugin_uri": "https://akismet.com/",
  "author": "Automattic",
  "author_uri": "https://automattic.com/wordpress-plugins/",
  "description": {
    "raw": "Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. It keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key.",
    "rendered": "Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. It keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key. <cite>By <a href=\"https://automattic.com/wordpress-plugins/\">Automattic</a>.</cite>"
  },
  "version": "4.1.6",
  "network_only": false,
  "requires_wp": "",
  "requires_php": "",
  "text_domain": "akismet"
}

For more information, refer to the relevant ticket on Trac (#50321).

Block Directory

The [48242] changeset also introduced an endpoint for searching the WordPress.org block directory.

GET /wp/v2/block-directory/search?term=starscape searches for blocks that match the term starscape.

This endpoint requires both the activate_plugins and install_plugins capabilities.

Response Format

The JSON Schema for the endpoint documents the full response type. The following is an example for the Starscape block.

{
  "name": "a8c/starscape",
  "title": "Starscape Block",
  "description": "Everything was made of collapsing stars, we are all made of star stuff. Now we also can create content in WordPress with stars in motion. Requirements As this is part...",
  "id": "starscape",
  "rating": 0,
  "rating_count": 0,
  "active_installs": 10,
  "author_block_rating": 0,
  "author_block_count": 1,
  "author": "Automattic",
  "icon": "https://ps.w.org/starscape/assets/icon.svg?rev=2232475",
  "assets": [
    "https://ps.w.org/starscape/tags/1.0.2/index.js?v=1591313160",
    "https://ps.w.org/starscape/tags/1.0.2/editor.css?v=1591313160",
    "https://ps.w.org/starscape/tags/1.0.2/style.css?v=1591313160"
  ],
  "last_updated": "2020-06-04T23:26:00",
  "humanized_updated": "1 month ago",
  "_links": {
    "wp:install-plugin": [
      {
        "href": "http://trunk.test/wp-json/wp/v2/plugins?slug=starscape"
      }
    ]
  }
}

For more information, refer to the relevant ticket on Trac (#50321).

Image Editing

In [48291] an endpoint was introduced for editing image attachments in the media library. This facilitates the inline image editing feature in the block editor.

POST /wp/v2/media/5/edit edits the image with id 5. This rotates the image 90 degrees, and then zooms the image to 80% of its original height and width into the top-left corner.

{
  "x": 0,
  "y": 0,
  "width": 80,
  "height": 80,
  "rotate": 90
}

Notes: width and height only supports percentage values. x & y specify the crop starting point.

This endpoint requires permission to edit the original attachment as well as the upload_files capability. The endpoint returns the newly created attachment.

For more information, refer to the relevant ticket on Trac (#44405).

Block Renderer

Every block has a different set of attributes. These attributes are specified as a JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema object. Previously, every block registered its own block renderer route using its attributes for the schema. This allowed for the attributes to be validated using the built in endpoint validation rules.

However, this approach had the unfortunate side effect of creating a large number of nearly identical REST API routes (one for each dynamic block). Each registered route has a performance impact. As the number of server side blocks goes up, this becomes more and more of an issue.

In [48069], the block renderer route was changed to register a single block renderer route and dynamically validate the attributes based on the selected block. This is an internal only change, consumers of the block renderer endpoint shouldn’t notice any changes. The same request format continues to work.

However, this means the attributes schema for a block can no longer be fetched by making an OPTIONS request to the block renderer endpoint. The new block types endpoint should be used instead.

In [47756] support for the POST request method was added to the block renderer. This allows a block to be rendered that has attributes larger than would be allowed in the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org.

For more information, refer to the tickets on Trac (#49680 and #48079).

Themes

In [47921], support for returning the majority of a theme’s headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. information from its public style.css file was added to the /wp/v2/themes endpoint. Previously, only the theme’s supported features were returned.

For example, Twenty Twenty now produces the following response:

{
  "stylesheet": "twentytwenty",
  "template": "twentytwenty",
  "requires_php": "5.2.4",
  "requires_wp": "4.7",
  "textdomain": "twentytwenty",
  "version": "1.4",
  "screenshot": "http://trunk.test/wp-content/themes/twentytwenty/screenshot.png",
  "author": {
    "raw": "the WordPress team",
    "rendered": "<a href=\"https://wordpress.org/\">the WordPress team</a>"
  },
  "author_uri": {
    "raw": "https://wordpress.org/",
    "rendered": "https://wordpress.org/"
  },
  "description": {
    "raw": "Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor. ",
    "rendered": "Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor."
  },
  "name": {
    "raw": "Twenty Twenty",
    "rendered": "Twenty Twenty"
  },
  "tags": {
    "raw": [
      "blog",
      "one-column",
      "custom-background",
      "custom-colors",
      "custom-logo",
      "custom-menu"
    ],
    "rendered": "blog, one-column, custom-background, custom-colors, custom-logo, custom-menu"
  },
  "theme_uri": {
    "raw": "https://wordpress.org/themes/twentytwenty/",
    "rendered": "https://wordpress.org/themes/twentytwenty/"
  },
  "theme_supports": {}
}

Currently, themes can declare support for a given feature by using add_theme_support(). In [48171], the register_theme_feature APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. was introduced that allows WordPress Core and plugins to declare a list of available features that themes can support.

The REST API uses this to expose a theme’s supported features if the feature has been registered with show_in_rest set to true.

As a reminder, the themes endpoint is available to any users who can edit a post type that is visible in the REST API. In other words, if a user can use the Block Editor, they can access this information.

For more information, refer to the relevant tickets on Trac (#49906 and #49406).

Props @desrosj and @justinahinon for reviewing.

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

REST API Parameter & JSON Schema changes in WordPress 5.5

WordPress 5.5 introduces a number of new features and changes to how request and response parameters are handled, particularly around JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema features.

General

Support more JSON Schemas when filtering a response by context

The REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. uses context to remove fields from a response if the user does not have adequate permissions or for performance reasons. For example, in the following schema, if the user requested the resource with context=view, only the name properties would be returned.

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "context": [ "view", "edit" ]
    },
    "role": {
      "type": "string",
      "context": [ "edit" ]
    }
  }
}

In [47758], support was added for removing more properties from a response if additional features were in use. The array type, multi-types, and the additionalProperties keyword are now supported. Additionally, the filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. recurses to an infinite depth.

So for the following schema, if the resource was requested with context=view the following fields would be returned.

{
  "type": "object",
  "properties": {
    "notes": {
      "context": [ "view", "edit" ],
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "note": {
            "context": [ "view", "edit" ],
            "type": "string"
          },
          "ip": {
            "context": [ "edit" ],
            "type": "string"
          }
        }
      }
    }
  }
}

Note that the ip field has been omitted.

{
  "notes": [
    {
      "note": "My Note"
    }
  ]
}

For more information, see #48819 on TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress..

Inconsistent parameter type handling in WP_REST_Request::set_param()

Previously, when parameters were manually added to a WP_REST_Request object using set_param(), the parameter was added into the first available parameter slot. This could be unexpected if a parameter was already in another parameter slot and you were trying to overwrite the value.

In [47559], this changed to first look for an existing parameter, and then falls back to the first parameter in the order if none was found.

For more information, refer to the relevant Trac ticketticket Created for both bug reports and feature development on the bug tracker. (#40838).

WP_REST_Controller::get_endpoint_args_for_item_schema drops some keywords

The primary mechanism for developers to declare the parameters an endpoint accepts is by writing a JSON Schema in WP_REST_Controller::get_item_schema(). The schema is then transformed into the args format by using WP_REST_Controller::get_endpoint_args_for_item_schema() during route registration. This transformation process dropped the minimum, maximum, exclusiveMinimum, and exclusiveMaximum JSON Schema keywords. This list of allowed keywords was updated in [47911] to ensure this doesn’t happen.

For more information, check out #50301 on Trac.

Check required properties are provided when validating an object

Previously, the REST API would validate that all parameters with the required attribute were provided in WP_REST_Request::has_valid_params. This meant that if an object was defined with its own set of required parameters, it was not checked by the default parameter validator.

So, for instance, given the following schema, the request would be accepted despite the missing version field.

{
  "type": "object",
  "properties": {
    "fixed_in": {
      "required": true,
      "type": "object",
      "properties": {
        "revision": {
          "required": true,
          "type": "integer"
        },
        "version": {
          "required": true,
          "type": "string"
        }
      }
    }
  }
}
{
  "fixed_in": {
    "revision": 47809
  }
}

In [47809] this was fixed to now generate a rest_property_required error.

Version 4 syntax

This change also adds support for JSON Schema Version 4 required-property syntax where the list of required properties for an object is defined as an array of property names. This can be particularly helpful when specifying that a metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. value has a list of required properties.

Given the following meta field.

register_post_meta( 'post', 'fixed_in', array(
	'type'         => 'object',
	'show_in_rest' => array(
		'single' => true,
		'schema' => array(
			'required'   => array( 'revision', 'version' ),
			'type'       => 'object',
			'properties' => array(
				'revision' => array(
					'type' => 'integer',
				),
				'version'  => array(
					'type' => 'string',
				),
			),
		),
	),
) );

And the following request object.

{
	"title": "Check required properties",
	"content": "We should check that required properties are provided",
	"meta": {
		"fixed_in": {
			"revision": 47089
		}
	}
}

The following error will be returned.

{
  "code": "rest_property_required",
  "message": "version is a required property of meta.fixed_in.",
  "data": {
    "status": 400
  }
}

If the fixed_in meta field was omitted entirely, no error would be generated. An object that defines a list of required properties does not indicate that the object itself is required to be submitted. Just that if the object is included, that the listed properties must also be included as well.

Specifying the required properties in the top-level schema for an endpoint using a required array is not supported. Given the following schema, a user could successfully submit a request without the title or content properties. This is because, as discussed earlier, the schema document is not itself used for validation, but instead transformed to a list of parameter definitions.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "my-endpoint",
  "type": "object",
  "required": [ "title", "content" ],
  "properties": {
    "title": {
      "type": "string"
    },
    "content": {
      "type": "string"
    }
  }
}

For more information, refer to #48818 on Trac.

Make multi-typed schemas more robust

A multi-type schema is a schema where the type keyword is an array of possible types instead of a single type. For instance, [ 'object', 'string' ] would allow objects or string values.

In [46249] basic support for these schemas was introduced. The validator would loopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop. over each schema type trying to find a version that matched. This worked for valid values, but for invalidinvalid A resolution on the bug tracker (and generally common in software development, sometimes also notabug) that indicates the ticket is not a bug, is a support request, or is generally invalid. values it provided unhelpful error messages. The sanitizer also had its utility restricted.

In [48306], the validators and sanitizers will now first determine the best type of the passed value and then apply the schema with that set type. In the case that a value could match multiple types, the schema of the first matching type will be used.

So, for instance, given the following schema, if the value 40 was submitted for the parameter, the user would receive param must be between 10 (inclusive) and 20 (inclusive) as an error message instead of param is not of type null,integer.

{
  "type": [ "null", "integer" ],
  "minimum": 10,
  "maximum": 20
}

Correct rest_sanitize_value_from_schema return type

The return type of rest_sanitize_value_from_schema has been corrected in [48307] to be mixed|WP_Error. Previously, the function had been documented as returning true|WP_Error.

Though WP_Error was a documented return type, it would not have been returned before WordPress 5.5. The function now returns a WP_Error if the value cannot be safely sanitized. For instance when checking the uniqueItems keyword.

New Keywords

WordPress 5.5 adds support for a number of JSON Schema keywords.

Pattern

The JSON Schema keyword pattern can be used to validate that a string field matches a regular expression.

For instance, given the following schema, #123 would be valid, but #abc would not.

{
  "type": "string",
  "pattern": "#[0-9]+"
}

The regex is not automatically anchored. Regex flags, for instance /i to make the match case insensitive are not supported. Developers are advised to use a constrained set of regex features so the schema can be interoperable between JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. and PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher. The pattern should be valid according to the ECMA 262 regex dialect.

Relevant changeset: [47810].

Min and max string length

The minLength and maxLength keywords can be used to constrain the acceptable length of a string. Importantly multi-byte characters are counted as a single character and bounds are inclusive.

For instance, given the following schema, ab, abc, and abcd are valid, while a, and abcde are invalid.

{
  "type": "string",
  "minLength": 2,
  "maxLength": 4
}

The exclusiveMinimum and exclusiveMaximum keywords do not apply, they are only valid for numbers.

Relevant changeset: [47627].

Min and max array items

The minItems and maxItems keywords can be used to constrain the acceptable number of items included in an array.

For instance, given the following schema, [ 'a' ] and [ 'a', 'b' ] are valid, while [] and [ 'a', 'b', 'c' ] are invalid.

{
  "type": "array",
  "minItems": 1,
  "maxItems": 2,
  "items": {
    "type": "string"
  }
}

Again, the exclusiveMinimum and exclusiveMaximum keywords do not apply.

Relevante changeset: [47923].

Unique items

The uniqueItems keyword can be used to require that all items in an array are unique.

For instance, given the following schema, [ 'a', 'b' ] is valid, while [ 'a', 'a' ] is not.

{
  "type": "array",
  "uniqueItems": true,
  "items": {
    "type": "string"
  }
}

Uniqueness

Items of different types are considered unique, for instance, '1', 1 and 1.0 are different values.

When arrays are compared, the order of items matters. So the given array is considered to have all unique items.

[
  [ "a", "b" ],
  [ "b", "a" ]
]

When objects are compared, the order the members appear in does not matter. So the given array is considered to have duplicate items since the values are the same, they just appear in a different order.

[
  { 
    "a": 1,
    "b": 2
  },
  {
    "b": 2,
    "a": 1
  }
]

Uniqueness is checked in both rest_validate_value_from_schema and rest_sanitize_value_from_schema. This is to prevent instances where items would be considered unique before sanitization is applied, but after sanitization the items would converge to identical values.

Take for instance the following schema:

{
  "type": "array",
  "uniqueItems": true,
  "items": {
    "type": "string",
    "format": "uri"
  }
}

A request with [ "https://example.org/hello world", "https://example.org/hello%20world" ] would pass validation because the each string value is different. However, after esc_url_raw converted the space in the first url to %20 the values would be identical.

In this case rest_sanitize_value_from_schema would return an error. As such, developers are advised to always validate and sanitize parameters.

For more information, see #48821 on Trac.

Format Changes

A couple of changes have been made to handling of the format keyword.

Only validate the format keyword if the type is a string

Previously, implementing multi-type fields that had a format keyword required disabling the default validate & sanitize callbacks. After [48300] this can now be expressed entirely in JSON Schema.

For instance, given the following schema, a user can now submit both https://example.org/hello world and { "link": "https://example.org/hello world" } and validation & sanitization will be properly applied in both cases.

{
  "type": [ "string", "object" ],
  "properties": {
    "link": {
      "type": "string",
      "format": "uri"
    },
    "label": {
      "type": "string"
    }
  },
  "format": "uri"
}

For backward compatibility with invalid schemas the format validation will still apply if the type is not specified, or it is invalid.

Miscellaneous changes

  • The hex-color format has been introduced. This enforces a 3 or 6 hex digit color with a leading (see #49270).
  • The uuid format has been introduced. This enforces a uuid of any version using the wp_is_uuid function (see #50053).
  • The rest_validate_value_from_schema and rest_sanitize_value_from_schema functions will now issue _doing_it_wrong notices if the type keyword is missing or contains invalid values. The following types are valid: object, array, string, number, integer, boolean, and null (see #48821).
  • The following functions have been introduced to encapsulate type validation & sanitization: rest_is_integer, rest_is_array, rest_sanitize_array, rest_is_object and rest_sanitize_object (see #48821).

Props @desrosj, @davidbaumwald, and @justinahinon for reviewing.

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