Configuring development mode in 6.3

WordPress 6.3 introduces a new concept called “development mode”, which affects certain nuances in how WordPress behaves. Going forward, sites will be able to configure their development mode using a new WP_DEVELOPMENT_MODE constant, which is recommended for any development sites.

What is the development mode?

The development mode configured on a site defines the kind of development work that the site is being used for. 

Possible values for WP_DEVELOPMENT_MODE are:

  • coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.” indicates that this site is used as a WordPress core development environment. For example, this may be relevant when you are contributing directly to WordPress core.
  • 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” indicates that this site is used as a WordPress plugin development environment. For example, this may be relevant when you are working on a plugin for the plugin repository.
  • “theme” indicates that this site is used as a WordPress theme development environment. For example, this may be relevant when you are working on a theme for the theme repository.
  • “all” indicates that this site is used as a WordPress development environment where all three aspects may be modified. For example, this may be relevant when you are working on a specific site as a whole, e.g. for a client.
  • An empty string indicates that no particular development mode is enabled for this site. This is the default value and should be used on any site that is not used for development.

Per this definition, setting a development mode is only relevant for sites where any kind of development is occurring. For example, it is not advised or relevant to use on a production siteProduction Site A production site is a live site online meant to be viewed by your visitors, as opposed to a site that is staged for development or testing..

What specifically does the development mode do?

There are currently only a few use-cases in WordPress core which are determined by the development mode, but this will likely increase in the future. Most usage today relates to theme.json caching.

It is also a great example for the kind of nuance that the development mode of a site affects:

  • On most sites, caching certain data from theme.json is reliable since that data would only be invalidated when the theme is updated.
  • However, if you are actively developing a theme on the site and modifying theme.json constantly, having to manually invalidate the cache all the time would be detrimental to the development workflow. Therefore, that specific caching functionality is bypassed if the development mode is set to “theme”.
  • On the flipside, if you are directly contributing to WordPress core (i.e. setting your development mode to “core”), your site should behave as close to the actual behavior as possible, so even though you are in a development environment as well, having the theme.json data cache bypassed would not represent the actual behavior intended for a regular WordPress site.
  • That is why that specific caching functionality is only bypassed during theme development, but not during core development.

Difference between development mode, environment type, and debug mode

WordPress already contains two seemingly related concepts, which are the environment type (WP_ENVIRONMENT_TYPE constant) and debug mode (WP_DEBUG constant). Here is how they differ:

  • WP_DEBUG (boolean) toggles general debugging mode, which results in additional notices being displayed or logged.
  • WP_ENVIRONMENT_TYPE (string) defines whether the site is a local, development, staging, or production environment. This value can be used to set defaults for other configuration parameters or toggle certain features on the site.
  • WP_DEVELOPMENT_MODE (string) defines a specific scope of development that applies to the current site, which results in certain low-level WordPress behavior to change. This is different from WP_DEBUG which does not affect actual behavior. Additionally, WP_DEBUG typically applies to any development environment, while WP_DEVELOPMENT_MODE is more specific, as explained in the aforementioned example.

It is likely that you will only use the WP_DEVELOPMENT_MODE constant on a site where WP_DEBUG is enabled and WP_ENVIRONMENT_TYPE is either “development” or “local”, since it is not advised for development to occur directly against staging or production environments. That said, the constant is still decoupled, also because it defines the kind of development that is occurring more granularly than a simple on/off switch like WP_DEBUG.

Setting the development mode for a site

To set the development mode for a site, simply add a definition of the WP_DEVELOPMENT_MODE constant to your wp-config.php file. For example, if your site is a development environment for your plugin:

define( 'WP_DEVELOPMENT_MODE', 'plugin' );

Most likely, in this case you also want to make sure you have WP_DEBUG enabled and WP_ENVIRONMENT_TYPE set to “development” or “local”.

Checking the development mode for a site

A new function wp_is_development_mode( $mode ) is the recommended way to check whether a given development mode is enabled for the WordPress site. The function expects you to pass a $mode parameter that you would like to check for (e.g. “core”, “plugin”, or “theme”) and returns a boolean for whether said mode is enabled.

Per the aforementioned list of possible values, if a site has WP_DEVELOPMENT_MODE set to “all”, the function will return true for any $mode passed.

Here is an example:

if ( wp_is_development_mode( 'theme' ) ) {
	/*
	 * This could contain some logic that only applies when developing a theme
	 * on the site.
	 */
}

Additionally to wp_is_development_mode( $mode ), another lower-level function wp_get_development_mode() has also been added in WordPress 6.3, which returns the value of the WP_DEVELOPMENT_MODE constant directly. However, accessing the constant value directly is discouraged. Due to special values such as “all” that can encompass multiple other development modes, it is advised to always use wp_is_development_mode( $mode ) instead.

If you are logged into the WP Adminadmin (and super admin) interface, you can access the current value of the WP_DEVELOPMENT_MODE constant under Tools > Site Health > Info, in the WordPress Constants section.

See #57487 for additional context on this change.

Props @peterwilsoncc for technical review, @stevenlinx for proofreading.

Update July 17, 2023: The function wp_in_development_mode() was renamed to wp_is_development_mode() after initial publication of this article (see [56249]). All references have been updated.

#6-3, #dev-notes, #dev-notes6-3

Layout updates in the editor for WordPress 6.3

Here are the 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. for layout-related changes in the editor.

Layout support stabilization and updates

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. support for layout has now graduated from experimental to stable. Everything works the same; the only difference is that when adding block support for layout in block.json, its name is now layout instead of __experimentalLayout. Support for the __experimentalLayout syntax will be maintained for a while, but it is recommended to upgrade any custom blocks using layout to the stable layout syntax. (#51434)

Code example (in block.json):

"supports": {
    "layout": true
}

Changes in CSSCSS Cascading Style Sheets. specificity for some layout types

The CSS output for the margin styles of children of constrained and flow (default) layout containers has changed. The generic rules used to have a specificity of 0,1,0 for all blocks; they have now changed to 0,0,0 for all blocks with an extra 0,2,0 rule applied only to the first and last blocks in the container.

This fixes the issue of global margin styles for specific blocks being overridden by layout styles (see #43404).

Compound block and layout type classname applied to inner wrapper of layout blocks

As of WP 6.2, layout classnames are added to the block inner wrapper. For most blocks, this is the same as the outer wrapper, but some blocks (e.g. coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. Cover) have multiple inner containers. In 6.3, a new classname is added to the inner wrapper of all blocks with layout, comprised of block classname + layout classname, e.g.: .wp-block-cover-is-layout-constrained. This is the updated Cover block markup:

<div class="wp-block-cover is-light">
    <span aria-hidden="true" class="wp-block-cover__background has-tertiary-background-color has-background-dim-100 has-background-dim"></span>
    <div class="wp-block-cover__inner-container is-layout-constrained wp-block-cover-is-layout-constrained">
        <p class="has-text-align-center has-large-font-size"></p>
    </div>
</div>

This makes it possible for blocks with a complex markup structure to support custom spacing styles.

Layout definitions removed from core theme.json

The layout definitions object, which stores base styles for the layout block support, has been removed from the core theme.json (settings.layout.definitions) and moved into the internal layout support files. Extending or overriding core layout definitions was never officially supported and including those definitions in a theme theme.json file resulted in bugs such as #49914.

Create Block Theme 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 generated theme.json files that included the layout definitions object before the bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. was fixed in #50268, so theme authors are advised to remove any layout definitions that may have been inadvertently included in their theme.json files in order to prevent buggy behavior.

New Grid layout type

A new grid layout type is available, based on CSS Grid, and defaulting to an auto-fill approach with configurable column width. It is also possible to set a fixed number of columns, by using the columnCount property in the layout object. (#49018)

To create a block with a grid layout, the following needs to be added in the supports object of the block’s block.json:

"layout": {
			"default": {
				"type": "grid"
			}
		}

Layout and block spacing support added to Post Template block

Previously, the Post Template block had custom layout styles that allowed for either a “list” or a “grid” (implemented behind the scenes with CSS flex) layout, with controls living in its parent Query block.

For 6.3, layout and block spacing support have been added to Post Template, and its controls now live in the Post Template toolbar. There is still a choice of “list” and “grid” styles, but “grid” is now implemented with the grid layout type. (#49050)

Spacer block gets orientation from the parent block layout

Spacer blocks inside a flex type layout block will now use the orientation of the parent layout. It is still possible to pass Spacer an orientation value from the parent block context, but Spacer blocks inside flex layouts will prioritize the flex orientation over the context one. (#49322)

Props for co-editing to @andrewserong and review to @bph and @leonnugraha.

#6-3, #dev-notes, #dev-notes6-3

WP_Query used internally in get_pages()

In WordPress 6.3, the function get_pages() has been updated to utilize WP_Query internally, resolving a 13-year-old ticketticket Created for both bug reports and feature development on the bug tracker. (#12821). This modification significantly reduces the complexity of the get_pages() function by offloading the burden of querying databases and handling the cache to WP_Query. The change builds upon the previous enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. introduced in #22176, which introduced query caching to WP_Query.

As a result, this update eliminates redundant code and ensures that all filters running in WP_Query are now also applied during the call to get_pages(). Users who leverage filters like posts_pre_query or posts_results to customize the behavior of WP_Query, such as retrieving data from alternative sources like cache or another database (e.g., elastic search), will benefit from this change.

Additionally, based on feedback from the glotpress team, a new 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. named get_pages_query_args has been added. This filter enables developers to modify the parameters passed to WP_Query, maintaining compatibility with the original parameter arguments.

See #56586 and #55806 for additional context.

Props to @flixos90 for peer review, to @stevenlinx and @leonnugraha for review.

#6-3, #dev-notes, #dev-notes6-3

Improved Caching for Database Queries in WP_User_Query

In WordPress 6.3, significant enhancements have been made to the WP_User_Query class, specifically regarding caching of database queries. This update builds upon the ongoing efforts of the performance team to optimize query caching for various WordPress CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. query classes. WP_Query introduced query caching in WordPress 6.1, while other query classes like WP_Comment_Query, WP_Site_Query, WP_Network_Query, and WP_Term_Query already had built-in query caching. As a result, WP_User_Query was the only remaining query class lacking this caching capability.

The implementation of query caching in WP_User_Query aligns with that of other query classes. Once a query is executed, the resulting database queries are cached, and subsequent queries with the same parameters will retrieve the data from the cache. This caching behavior, when combined with persistent object caching, ensures that the database query won’t be executed again until the caches are invalidated, leading to a substantial reduction in overall database queries. Even sites using in-memory caching will benefit from avoiding repetitive queries, although the performance improvement may not be as significant.

It’s important to note that starting from this version onward, all calls to WP_User_Query will be automatically cached by default. However, if you wish to disable query caching for specific queries, you can simply set the cache_results parameter to false, as demonstrated in the following example:

$args = array(

   'number' => 50,

   'cache_results' => false

);

$query = new WP_User_Query( $args );

Alternatively, you can globally disable caching by using a 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., as shown below:

function disable_caching( $wp_user_query ) {

   $wp_user_query->query_vars['cache_results'] = false;

}

add_action( 'pre_get_users', 'disable_caching' );

For developers working on custom solutions, it’s essential to utilize Core functions such as wp_insert_user for adding users to the database. These functions are well-maintained and ensure proper cache invalidation. If you directly update the database, it is strongly recommended to call the clean_user_cache function after modifying the respective database row.

With this update, a new global cache group called user-queries is introduced to store the results of queries. If your site uses persistent object caching, make sure it supports adding global cache groups by utilizing the wp_cache_add_global_groups function, introduced in WP 2.6. If not, you will need to manually add the three new global cache groups.

It’s worth noting that caching will be disabled for user queries that utilize the field parameter and request more than 3 fields. This decision is made to prevent cache values from becoming excessively large and to avoid filling up caches with data unlikely to be reused.

Lastly, plugins utilizing the users_pre_query hook to modify the returned values will bypass caching and continue to function as they did in previous versions of WordPress.

For additional context on the changes, please see #40613.

Props to @flixos90 and @adamsilverstein for peer review, to @stevenlinx for review.

#6-3, #dev-notes, #dev-notes6-3

I18N Improvements in 6.3

Various internationalization (i18n) improvements are in WordPress 6.3, and this developers note will focus on these.

Allow to short-circuit load_textdomain()

In #58035 / [55928], a new pre_load_textdomain 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. was introduced. This is useful for plugins to develop and test alternative loading/caching strategies for translations. This brings consistency with the existing pre_load_script_translations filter for 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/. translations.

Improvements to just-in-time translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. loading

In #58321, it was reported that _load_textdomain_just_in_time() was firing too often if no translations were found for a given text domain, which typically is the case on site running English (US).

[55865] addresses this issue, which resulted in some minor performance improvements.

Props to @spacedmonkey for technical review, to @stevenlinx for proofreading.

#6-3, #dev-notes, #dev-notes6-3, #i18n

Registering scripts with `async` and `defer` attributes in WordPress 6.3

WordPress 6.3 introduces support for registering scripts with async and defer attributes as part of an enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. to coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.’s existing Scripts 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.. This addresses a long-standing Trac ticket, and adds the ability to define a loading strategy for scripts. Supported strategies are as follows:

  • Blocking (default, this strategy is not supplied)
  • Deferred (by supplying a defer loading strategy)
  • Asynchronous (by supplying an async loading strategy)

This enhancement was originally proposed in December 2022.

Why is this enhancement useful?

Adding defer or async to script tags enables script loading without “blocking” the rest of the page load, resulting in more performant sites via improved Largest Contentful Paint (LCP) performance. This leads to a better user experience. This has been common practice in web engineering for over a decade, yet to date there have been no core methods or a means of achieving this when registering/enqueuing scripts using core WordPress APIs.

Prior to this enhancement, developers have had to resort to less than ideal alternatives such as directly filtering the tags at the point of output (using the script_loader_tag 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., or worse the clean_url filter), or handling the 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.) output directly using wp_print_script_tag and the wp_script_attributes filter. Although fairly common practice (as it was the only means available prior), it is considered “hacky” as it does not take into account the dependency tree, or inline scripts for that matter, which can lead to interoperability issues or bugs with other scripts.

The difference between deferred (via the defer script attribute) and asynchronous (via the async script attribute) scripts is as follows:

  • Deferred scripts
    Scripts marked for deferred execution — via the defer script attribute — are only executed once the DOM tree has fully loaded (but before the DOMContentLoaded and window load events). Deferred scripts are executed in the same order they were printed/added in the DOM, unlike asynchronous scripts.
  • Asynchronous scripts
    Scripts marked for asynchronous execution — via the async script attribute — are executed as soon as they are loaded by the browser. Asynchronous scripts do not have a guaranteed execution order, as script B (although added to the DOM after script A) may execute first given that it may complete loading prior to script A. Such scripts may execute either before the DOM has been fully constructed or after the DOMContentLoaded event.

Summary of the changes

At a high level, the changes can be summarized as follows:

  • WordPress now adds support for specifying a script loading strategy via the wp_register_script() and wp_enqueue_script() functions.
  • These functions have new function signatures, with the prior $in_footer boolean parameter being overloaded to accept a new $args array parameter in order to facilitate an easy entry point for specifying a loading strategy for a script, while still retaining full backward compatibility for $in_footer implementations; this retains the means of specifying whether a script should be printed in the footer or not via a key within the new $args parameter. Note that the strategy can also be specified in a backwards-compatible way via wp_script_add_data().

Various additions and enhancements to the WP_Scripts class were made to facilitate the necessary business logic that prepares and outputs a script’s loading strategy.

Example 1: Specifying a loading strategy for a script

A loading strategy may be assigned via the wp_register_script() and wp_enqueue_script() functions by passing a strategy key value pair to the new/overloaded $args parameter. 

The following example showcases a new script with handle 'foo' being registered as a deferred script:

wp_register_script( 
	'foo', 
	'/path/to/foo.js', 
	array(), 
	'1.0.0', 
	array(
		'strategy' => 'defer'
	) 
);

This exact same means of specifying a loading strategy may be achieved via the wp_enqueue_script() function.

Example 2: Specifying that a script be printed in the footer via the new API

The next example showcases a second script being specified for footer printing using the new API, while also supplying the async loading strategy at the same time:

wp_register_script( 
	'bar', 
	'/path/to/bar.js', 
	array(), 
	'1.0.0', 
	array(
		'in_footer' => true,
		'strategy'  => 'async',
	)
)

This exact same means of specifying footer printing may be achieved via the wp_enqueue_script() function.

Implementation details

This feature enhances the existing Scripts API by providing a simple means of specifying a loading strategy by extending commonly used & well known aspects of the Scripts API. It also takes into consideration a script’s dependency tree (its dependencies and/or dependents) when deciding on an “eligible strategy” so as not to result in application of a strategy that is valid for one script but detrimental to others in the tree by causing an unintended out of order of execution. This is near-impossible to achieve via the prior means of adding script loading strategy attributes when using the alternative “hacky” means outlined in the section above.

Technical implementation of the script loading strategy enhancements have been undertaken within the existing Scripts API, notably within the WP_Scripts class, and via enhancements to the familiar and commonly used wp_register_script() and wp_enqueue_script() functions.

A note on dependencies vs dependents
To avoid confusion regarding terminology, let’s clarify the difference between a script’s dependencies vs. its dependents. A script’s dependencies refers to the scripts that said script itself depends on, i.e they must be enqueued prior to said script being enqueued. A script’s dependents on the other hand refers to the scripts that depend on said script, i.e scripts that define said script in their dependencies array.

Changes to the $in_footer parameter of wp_register_script() and wp_enqueue_script() functions

The most notable change to the existing wp_register_script() and wp_enqueue_script() functions is the function signature change, where $in_footer (previously a boolean parameter) has been overloaded to also accept an array $args parameter, with any of the following keys:

  • (bool) in_footer
    • Behaves just like prior implementation of the top level $in_footer param.
  • (string) strategy
    • Accepts an intended loading strategy for the given script being registered/enqueued. Acceptable string values available at the time of implementation are defer for deferred scripts and async for asynchronous scripts.
    • Defaults to blocking behavior, thus retaining backward compatibility for existing script registrations and enqueues.

Retaining backward compatibility

For prior/existing usage of the wp_register_script() and wp_enqueue_script() functions making use of the $in_footer boolean param, backward compatibility is retained via logic that explicitly sets the scripts group to the applicable value for footer or 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. printing based on the boolean value passed to the new/overloaded $args parameter. Thus, full backward compatibility is retained and this is a non-breaking enhancement of the API.

While the changes introduced within this feature themselves are considered non-breaking, when making use of the new $args parameter (replacing/overloading the previous $in_footer parameter) in 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/theme/codebase powered by WordPress <6.3, there is one scenario where the $in_footer intention would be misunderstood by core. Take for example the following scenario:

wp_register_script( 
	'foo', 
	'/path/to/foo.js', 
	array(), 
	'1.0.0', 
	array( 
		'strategy'  => 'defer',
		'in_footer' => false, // Note: This is the default value.
	)
);

In WordPress >=6.3 this would be correctly evaluated as being printed in the head via the in_footer array key value being false (which is also the default).

In WordPress versions <6.3, however, the presence of the above array assigned to the $in_footer parameter would itself evaluate to a boolean value of true, the opposite of what may be intended by the developer. That being said, one could rightfully argue that in versions of WordPress that do not support deferred/asynchronous scripts, having them printed in the footer is the next best alternative.

The simplest way to prevent this interoperability problem is to pass the strategy via a different means than the $args param to wp_register_script() or wp_enqueue_script() functions.

It can be passed instead via wp_script_add_data(), in which case it will be understood by WordPress 6.3 but ignored by older versions.

wp_register_script( 
	'foo', 
	'/path/to/foo.js', 
	array(), 
	'1.0.0', 
	false
);
wp_script_add_data( 'foo', 'strategy', 'defer' );

Alternatively, interoperability between WordPress versions newer and older than 6.3 using a single function call per script can be achieved by wrapping script registrations/enqueues within a wrapper function that accounts for new and old function signatures, thus retaining total backward compatibility.

An example of such a script registration/enqueue wrapper may look something as follows:

myplugin_register_script( $handle, $src, $deps, $ver, $args ) { 
    global $wp_version;

    // If >= 6.3, re-use wrapper function signature.
    if ( version_compare( $wp_version,'6.3', '>=' ) ) { 
        wp_register_script(
            $handle,
            $src,
            $deps,
            $ver,
            $args
            );
        } else {
        // Extract in_footer value for older version usage.
        $in_footer = isset( $args['in_footer'] ) ? $args['in_footer'] : false;
        
        wp_register_script(
            $handle,
            $src,
            $deps,
            $ver,
            $in_footer
        );
    }
}

Intended vs. eligible loading strategies

It should be noted that while a developer may intend for a given script to contain a certain loading strategy, the final loading strategy may differ based on factors such as script dependencies/dependents and inline scripts. 

For example, if a developer registers script foo with a strategy of defer, its dependencies must either use a defer or blocking strategy and its dependents must use a defer strategy in order for the intended execution order to be maintained. If a dependent of said script foo is then registered with a blocking intended strategy, script foo and all of its dependencies would then become blocking.

Newly added logic within the WP_Scripts class is responsible for exercising a series of logical checks that ensure that the final strategy for a given script handle is the most eligible strategy based on the factors outlined further above.

A handle will never inherit a strategy that is “more strict” than the one intended, i.e. a script marked for deferred loading will never be changed to asynchronous loading, but the reverse may indeed be the outcome if environmental factors warrant it.

Inline scripts

There are some nuances when applying a loading strategy to scripts (or scripts in the dependency tree) that have inline scripts attached to them, as this ultimately has an effect on the final outcome of an intended/eligible strategy.

Inline scripts that are registered in the before position, remain largely unchanged in behavior, given that they will inherently be parsed and/or executed before the main/parent script is parsed for immediate, deferred or asynchronous execution.

Inline scripts that are registered in the after position (the default for wp_add_inline_script()), however, will affect the final loading strategy of a main/parent script if said main/parent script has an async or deferred eligible strategy. This is largely due to the complexity in ensuring that inline scripts attached to deferred/asynchronous scripts execute at the appropriate and expected time, while not having a negative impact on the parent script itself, and the dependency tree as a whole.

Therefore, if a given script handle contains inline scripts in the after position, this script will be assumed to be blocking and any intended strategy such as defer/async will be removed, with the final eligible strategy being blocking. This, in turn, may affect the script’s dependency tree, and all scripts within it, may too, be treated as blocking scripts in a bid to retain correct execution order and functionality of the enqueued scripts. Logic is explicitly employed to ensure correct execution order of the script tree in these instances.

A follow-up ticketticket Created for both bug reports and feature development on the bug tracker. #58632 has been opened to continue ongoing discussions and proof of concept implementations to potentially introduce delayed execution of inline after scripts in the future which would preserve their loading order.

Migrating to the new API

Code implementations making use of legacy methods of adding async or defer attributes to script tags should migrate to the new API. These include scenarios where the attributes have historically been added to a script tag by way of the script_loader_tag filter, or worse via the clean_url filter.

The following examples show implementation that make use of the script_loader_tag and clean_url filters, which are now considered less-than-ideal approaches, and then show how it should be done using the new API:

MigrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. Consideration Example 1: Adding a defer attribute via the script_loader_tag filter

function old_approach( $tag, $handle ) {
// Only affects foo script.
    if ( 'foo' !== $handle) {
        return $url;
    }

// Modern implementations may employ WP_HTML_Tag_Processor here.
    return str_replace( ' src=', ' defer src=', $tag );
}
add_filter( 'script_loader_tag', old_approach, 10, 2 );

Migration Consideration Example 2: Adding a defer attribute via the clean_url filter

// WARNING: THIS HAS ALWAYS BEEN BRITTLE AND IS NOT RECOMMENDED.
function old_brittle_approach( $url ) {
    // Only affects foo script.
    if ( false === strpos( $url, 'foo.js' ) ) {
        return $url;
    }

    return "$url' defer "; // Assumes single-quoted attributes!
}
add_filter( 'clean_url', 'old_brittle_approach' );

If you are using an approach similar to the one above to add defer or async attributes to a script, please migrate to the new API by using any one of the approaches outlined earlier in this post.

Props: @joemcgill @flixos90 @westonruter @adamsilverstein @jyolsna @stevenlinx

#6-3, #dev-notes, #dev-notes6-3

Image performance enhancements in WordPress 6.3

WordPress 6.3 comes with several enhancements that improve load time performance for content with images. The benefits can be seen in the Largest Contentful Paint metric (short “LCP”), which captures the time from the beginning of the request until the largest content element in the viewport has been rendered.

Summary of the changes

At a high level, the changes can be summarized as follows:

  • WordPress now automatically adds the fetchpriority attribute with a value of “high” to the image it determines most likely to be the “LCP image”, i.e. the image that is the largest content element in the viewport. The attribute tells the browser to prioritize this image, even before it has computed the layout, which typically improves LCP by 5-10%. See the original proposal post for additional context.
  • Further adjustments and fixes have been implemented to improve the automatic handling of lazy-loading via the loading attribute to more reliably detect when to omit the attribute from some images. This effort was started in WordPress 5.9 and continued in WordPress 6.2. Most recently, a holistic assessment of the remaining issues led to all of them being fixed in WordPress 6.3. See the relevant WordPress 5.9 dev note post for additional context on why not lazy-loading in-viewport images, especially the LCP image, is important for performance.

In order to implement the automated fetchpriority support, some refactoring was needed to decouple the logic for detecting in-viewport images from the lazy-loading specific functionality. As a result, the fetchpriority and loading attributes are now controlled by a single function in WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. If you have been relying on specific parts of WordPress core’s lazy-loading logic in your 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 or theme, please continue reading to learn more about those changes.

New function wp_get_loading_optimization_attributes()

A new function wp_get_loading_optimization_attributes( string $tag_name, array $attr, string $context ) is introduced in WordPress 6.3, which returns an associative array of additional attributes and their values. This provides a central place to get HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. attributes that potentially improve the load time performance of images. For now, the function can only return a fetchpriority or loading attribute (or neither if not applicable), though this may be enhanced with other performance-related attributes in the future.

The new function is now used everywhere in WordPress core where fetchpriority or loading attributes are handled for images, most importantly:

  • Handling in-content images, via wp_filter_content_tags()
  • Handling programmatically rendered images, via wp_get_attachment_image() / get_the_post_thumbnail()
  • Handling avatarAvatar An avatar is an image or illustration that specifically refers to a character that represents an online user. It’s usually a square box that appears next to the user’s name. images, via get_avatar()
  • Handling the custom 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. image, via get_header_image_tag() (support added via #58680)
  • Handling images within shortcodes, via do_shortcode() (support added via #58681)
  • Handling images in the image widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user., via the WP_Widget_Media_Image class (support added via #58704)

That by itself is an improvement already, leading to consistent behavior across the board. Prior to this change, the above functions were using slightly different implementations for lazy-loading which could potentially lead to issues.

In order to use the function, pass the HTML element’s 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.) name and attributes to the new function, alongside a context string depending on how the image is being rendered in WordPress (for example “the_content”, or “wp_get_attachment_image”). Providing the tag name is important as the function is not limited to only images. At the moment it supports “img” tags (for fetchpriority and loading attributes) and “iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser.” tags (for loading attributes), though this may be expanded in the future. The return value of the function is always an array, which can be merged into the tag’s existing array of attributes, or alternatively specific attribute values can be extracted as needed.

Here you can see an example on how to potentially use the function:

$attr = array(
	'src'    => 'my-image.jpg',
	'width'  => 500,
	'height' => 300,
);
$attr = array_merge(
	$attr,
	wp_get_loading_optimization_attributes( 'img', $attr, 'wp_get_attachment_image' )
);
echo '<img';
foreach ( $attr as $key => $value ) {
	echo ' ' . sanitize_key( $key ) . '="' . esc_attr( $value ) . '"';
}
echo '>';

Note that the example uses the “wp_get_attachment_image” context, which is typically used only in the wp_get_attachment_image() function. While you could provide another arbitrary value here, at the moment it is advisable to use the most suitable core context as WordPress core only handles those specifically. Assuming that this is a generic image being rendered, the “wp_get_attachment_image” context works well.

Which additional attributes are returned in the above example depend on the specific context and place where the function is called. If the image is the first large image rendered in the response, the return value would be array( 'fetchpriority' => 'high' ). If it is an image that is likely further down the page, the return value would be array( 'loading' => 'lazy' ). There is also a chance that the array would be empty; for example, if the image is likely in the viewport but not large enough to be eligible as the LCP image.

Note that the function will never return both fetchpriority="high" and loading="lazy" for the same image, as those two attribute-value combinations are mutually exclusive and should never be used on the same element: lazy-loading an image while also marking it as high priority is an anti-pattern that should be avoided.

Rendering a custom header image in your theme

Custom header images are a feature that classic themes have supported for many years. With WordPress 6.3, the get_header_image_tag() function has received support to automatically include relevant loading optimization attributes, using the new wp_get_loading_optimization_attributes() function.

If you are developing or maintaining a theme that has custom header image support, it is advisable to use the get_header_image_tag() function to render the image. If you’re concerned about supporting WordPress versions prior to when this function was introduced in WordPress 4.4, see #58675 for how older core themes are updated to use this function when available.

If for some reason you are unable to use the get_header_image_tag() function and need to render the image tag manually while retrieving the image URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org with the get_header_image() function, you can still ensure the loading optimization attributes are applied. For example consider the following code:

$attr = array(
	'src'    => get_header_image(),
	'width'  => (int) get_custom_header()->width,
	'height' => (int) get_custom_header()->height,
);
$attr = array_merge(
	$attr,
	wp_get_loading_optimization_attributes( 'img', $attr, 'get_header_image_tag' )
);
echo '<img';
foreach ( $attr as $key => $value ) {
	echo ' ' . sanitize_key( $key ) . '="' . esc_attr( $value ) . '"';
}
echo '>';

Additionally, if you are certain that the header image is also the LCP image of the page, you can manually mark it as such, by adding 'fetchpriority' => 'high' to the $attr array before calling wp_get_loading_optimization_attributes(). Calling the function would still be crucial to ensure WordPress core considers the image so that subsequent images on the page also receive the correct attributes.

Customization of image priority and lazy-loading behavior

With the new function being used consistently anywhere images are rendered in WordPress core, support for customizing is also improved. The function will never override attributes that are already provided, so if you set a fetchpriority or loading attribute on an image before this function is called, the attribute will be kept as is. This allows fine tuning by not enforcing the default automated behavior. If doing so, keep in mind never to set both fetchpriority="high" and loading="lazy" for an element. If the function encounters those two attribute-value combinations together, it will trigger a warning.

Modifying the default behavior for lazy-loading works just like before. Relevant filters like wp_lazy_loading_enabled, wp_img_tag_add_loading_attr, and wp_omit_loading_attr_threshold have not been modified in this release. However, the default value for the wp_omit_loading_attr_threshold 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. has been modified from 1 to 3 (see #58213 for context) in order to better support the common pattern of multi-column layouts with images. Please refer to the lazy-loading dev notedev 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. posts for WordPress 5.5 and for WordPress 5.9 for more information on those existing filters.

For fetchpriority, a new filter wp_min_priority_img_pixels was introduced, which allows developers to modify the size threshold above which an image is considered eligible to receive fetchpriority="high". This minimum threshold value is in place to ensure only images of a certain size are considered for the LCP image. For example, a small icon would never be the LCP image — even if there were no other images above the fold, the LCP element would most likely be a non-image element, e.g. a heading.

The size is defined as a the product of “width * height” of the image, and the default threshold is 50,000. For example, this means that an image with a width of 300 pixels and height of 200 pixels is eligible (since 300*200=60000) while an image with a width of 200 pixels and height of 200 pixels is not (since 200*200=40000).

Here is an example: Imagine you have an image that is 200×200 pixels, and despite being that small, you know it is the LCP image of the specific URL it appears on. You could then use the new filter to ensure the image can receive fetchpriority="high".

add_filter(
	'wp_min_priority_img_pixels',
	function( $size ) {
		if ( is_this_the_url_with_the_image() ) {
			return 200 * 200; // Images at least as big as 200x200 
		}
		return $size;
	}
);

Keep in mind that in such a situation you could alternatively provide fetchpriority="high" on the image manually, which is just another arguably more direct way to achieve the same result. Therefore this approach is better suited for dynamic content in layouts where you might not know the exact image.

Deprecated functions

With the new wp_get_loading_optimization_attributes() function controlling both fetchpriority and loading attributes, a few existing functions are being deprecated as part of the WordPress 6.3 release.

wp_get_loading_attr_default() is superseded by the new function and therefore should not be used anymore. If you are currently calling this function in your plugin, you can use code such as the one below to remain compatible with WordPress versions before and after this deprecation:

function myplugin_get_loading_attr_default( $tag_name, $attr, $context ) {
	// For WP >= 6.3.
	if ( function_exists( 'wp_get_loading_optimization_attributes' ) ) {
		$loading_optimization_attr = wp_get_loading_optimization_attributes( $tag_name, $attr, $context );
		if ( isset( $loading_optimization_attr['loading'] ) ) {
			return $loading_optimization_attr['loading'];
		}
		return false;
	}

	// For WP < 6.3.
	return wp_get_loading_attr_default( $context );
}

While the above works well for a transition period, for the long term consider also supporting fetchpriority, e.g. by using the new function directly.

The other function that has been deprecated is wp_img_tag_add_loading_attr(), as this function is superseded by a new wp_img_tag_add_loading_optimization_attrs(), which also encompasses the fetchpriority enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature.. Note that both of these functions are intended for parsing HTML content from the database, so it is advisable for plugins to not rely on either of them. Try to use wp_get_loading_optimization_attributes() or a wrapper function like the above instead to add loading optimization attributes to a specific image.

Please see #58235 for more information on fetchpriority support.

Lazy-loading issues addressed

As mentioned before, WordPress 6.3 addresses several outstanding problems where loading="lazy" was being added to images that should not receive it. Other than the aforementioned change of the default value for the wp_omit_loading_attr_threshold filter from 1 to 3 (see #58213), those fixes do not affect the underlying developer APIs in any way.

Here is a list of the other lazy-loading bugs that were addressed:

  • Images in the header (before “the 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.”) in classic themes are now eligible for having the loading=”lazy” attribute omitted. See #58211.
    • For 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. themes, this was already fixed in WordPress 6.2, via #56930.
  • Programmatically rendered images which are injected into post content no longer incorrectly affect the lazy-loading logic. See #58089.
  • Rendering an excerptExcerpt An excerpt is the description of the blog post or page that will by default show on the blog archive page, in search results (SERPs), and on social media. With an SEO plugin, the excerpt may also be in that plugin’s metabox. where the full post contains images no longer incorrectly affects the lazy-loading logic. See #56588.
  • A duplicate call to get the loading attribute value in get_the_post_thumbnail() has been removed. See #58212.
  • Images before “the loop” are now counted towards the threshold for not lazy-loading. See #58635.

Props @westonruter @joemcgill for technical review, @mukesh27 @stevenlinx for proofreading.

#6-3, #dev-notes, #dev-notes6-3

Improvements to the metadata API in WordPress 6.3

WordPress 6.3 brings significant improvements to the metadata 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., enhancing the lazy loading capabilities for term, comment, and site metadata. These enhancements aim to improve performance, optimize code readability, and ensure a consistent developer experience across different metadata types.

For context: Lazy loading metadata in WordPress refers to a technique where metadata associated with various elements, such as terms, is loaded only when it is actually needed. Instead of fetching and loading all metadata upfront, the metadata is deferred to a queue until the specific metadata type is requested, reducing unnecessary database queries or cache lookups and improving overall performance. 

Term metadata lazy loading improvements

Term 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. in WordPress has always been lazily loaded since its introduction in WordPress 4.4. However, this behavior was only applicable to WP_Query. In other CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. components like WP_Term_Query, term meta was always primed by default, unless developers explicitly set the update_term_meta_cache parameter to false. Unfortunately, many developers overlooked this parameter, resulting in unnecessary loading of term meta. 

In WordPress 6.3, WP_Term_Query has been improved so that instead of priming term meta, which involves performing a database or cache lookup, terms are now added to a queue dedicated to loading term meta. This queue, implemented as an array stored in memory, offers excellent performance. Additionally, it allows developers to conveniently include term IDs if they anticipate requiring them later. Only when the first call to get_term_meta is made, will all the term meta be primed in a single request.

To facilitate the lazy loading of term meta, a new function called wp_lazyload_term_meta() has been introduced in Core. This function provides an efficient mechanism for handling term meta and contributes to optimizing the overall performance of WordPress. To use, pass an array of term IDs to the function like this: 

wp_lazyload_term_meta( array( 1, 2, 3 ) );

For more information, please check out the original ticketticket Created for both bug reports and feature development on the bug tracker. #57645.

WordPress 6.3 introduces further enhancements to the handling of term meta in WP_Query.  Currently, in the WP_Query class, the function wp_queue_posts_for_term_meta_lazyload is invoked, which adds the term IDs of all terms linked to the queried posts into the lazy load metadata queue. Improvements to this function were made in WordPress 6.2, which improved the performance of this function by utilizing wp_cache_get_multiple()

In this new release, an unnecessary check to verify term existence has been eliminated. As a result, when wp_queue_posts_for_term_meta_lazyload() is called, it no longer executes get_term for every individual term ID, as it is highly unlikely that the term would not exist. See #57966

There were also a number of other places where term meta was not loaded at all as it is completely unneeded. See #58230, #57701

Comment metadata lazy loading improvements

Similar to term meta, comment meta in WordPress was previously lazily loaded only within the context of WP_Query. Now, rather than priming comment meta in the WP_Comment_Query class, it is added to the lazily loaded metadata queue and loaded only when used. This keeps the logic consistent between term and comment meta and also makes the code much more readable. As a result, the wp_queue_comments_for_comment_meta_lazyload() function, which is now unnecessary, has been deprecated. See #57801, #58301

Site metadata is now lazily loaded

Site meta was introduced in #40647 and allows developers to extend 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 functionality by adding metadata to a site on the multisite. Site meta is not to be confused with networknetwork (versus site, blog) meta, which is used for network options. Site meta is only used in one place in core and designed for 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 use. 

Previously, when calling WP_Site_Query or get_sites(), developers had to pass an update_site_meta_cache parameter to the query to ensure that site meta was not primed, resulting in a cache lookup or database query. Now, whenever update_site_meta_cache is true, site ids are added to the queue to be lazily loaded. If get_site_meta() is called, all the IDs in the queue are loaded in a single call. 

A new function has been added in core to add site IDs to the queue called wp_lazyload_site_meta(). It can be used by passing an array of site IDs, like this:

wp_lazyload_site_meta( array( 1, 2, 3 ) );

For more information, please check out the original ticket #58185.

General improvements to the Lazy loading API

The WP_Metadata_Lazyloader class, responsible for lazy loading metadata, underwent a significant refactor to enhance code maintainability. As part of this refactoring, the lazyload_term_meta and lazyload_comment_meta methods of the class have been deprecated. These methods have been replaced with a more versatile lazyload_meta_callback method, which can be reused for any metadata type. 

If your code currently utilizes the lazyload_term_meta and lazyload_comment_meta methods, it is recommended that you transition to using lazyload_meta_callback. This improvement allows for easy extension of the metadata data API to support additional metadata types in the future, such as posts and users.

Further enhancements were made to the lazy loading metadata API. A check is now implemented to verify if the requested ID is already present in the queue before processing. Here’s an example scenario: Let’s say your page request has added three items (IDs 5, 6, and 7) to the metadata lazy loading queue. However, when you call the get_term_meta function, you request ID 9, which is not in the queue. Previously, this would have led to an additional database or cache lookup. With the latest improvement, the queue is checked before processing to see if the current ID is already in the queue. If it’s not, the ID is added to the queue, preventing unnecessary lookups. See #57901

There have been significant changes in how metadata is handled for comments, terms, and sites. In the updated implementation, the prime meta parameters in functions such as _prime_term_cache() and WP_Term_Query are always respected. However, instead of directly priming the meta, it is now added to the metadata queue. Previously, there were inconsistencies in these functions and classes. For example, in _prime_term_cache(), if the term was already present in the cache, the term meta would not be primed. This led to a confusing developer experience and degraded performance.

Now, with the latest improvements, adding an ID to the queue has minimal to no performance impact when requested. As a result, these functions and classes are expected to behave in a more predictable manner. Developers can rely on consistent behavior and improved performance regarding metadata handling for comments, terms, and sites. See #57227

Props to @flixos90 and @joemcgill for peer review, to @stevenlinx, @leonnugraha and @costdev for review.

#6-3, #dev-notes, #dev-notes6-3

New in 6.3: Rollback for failed manual plugin and theme updates

Should the manual 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 or theme update process fail, the rollback feature will automatically restore the previously installed version to ensure the website remains available to its users.

The Rollback feature originated in #51857.

When updating a plugin or theme, the old version of the plugin or theme is moved to a wp-content/upgrade-temp-backup/plugins/PLUGINNAME or wp-content/upgrade-temp-backup/themes/THEMENAME folder.

The reason we chose to move instead of zip, is because zipping/unzipping are resources-intensive processes, and could increase the risk of failure on low-end, shared hosts. Moving files, on the other hand, is performed instantly and won’t be a bottleneck.

Moving is accomplished by means of the new move_dir() function, included in WordPress 6.2. PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher’s rename() is used for this with a fallback to copy_dir(), a recursive file copy used by CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. for a long time. move_dir()uses the ::move()method for the WP_Filesystem_DirectWP_Filesystem_FTPextWP_Filesystem_ftpsockets, and WP_Filesystem_SSH2 filesystem abstractions and has a fallback to copy_dir(). Find out more in the move_dir() devnote.

If the update process fails, then the backup we moved in the “upgrade-temp-backup” folder is restored to its original location. If the update succeeds, then the backup is deleted.

Two new checks were added in the Site Health screen:

  • Check to make sure that the backup folders are writable.
  • Check there is enough disk-space available to safely perform updates.

To avoid confusion: The “upgrade-temp-backup” folder will NOT be used to “roll back” a plugin/them to a previous version after a successful update. You can use the various rollback plugins for this.

The “upgrade-temp-backup” folder will simply contain a transient backup of the previously installed plugin or theme getting updated, and as soon as the update process finishes, the folder will be emptied.

When a rollback occurs, the user should simply see that there is an update pending and their site should still be working.

The simplest explanation of what results in a manual update failure and a rollback is anything that returns a WP_Error from WP_Upgrader::install_package().

  1. A bad request, something missing the source or destination of the update.
  2. WP_Error returned from the upgrader_pre_installupgrader_source_selectionupgrader_clear_destinationupgrader_post_install filters.
  3. An empty download package.
  4. A failure when moving the moving the installed plugin/theme to the temp-backup directory.
  5. If the remote source destination folder not able to be cleared and something is there.
  6. Unable to create remote destination folder.
  7. Unable to move/copy the the update to the remote destination.

Updated August 2, 2023 @afragen

The above encompasses parts 1 and 2 of the Rollback feature. Part 3, hopefully for WordPress 6.4, is the same process but for automatic updates. Specifically, Rollback part 3 checks to see that the updated plugin does not cause a PHP fatal error when activated. If it does, this error is captured and the previously installed version is restored.

All of the Rollback feature, parts 1-3, are included for testing in the Rollback Update Failure feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins..

Props @costdev for peer review, @stevenlinx and @desrosj for review.

#6-3, #dev-notes, #dev-notes6-3, #props

Requests library upgraded to 2.0.5 in WordPress 6.2

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. #54504 was created to upgrade the Requests library to 2.0.0. Requests 2.0.0 was a major releasemajor release A release, identified by the first two numbers (3.6), which is the focus of a full release cycle and feature development. WordPress uses decimaling count for major release versions, so 2.8, 2.9, 3.0, and 3.1 are sequential and comparable in scope. and contains breaking changes. Additional upgrades to 2.0.5 were completed during this process. View the upgrade guide.

Namespacing introduced

Requests 2.0.0 introduces namespacing (PSR-4) for all Requests code. A full backward compatibility layer is included and old PSR-0 class names are still supported. However, using the old PSR-0 class names will generate a deprecation notice.

If your 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 or theme uses the WordPress native wp_remote_*() functions, you won’t need to do anything.

If your plugin or theme uses Requests directly, and only supports the latest version of WordPress, you should update your code to use the namespaced names. For example, to perform a request with a try-catch using the namespaced names:

// Old: Perform a request with a try-catch in Requests 1.x.
try {
	$response = Requests::request( $url, $headers, $data, $type, $options );
} catch ( Requests_Exception $e ) {
	return new WP_Error( 'http_request_failed', $e->getMessage() );
}

// New: Perform a request with a try-catch in Requests 2.x.
try {
	$response = WpOrg\Requests\Requests::request( $url, $headers, $data, $type, $options );
} catch ( WpOrg\Requests\Exception $e ) {
	return new WP_Error( 'http_request_failed', $e->getMessage() );
}

If your plugin or theme uses Requests directly and supports a wider range of WordPress versions, you may need to conditionally declare the REQUESTS_SILENCE_PSR0_DEPRECATIONS constant as true to silence deprecation notices about the old PSR-0 class names. You should upgrade your code as soon as WordPress 6.2 becomes the minimum WordPress version for your plugin or theme.

if ( ! defined( 'REQUESTS_SILENCE_PSR0_DEPRECATIONS' ) ) {
    define( 'REQUESTS_SILENCE_PSR0_DEPRECATIONS', true );
}

Directory structure changes

wp-includes/class-requests.php and wp-includes/Requests/library/Requests.php have been deprecated in WordPress 6.2.

Class and interface files/directories have been moved.

  • Old location: wp-includes/Requests/.
  • New location: wp-includes/Requests/src/.

The Requests class file is now located at wp-includes/Requests/src/Requests.php.

New minimum PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher version

Requests 2.0.0 drops support for PHP 5.2 – 5.5, and the new minimum supported PHP version is now 5.6.

Support for HHVM has also been dropped formally now.

Many classes now marked final

A large number of classes have been marked as final. These changes were made after researching which classes were being extended by plugin and theme authors, and due diligence was applied before making these changes.

If this change causes a problem that wasn’t anticipated, please open an issue to report it.

Stricter input validation

All typical entry point methods in Requests will now, directly or indirectly, validate the received input parameters for being of the correct type.

When an incorrect parameter type is received, a catchable WpOrg\Requests\Exception\InvalidArgument exception will be thrown.

The input validation has been set up to be reasonably liberal, so if Requests was being used as per the documentation, this change should not affect you. If you still find the input validation to be too strict and you have a good use-case of why it should be loosened for a particular entry point, please open an issue to discuss this.

PHP 8.x compatibility

Requests 2.x is compatible with PHP 8.0, 8.1 and 8.2.

A note on bundled certificates

While the changelogs for Requests 2.0.0 – 2.0.5 mention updates for bundled certificates, WordPress uses its own bundled certificates, so these updates do not affect plugins or themes.

Full changelogs

Props to @jrf and @hellofromtonya for peer review, to @bph, @webcommsat, and @milana_cap for review.

#6-2, #dev-notes, #dev-notes-6-2