Main query loop handling for block themes in 6.4

In WordPress 6.4, a change has been applied to how the main query 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. is being handled 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. For singular content, the output of block templates (e.g. single.html or page.html) will be automatically wrapped in the main query loop.

Classic theme background

Historically, classic themes have included a main query loop (sometimes referred to as just “the loop”) in every template where any kind of WordPress posts from the database would be displayed. For reference, this is what the loop roughly looks like in most classic themes:

if ( have_posts() ) {
	while ( have_posts() ) {
		the_post();

		// Render the post.
	}
}

While the loop is primarily intended to iterate over the posts in a list of posts (such as the blogblog (versus network, site) or a categoryCategory The 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. archive), even for singular content (e.g. a single post or page) this loop has been historically required for various reasons. For example, the in_the_loop() function is used by many plugins to check whether a post is currently being output.

The loop in block themes

Block themes do not use PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher templates, so they do not manually output a loop like the one shared previously. Instead, blocks are responsible for handling it, typically the “Query loop” block (core/query). It automatically takes care of rendering the loop correctly.

However, the core/query block is only intended for use in archives, or any content that displays a list of posts. It would not be suitable for singular content as it wraps the posts in list markup, which would be semantically incorrect. Furthermore, while using a loop on singular content has been common knowledge for classic theme developers, end users of WordPress may not be familiar with that concept, and it can certainly be confusing when you learn about it. In other words, there is not a good reason to bother WordPress end users with having to use blocks correctly to make sure the loop is entered.

However, this means that for singular content, there is no coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. block available for handling the loop. Prior to 6.4, the loop was still being established for most cases even on singular content, but it was using a problematic workaround that forced the loop to start based on specific post blocks like “Content” and “Featured imageFeatured image A featured image is the main image used on your blog archive page and is pulled when the post or page is shared on social media. The image can be used to display in widget areas on your site or in a summary list of posts.” being rendered (core/post-content and core/post-featured-image). This workaround was not reliable as the blocks could also be intentionally used outside of the main query loop, and therefore led to other bugs, such as, #58027.

Current solution

To address the above, two changes [56507] and [57019] were made as part of WordPress 6.4 to automatically wrap the entire block template in a main query loop under the following circumstances:

  • The current main query is for singular content (via the is_singular() function).
  • There is indeed only a single post in the main query result.
  • The current block template is part of the current theme.
    • This is almost always the case. An exception is plugins that may inject their own block templates for specific content.
  • There post is still available to render (via the have_posts() function).

This is generally considered safe because in this situation the loop only contains the single post anyway that the current block template is for. A search through the WordPress 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 has not found relevant blocks where this would pose a problem. However, if you are developing a custom block plugin that makes specific assumptions about the main query loop, it is recommended that you check your block’s implementation to be compatible with this change in WordPress 6.4. This may especially be relevant for custom blocks that can be used alternatively to the “Query loop” block from WordPress core.

For example, if your custom block contains a main query loop similar to the example shown before, you could update it as follows to maintain compatibility with the new behavior:

if ( is_singular() && in_the_loop() ) {
	// Render the post.
} elseif ( have_posts() ) {
	while ( have_posts() ) {
		the_post();

		// Render the post.
	}
}

Please visit #58154 and #59225 for additional context on these changes.

Props to @gziolo and @webcommsat for peer review.

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

Framework for storing revisions of Post Meta in 6.4

RevisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision. are now supported for post 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. on an opt-in basis. This feature is currently used by coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. for footnotes – enabling footnotes to be correctly previewed, stored in autosaved and stored and retrieved from revisions. In the future, this may be expanded to the featured imageFeatured image A featured image is the main image used on your blog archive page and is pulled when the post or page is shared on social media. The image can be used to display in widget areas on your site or in a summary list of posts. which is also stored in meta.

When registering a new meta field with register_meta or register_post_meta, the $args parameter accepts a new revisions_enabled argument. This defaults to false—set to true to opt in to revisions for this meta field. Note that the object type the meta is being created for must have revision support in order for the meta field to have revisions enabled. Attempting to enable meta revisions for a post type that doesn’t support revisions will result in a doing_it_wrong warning.

This feature is designed to lay the groundwork for future improvements to GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ as part of Phase 3: Collaboration. Care has been made to do this in a backwards compatible manner, but if you are unhooking anything in the post save/update process, please read this entire note and test your code before WordPress 6.4 is released

A new wp_post_revision_meta_keys 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. is introduced, which can be used to filter the list of meta fields to be revisioned. The post type is also passed to the filter, which can be used to adjust revisioning of meta fields.

// Example to add a `my_product_price` meta field to revisions.
function enable_revisions_for_product_price_field( $revisioned_keys ) {
	if ( ! in_array( 'my_product_price', $revisioned_keys, true ) ) {
		$revisioned_keys[] = 'product_price';
	}
	return $revisioned_keys;
}
add_filter( 'wp_post_revision_meta_keys', 'enable_revisions_for_product_price_field' );

The _wp_put_post_revision action, which fires once a revision is saved, includes a new parameter—the post ID associated with the revision. This is used by core to copy the meta data from the main post to the revision.

The wp_creating_autosave action, which fires before an autosave is stored, includes a new parameter `$is_update`, added to indicate if the autosave is being updated or was newly created.

Meta revisions are also stored for autosaves and when previewing posts, fixing a long standing 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. (#20299) that caused live post meta to be overwritten when post meta was previewed. This is because using update_post_meta to save meta to a revision actually resulted in it being saved to the parent post. With this change, developers can now opt in to meta revisions and core will correctly attach the meta data to the revision. Meta that has revisions is also restored when restoring an autosave or revision. 

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/. revisions endpoint now returns meta when stored as part of a revision and the autosaves endpoint now handles storing of meta.

The new meta revision features are enabled in core via 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.; removing the hooks will disable the features:

To disable post meta revisions entirely, use:

remove_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );

To disable storing post meta on autosaves, use:

remove_action( 'wp_creating_autosave', 'wp_autosave_post_revisioned_meta_fields' );

To disable restoring meta when restoring revisions or autosaves, use:

remove_action( 'wp_restore_post_revision', 'wp_restore_post_revision_meta', 10, 2 );

Important note: as part of this change, the timing for the storing of post revisions has been changed. Previously, revisions were created before post meta had been saved by the REST API on the post_updated hook.  Revisions are now hooked on the wp_after_insert_post action, fired after post meta has been saved. 

Special backwards compatibility consideration has been given to plugins that may be unhooking post_updated from wp_save_post_revision to disable revisions—in that case, core ensures revisions are still not created.

Note that if you are querying for meta directly, your query may need to be adjusted to take into account the possibility of meta values tied to revisions by making sure your query includes the post_parent field.


Props to @westonruter, @jorbin and @webcommsat for reviewing.

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

WordPress 6.4 Field Guide

This guide shares more in-depth changes that you will find in 6.4 and is published in the Release Candidaterelease candidate One of the final stages in the version release cycle, this version signals the potential to be a final release to the public. Also see alpha (beta). cycle to help inform WordPress developers, extenders, and others. The release squad and many contributors across the global project have worked to bring these changes, and you can follow this work and add to the contribution using the ticketticket Created for both bug reports and feature development on the bug tracker. systems in Trac and GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/.

Some fascinating stats! TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. has about 268 tickets, 113 of which are enhancements and feature requests, 134 bug fixes, and 21 other tasks. This time, there are more than 64 tickets with a focus on performance, 17 for accessibility, and 16 for modernizing code and applying coding standards. GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/’s GitHub repo brings more than 1,400 updates/changes, providing to coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. more than 420 enhancements, 445 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. fixes, and 42 accessibility improvements.

Changes in 6.4 are spread across about 45 core components. Below is the breakdown of the most important ones.

You can also find all Developer Notes relating to 6.4 as they continue to be published until the release goes live. You can follow them using this tag.

Updated Nov 14, 2023: overview posts available on accessibility and performance improvements in 6.4.

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

WordPress 6.4 is bringing six Gutenberg releases into the core – 16.2, 16.3, 16.4, 16.5, 16.6, 16.7. You will find Block 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., the ability to register their own media categories, changes to the @wordpress/components package, updates for the user interface components, and many other changes.

WordPress 6.4 introduces Block Hooks (#53987), a feature that provides an extensibility mechanism for Block Themes. This is the first step in emulating WordPress’ Hooks concept that allows developers to extend Classic Themes using filters and actions.

From WordPress 6.4, extenders can register their own inserter media categories and provide users with more options from which to choose.

6.4 brings in a number of notable changes to the @wordpress/components package.

There are a number of other changes, including a new background image block support, fluid typography, disabled layout controls globally or on a block basis by theme.json, Stabilized APIs for InnerBlocks, and much more.

New 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. (November 13, 2023) – For singular content, the output of block templates, for example, (single.html or page.html) will be automatically wrapped in the main query 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..

Adminadmin (and super admin) notices

Two new functions abstract the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. markup generation to reduce the maintenance burden, encourage consistency, and enable argument and message filtering for all admin notices used widely in WordPress Core and the extender community.

General

A developer note will be added later on the following change:

Introduce wp_trigger_error() to complement _doing_it_wrong() #57686

HTML 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.

WordPress 6.4 includes continued development of the HTML API, including the introduction of a minimal HTML Processor with the concept of breadcrumbs, and makes it possible to, for example, search for images that are direct children of a DIV.

Also included in 6.4, is the addition of a couple of CSSCSS Cascading Style Sheets./class helpers in the Tag Processor, which will make it possible to search for a 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.) containing more than one class name, or to search for a tag not containing a given class name.

Media

New WordPress installations will now have attachment pages fully disabled for new sites. This will benefit SEO by avoiding attachment pages created by default, which were indexed by search engines and could have led to bad results for users and site owners. The change introduces a wp_attachment_pages_enabled database option to control the attachment pages’ behavior. In the dev note, there is information on how to update existing sites.

Additional performance improvements

A significant part of the 6.4 release brings performance improvements and greater efficiency to WordPress. An overview post on performance improvements in 6.4 is also available.

New functions get_options(), wp_prime_option_caches(), and wp_set_option_autoload_values() allow an enhanced performance of retrieving options from the database.

WordPress 6.4 brings many improvements to template loading.

  • Performance gains have been achieved by introducing caching using an object cache in a new method called WP_Theme::get_block_patterns().
  • Unnecessary checks were removed if a theme file existed in the theme functions that enhanced efficiency and performance. These improvements in the Themes API mean the current theme’s stylesheet directory is checked to ensure it matches the template directory, before further file existence checks are done. Improvements are also in the performance of get_block_theme_folders(). This is through a new method, WP_Theme::get_block_template_folders(), and improved error handling. The result is a quicker and more efficient lookup of block template folders within themes. WordPress developers and users can anticipate improved performance, reduced I/O overhead, and a smoother experience when working with block themes.

Improved image loading

Several enhancements to the wp_get_loading_optimization_attributes() function, which provides a central place to manage loading optimization attributes, specifically for images and iframes. 

Script Loader

In WordPress 6.4, script loading strategies are now employed for frontend scripts in core and bundled themes. For the most part, the defer loading strategy is used since it is more consistent in its loading behavior, in that a defer script always executes once the DOM has loaded; a script with async may actually block rendering if it is already cached. Additionally, loading with defer has been moved from the footer to the head so that they are discovered earlier while the document is loading and can execute sooner once the document is loaded.

Style loading

This dev note highlights the changes made in WordPress 6.4 to style loading. The main focus of the changes was to replace manually created style tags printed at the wp_head action with calls to wp_add_inline_style().

More performance-related changes

  • 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.: The double sanitization in the get_term function has been stopped. This will prevent the unnecessary calls to sanitize_term, which was detrimental to performance. Trac ticket #58329.
  • Themes: The TEMPLATEPATH and STYLESHEETPATH constants have been deprecated. get_template_directory() and get_stylesheet_directory() should be used instead. Trac ticket #18298

And there’s more!

Some of the additional changes in 6.4 to highlight.

External libraries

jQuery has been updated to version 3.7.1. This release fixes a regressionregression A software bug that breaks or degrades something that previously worked. Regressions are often treated as critical bugs or blockers. Recent regressions may be given higher priorities. A "3.6 regression" would be a bug in 3.6 that worked as intended in 3.5. from jQuery 3.6.0 that resulted in rounded dimensions for elements in Chrome and Safari. Also, a (mostly) internal Sizzle method, jQuery.find.tokenize that was on the jQuery object was accidentally removed when they removed Sizzle in jQuery 3.7.0. That method has been restored.

Trac Ticket #59322

Users

WordPress 6.4 brings a number of key improvements to the HTML markup of the wp-login.php page to make its structure more optimal and allow developers to have more customized individual styling flexibility. #30685

Clarify the “Add New” links in the Admin for better accessibilityAccessibility Accessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility)

In WordPress 6.4, the default values for the add_new label changed to include the type of content. This matches add_new_item and provides more context for better accessibility. The default value is ‘Add New Type’ for both hierarchical and non-hierarchical types. If you’ve previously used a label such as:

'add_new' => _x( 'Add New', 'Book', 'my-plugin' )

you are encouraged to change it to

'add_new' => __( 'Add New Book', 'my-plugin' )

Trac ticket #47125

Props to @swissspidy for this dev note.

Integration testing requirement change

PHPUnit Polyfills 1.1.0 is required for running integration tests with WordPress 6.4.

Trac ticket #59510

Props to @jrf for the information for this dev note.

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. API

  • HTTP API: WP_Http_Curl and WP_Http_Streams classes and filters have been deprecated as these classes have not been used in WordPress Core since the introduction of the Requests library. Trac ticket #58705

RevisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision.

Revisions are now supported for post 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. on an opt-in basis. Trac ticket #20564


Props to @costdev, @bph, @nalininonstopnewsuk, @codente, @spacedmonkey, @desrosj, @flixos90 for input on this Field GuideField guide The field guide is a type of blogpost published on Make/Core during the release candidate phase of the WordPress release cycle. The field guide generally lists all the dev notes published during the beta cycle. This guide is linked in the about page of the corresponding version of WordPress, in the release post and in the HelpHub version page., and @joemcgill, @clarkeemily, @cbringmann, @akshaya, @611shabnam, and @priethor for peer review.
Thank you to all those involved in collating, writing, and editing developer notes relating to 6.4.

#6-4, #dev-notes, #dev-notes-6-4, #field-guide

Updates to the HTML API in 6.4

WordPress 6.4 includes continued development of the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. 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., including the introduction of a minimal HTML Processor in #58517 and the addition of a couple of CSSCSS Cascading Style Sheets./class helpers in the Tag Processor in #59209.

A minimal HTML Processor and its breadcrumbs.

When the HTML Processor was introduced in WordPress 6.2, it carried the stipulation that it did not understand HTML structure. While this is an intentional limitation so that the Tag Processor can maintain predictable performance and simplicity, it leaves certain conditions awkward and 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. prone. Consider, for example, the goal of finding an IMG element inside a surrounding DIV.

$processor = new WP_HTML_Tag_Processor( $html );

// Find the wrapping DIV element.
if ( ! $processor->next_tag( 'DIV' ) {
    return $html;
}

// Find the inner IMG element.
while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
    // Abort if leaving the DIV wrapper before finding the image.
    if ( 'DIV' === $processor->get_tag() && $processor->is_tag_closer() ) {
        return $html;
    }

    if ( 'IMG' === $processor->get_tag() ) {
        do_something_to_img();
        return $processor->get_updated_html();
    }
}

This code maintains the safety against unexpected HTML inputs in the way that a regex-based approach would not, but it’s already a bit cumbersome, and could be mistaken by an unexpected HTML layout. For example, it assumes that a closing DIV 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.) will appear and that it will only appear after the IMG. It will fail to recognize the nested IMG in the following HTML because of the nested DIV before it.

<div class="profile">
    <div class="name">WordPress</div>
    <img src="wordpress-logo.png">
</div>

The new WP_HTML_Processor() class is being built in order to not only understand HTML syntax, but also its semantics – it understands HTML structure and all the quirks involved when discussing “malformed HTML.” In WordPress 6.4 the HTML Processor is available, and it adds a new concept of “breadcrumbs” to the API.

Breadcrumbs represent the path into an HTML document for a given element and will be familiar to those who navigate around a document in the browser development tools.

A browser displays the breadcrumbs for a selected element when inspecting the document.

Breadcrumbs always start with HTML as the start of the path, will list every ancestor of a given element, and include the element itself as the last segment in the path. The HTML Processor introduces a few ways to use these:

  • It’s possible to search for this structure by supplying array( 'breadcrumbs' => $breadcrumbs ) inside a call to next_tag().
  • The get_breadcrumbs() method reports the entire breadcrumb array from HTML to the matched element.
  • The matches_breadcrumbs( $breadcrumbs ) method indicates whether the matched element can be found at the given breadcrumbs.

Using the example above, it’s possible to search for images that are direct children of a DIV.

$processor = WP_HTML_Processor::create_fragment( $html );

if ( ! $processor->next_tag( array( 'breadcrumbs' => array( 'DIV', 'IMG' ) ) ) {
    return $html;
}

do_something_to_img();
return $processor->get_updated_html();

Properly handling this structure comes with additional costs, so the HTML Processor cannot provide the predictable performance that the Tag Processor can. It should still be fast enough to use though when rendering a 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.’s output.

One important note about using the HTML Processor is that it’s a work-in-progress and only supports a subset of allowable HTML. If it encounters a tag or a specific kind of HTML it doesn’t support, then it will abort processing to avoid corrupting your content. The HTML Processor reports if it gave up with the get_last_error() method. Currently, there’s only a small set of HTML that the HTML Processor supports, so don’t be surprised when it aborts before finding the tag you’re looking for. Each WordPress release will expand this support until it can read all possible HTML documents.

if ( $processor->next_tag( 'breadcrumbs' => array( 'figure', 'img' ) ) ) {
    // The tag was found in the HTML document.
    do_something_to_img();
    return $processor->get_updated_html();
}

if ( null === $processor->get_last_error() ) {
    // The tag was not in the HTML document.
} else {
    // It was not possible to determine if the tag was in the HTML document.
}

For more complicated queries, the matches_breadcrumbs() method can be used inside a next_tag() 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.. The * value is a special wildcard term. It only matches one of any tag, so if no open element exists in its place then the match fails.

while ( $processor->next_tag( 'IMG' ) ) {
    // Skip images that are already inside a FIGURE element.
    if ( $processor->matches_breadcrumbs( array( 'FIGURE', 'IMG' ) ) ) {
        continue;
    }

    // Only process images that are great-grand-children of a BLOCKQUOTE element.
    if ( $processor->matches_breadcrumbs( array( 'BLOCKQUOTE', '*', '*', 'IMG' ) ) ) {
        do_something_to_img();
    }
}

The HTML Processor is a subclass of the Tag Processor and so retains all the underlying methods to read and modify the HTML. In the future, it will be possible to insert and remove entire tags and to read and modify the inner markup inside a tag. In WordPress 6.4, however, the only new feature is the concept of breadcrumbs.

CSS helpers for the Tag Processor

It’s been possible to search for a tag containing a specific class with next_tag( array( 'class_name' => $class_name ) ) but not possible to search for a tag containing more than one class name, or to search for a tag not containing a given class name. In WordPress 6.4, it’s possible to do this with the new has_class() method. This method does exactly what it sounds like: it reports if a matched tag contains the given class name in its class attribute.

$processor = new WP_HTML_Processor( $html );

while ( $processor->next_tag() ) {
    // Skip an element if it's not supposed to be processed.
    if ( $processor->has_class( 'data-wp-ignore' ) ) {
        continue;
    }

    if ( $processor->has_class( 'data-wp-context' ) && $processor->has_class( 'active' ) ) {
        // Process the context…
    }
}

The has_class() method knows how to split CSS class names properly. It’s valuable to rely on this method instead of manually processing the class attribute value to avoid several common pitfalls, especially when wanting to know about the presence or absence of multiple class names.


Props to @bph @codente @webcommsat and @nalininonstopnewsuk for peer review.

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

Image loading optimization enhancements in 6.4

WordPress 6.4 comes with several enhancements to the wp_get_loading_optimization_attributes() function which was introduced in 6.3 as a central place to manage loading optimization attributes, specifically for images and iframes.

Quick recap: Loading optimization attributes are those such as loading="lazy" or fetchpriority="high", which can be added to certain HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. tags to optimize loading performance in the browser.

Simplified complex logic

The logic of the wp_get_loading_optimization_attributes() is generally quite complex, as it caters for several factors that influence when to apply certain loading optimization attributes. When the function was originally introduced, it was particularly hard to follow, since it included lots of early returns as well as closures. This was mostly due to maintaining its original code paths that came from the deprecated wp_get_loading_attr_default() function as much as possible to avoid breakage.

While the function logic is still complex in WordPress 6.4, it has been notably simplified, taking a more sequential and thus easier to follow approach. This facilitated the implementation of further enhancements such as those outlined below.

Please refer to 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. #58891 for additional details on these changes.

Managing the decoding="async" attribute

The decoding="async" attribute has been present on images by default since WordPress 6.1 (see Trac ticket #53232). As of WordPress 6.4, the logic for applying the attribute has been consolidated in the wp_get_loading_optimization_attributes() function, as the attribute is a perfect fit for that function.

Deprecated function

As part of that change, the wp_img_tag_add_decoding_attr() function has been deprecated, as its logic is now incorporated into wp_img_tag_add_loading_optimization_attrs(). Unless you are using the now deprecated function in your code, no changes should be needed after this update in regards to the decoding="async" attribute. The change merely enables control over the attribute in a more consistent manner (also see the new filters introduced below).

If you are using the deprecated function and want to use a fully backward compatible replacement, you can implement a custom wrapper function such as the following:

function myplugin_img_tag_add_decoding_attr( $image, $context ) {
	global $wp_version;

	// For WP >= 6.4.
	if ( version_compare( $wp_version, '6.4', '>=' ) ) {
		$image = wp_img_tag_add_loading_optimization_attrs( $image, $context );

		// Strip potential attributes added other than `decoding="async"`.
		return str_replace(
			array(
				' loading="lazy"',
				' fetchpriority="high"',
			),
			'',
			$image
		);
	}
 
    // For WP < 6.4.
    return wp_img_tag_add_decoding_attr( $image, $context );
}

Please refer to Trac ticket #58892 for additional details on these changes.

New filters to control loading optimization attributes

With WordPress 6.4, two filters have been added to wp_get_loading_optimization_attributes() which allow modifying or completely overriding the logic used to apply the loading optimization attributes:

  • The wp_get_loading_optimization_attributes 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 modify the results from the WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. logic.
  • The pre_wp_get_loading_optimization_attributes filter can be used to use entirely custom logic and effectively short-circuit the core function by returning a value other than false.

Below are a few examples on how these two filters could be used.

Filter usage examples

You could use the wp_get_loading_optimization_attributes filter to ensure a specific image within post content receives the fetchpriority="high" attribute while other ones do not:

function set_fetchpriority_high_on_specific_image( $loading_attrs, $tag_name, $attr, $context ) {
	if ( 'img' === $tag_name ) {
		if (
			'the_content' === $context &&
			isset( $attr['src'] ) &&
			$attr['src'] === 'https://example.org/a-specific-image.jpg'
		) {
			$loading_attrs['fetchpriority'] = 'high';
		} else {
			unset( $loading_attrs['fetchpriority'] );
		}
	}
	return $loading_attrs;
}
add_filter(
	'wp_get_loading_optimization_attributes',
	'set_fetchpriority_high_on_specific_image',
	10,
	4
);

Alternatively, you could use the wp_get_loading_optimization_attributes filter to disable adding the fetchpriority="high" attribute entirely:

function disable_fetchpriority_high( $loading_attrs ) {
	unset( $loading_attrs['fetchpriority'] );
	return $loading_attrs;
}
add_filter(
	'wp_get_loading_optimization_attributes',
	'disable_fetchpriority_high'
);

You could implement entirely custom logic 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 to detect which images appear in the viewport using client-side 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/. logic or an external service and replace the function’s default logic with that:

function override_loading_optimization_attributes( $override, $tag_name, $attr, $context ) {
	// Bail if another filter callback already overrode this.
	if ( false !== $override ) {
		return $override;
	}

	// Use custom logic to determine whether image is LCP and whether it appears above the fold.
	if ( 'img' === $tag_name ) {
		$is_lcp      = custom_function_to_detect_whether_image_is_lcp_element( $attr );
		$in_viewport = custom_function_to_detect_whether_image_is_above_the_fold( $attr );

		/*
		 * Always add `decoding="async"`.
		 * Add `fetchpriority="high"` only to the LCP image.
		 * Add `loading="lazy"` to any image below the fold / outside the viewport.
		 */
		$loading_attrs = array( 'decoding' => 'async' );
		if ( $is_lcp ) {
			$loading_attrs['fetchpriority'] = 'high';
		} elseif ( ! $in_viewport ) {
			$loading_attrs['loading'] = 'lazy';
		}
		return $loading_attrs;
	}

	return $override;
}
add_filter(
	'pre_wp_get_loading_optimization_attributes',
	'override_loading_optimization_attributes',
	10,
	4
);

Please refer to Trac ticket #58893 for additional details on these changes.

Support for custom context values

As explained in the 6.3 dev note for the wp_get_loading_optimization_attributes() function, initially it only supported specific context values used by WordPress core. This made it confusing to write custom code making use of that function as you would have been forced to use a context string used elsewhere in core in order to get the performance benefits.

This limitation has been addressed in WordPress 6.4: The function now supports arbitrary contexts, and for the most part does not apply context-specific optimizations. There are two exceptions to this which are the “template_part_header” and “get_header_image_tag” contexts. Images within these contexts will always be interpreted to be in the 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.. The list of these two context strings is filterable with a new wp_loading_optimization_force_header_contexts filter, which allows images with other contexts to be always assumed to appear above the fold.

Context filter usage example

Below is an example: Assume that your plugin has a particular header image rendering function where it is safe to assume any image rendered with that function appears above the fold. You can rely on an arbitrary context specific to your function and then force WordPress core to consider images with that context to appear above the fold.

function force_myplugin_special_header_context_above_the_fold( $header_contexts ) {
	$header_contexts['myplugin_special_header'] = true;
	return $header_contexts;
}
add_filter(
	'wp_loading_optimization_force_header_contexts',
	'force_myplugin_special_header_context_above_the_fold'
);

Your image rendering function then needs to call wp_get_loading_optimization_attributes( 'img', $attr, 'myplugin_special_header' ) for the above filter to take effect.

Please refer to Trac ticket #58894 for additional details on these changes.

Hook priority change for wp_filter_content_tags()

The wp_filter_content_tags() was originally introduced in WordPress 5.5 as the foundation for lazy-loading as well as other optimizations for certain tags in a content blob. More recently a problem was identified that the function is called earlier than do_shortcode(), which means that any images added by shortcodes will not be able to make use of the performance benefits from wp_filter_content_tags().

To address that limitation, the hook priority with which wp_filter_content_tags() is hooked into the various filters (“the_content”, “the_excerpt”, “widget_text_content”, and “widget_block_content”) has been changed from the default 10 to 12. For context, the do_shortcode() function has hook priority 11.

While this is technically a breaking change, careful consideration and research across the WordPress plugin directory went into this decision. No plugins in the directory are affected by this change, given that there is only limited direct usage of the wp_filter_content_tags() function, and such usage has not been specific to the core filters. Still, in case you use the wp_filter_content_tags() function directly in your code, you may want to double check that this hook priority change does not result in a problem.

If your plugin processes custom content with the wp_filter_content_tags() function, it is encouraged to call that function after parsing other content such as blocks and shortcodes. The core change will not cause problems if that is currently not the case in your code, however it is recommended to have your custom logic follow a similar order.

Please refer to Trac ticket #58853 for additional details on these changes.

Props to @westonruter and @webcommsat for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

New option functions in 6.4

WordPress 6.4 introduces a number of new functions related to options, with a particular focus on autoloaded options.

While options are autoloaded by default, based on the $autoload parameter of add_option() and update_option(), autoloading too many options is a common cause for slow server response time as well as bugs while using a persistent object cache.

To help 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 developers improve managing their plugins’ options, their performance, and whether to autoload them, two sets of functions are being introduced in 6.4:

  • wp_prime_option_caches() and related wrapper functions can be used to fetch multiple options with a single database query. Update: When this 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. was first published, the function was named prime_options(), however that name was since revised to wp_prime_option_caches().
  • wp_set_option_autoload_values() and related wrapper functions can be used to update one or more specific options’ autoload values, independently of their option values.

New functions to retrieve multiple options in a single database query

The wp_prime_option_caches( $options ) function expects an array of option names, and then fetches any options that aren’t already cached with a single database query. The options are then stored in the cache so that subsequent get_option() calls for any of those options do not result in separate database queries.

Note that wp_prime_option_caches() does not return the options as its sole responsibility is to update the relevant caches. In order to actually retrieve multiple options at once, a new wrapper function get_options( $options ) has been introduced. It calls wp_prime_option_caches() for the option names given and then get_option() for every individual option, returning an associative array of option names and their values.

Last but not least, a third function wp_prime_option_caches_by_group() is being introduced, which primes all options of a specific option group (the mandatory first parameter of register_setting()). This can be helpful for plugins to fetch all options of a specific option group the plugin uses.

All of the above functions have been introduced not only as a performant way to retrieve multiple options from the database, but also as an alternative to autoloading options that are only needed in a few specific areas. For example, a plugin that currently autoloads a few options which are only used on the plugin’s own WP Adminadmin (and super admin) screen would be encouraged to use the new wp_prime_option_caches() or wp_prime_option_caches_by_group() instead of autoloading the options.

Example

In this example, we assume a plugin has a WP Admin screen that relies on four options: “myplugin_foo”, “myplugin_bar”, “myplugin_foobar”, and “myplugin_barfoo”. All of these options are only used on that admin screen and therefore should not be autoloaded. In other words, any add_option() and update_option() calls for those options should provide “no” for the $autoload parameter.

function myplugin_prime_admin_screen_options() {
	/*
	 * By priming the options here, no further database queries will be used
	 * when later calling `get_option()`.
	 */
	wp_prime_option_caches(
		array( 'myplugin_foo', 'myplugin_bar', 'myplugin_foobar', 'myplugin_barfoo' )
	);
}

function myplugin_add_admin_screen() {
	$hook_suffix = add_menu_page( /* Menu page arguments. */ );
	add_action( "load-{$hook_suffix}", 'myplugin_prime_admin_screen_options' );
}
add_action( 'admin_menu', 'myplugin_add_admin_screen' );

This code would ensure that the options are retrieved in a single database query. Any subsequent get_option() calls for these options would then not result in another database query and thus be extremely fast. As such, the WP Admin screen is just as performant as it would have been with autoloading those options, yet without unnecessarily slowing down performance of the entire site.

To further enhance that example, the plugin could rely on a single option group for which it registers those options. Here is the example code for that:

function myplugin_register_settings() {
	register_setting(
		'myplugin_admin_screen',
		'myplugin_foo',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_bar',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_foobar',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_barfoo',
		array( /* Registration arguments. */ )
	);
}
add_action( 'init', 'myplugin_register_settings' );

function myplugin_prime_admin_screen_options() {
	/*
	 * By priming the options here, no further database queries will be used
	 * when later calling `get_option()`.
	 */
	wp_prime_option_caches_by_group( 'myplugin_admin_screen' );
}

// `myplugin_add_admin_screen()` would remain as in the previous code example.

With that adjustment, the option registration would be the central place to manage the options, and the myplugin_prime_admin_screen_options() function would remain simple without maintaining a list of the exact options.

Please refer to 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. #58962 for additional details on these changes.

New functions to set option autoload values

The wp_set_option_autoload_values( array $options ) function expects an associative array of option names and their autoload values to set, and then updates those options’ autoload values in a single database query.

Additionally, two wrapper functions for the above are also being introduced for ease of use:

  • wp_set_options_autoload( $options, $autoload ) can be used to set multiple options to the same autoload value.
  • wp_set_option_autoload( $option, $autoload ) can be used to set the autoload value for a single option.

These functions can be useful in a plugin deactivation hook: After deactivation, the plugin’s options won’t be used anymore, yet they should not be deleted from the database until the user decides to uninstall the plugin.

Example

In this example, we assume a plugin has two options “myplugin_foo” and “myplugin_bar”, both of which are used in various frontend page loads and therefore autoloaded by default. To properly clean up after itself, such a plugin could implement usage of the new functions as follows:

function myplugin_activate() {
	wp_set_options_autoload(
		array( 'myplugin_foo', 'myplugin_bar' ),
		'yes'
	);
}
register_activation_hook( __FILE__, 'myplugin_activate' );

function myplugin_deactivate() {
	wp_set_options_autoload(
		array( 'myplugin_foo', 'myplugin_bar' ),
		'no'
	);
}
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

This code would ensure that the options are no longer autoloaded when the plugin has been deactivated. If the plugin gets (re-)activated, the options will be set to autoload again if they are already in the database.

Please refer to Trac ticket #58964 for additional details on these changes.

Related autoload 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. fix for update_option()

While not directly tied to the above functions, it is worth noting that a bug relevant to autoloading has been addressed in 6.4: When using the $autoload parameter of update_option() alongside an option value update, the function would update the incorrect cache, not respecting the new autoload value. This could have severe implications such as returning a stale option value when the option in fact had already been deleted.

This bug has been fixed in 6.4, so that the $autoload parameter of update_option() can now be reliably used to change the option’s autoload value. It should be noted though that depending on the use-case the above functions to set option autoload values may be more suitable.

Please refer to Trac ticket #51352 for additional details on this bug fix.

Props to @westonruter and @webcommsat for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

Replacing hard-coded style tags with wp_add_inline_style()

This 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. highlights the changes made in WordPress 6.4 to style loading. The main focus of the changes was to replace manually created style tags printed at the wp_head action with calls to wp_add_inline_style(). This change was implemented to address issues related to redundant code and bypassing the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.’s style enqueuing system, which made it challenging for third-party developers to manage and control the output of style tags.

Deprecated Functions

To maintain backward compatibility, the following functions have been deprecated and replaced with the new approach:

  • print_embed_styles()
  • print_emoji_styles()
  • wp_admin_bar_header()
  • _admin_bar_bump_cb()
  • the_block_template_skip_link()

Backwards Compatibility Unhooking

Previously, when wanting to unhook certain functions like print_embed_styles() from happening at wp_print_styles(), a theme or 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 would do:

remove_action( 'wp_print_styles', 'print_emoji_styles' );

In 6.4 this print_emoji_styles() function is now deprecated. Nevertheless, the above method for preventing emoji styles from being printed is retained, even though they are now being printed by wp_enqueue_emoji_styles(). This applies to the other deprecated functions as well, so no developer action is required.

Developer Action Required

For developers who are currently using the wp_print_styles() function, whether it’s in unit tests or within their own code, some adjustments may be necessary to ensure a smooth transition. You should follow the example set by GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/. This is what you need to do:

/**
 * Remove the deprecated `print_emoji_styles` handler.
 * It avoids breaking style generation with a deprecation message.
 */
$has_emoji_styles = has_action( 'wp_print_styles', 'print_emoji_styles' );
if ( $has_emoji_styles ) {
    remove_action( 'wp_print_styles', 'print_emoji_styles' );
}

ob_start();
wp_print_styles();
$styles = ob_get_clean();

if ( $has_emoji_styles ) {
    add_action( 'wp_print_styles', 'print_emoji_styles' );
}

This code snippet demonstrates how to handle the situation. It first checks if there’s an action hooked to wp_print_styles for print_emoji_styles(). If it does exist, it removes the action temporarily to avoid issues with style generation.

By following this approach, you can ensure that your code remains compatible with the changes introduced in this commit while avoiding any disruptions to style generation. It’s recommended to review and adjust your code accordingly if you’ve been using the wp_print_styles() function.

Exceptions

In the case of the functions for printing custom backgrounds and custom styles, converting them to use inline styles was deemed infeasible. Changing the style 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.) IDs in this context could potentially disrupt 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/. functionality for several plugins in the repository. Therefore, these functions remain unaffected by this change.

Please refer to #58775 for additional details on these changes.

Props to @spacedmonkey for writing the dev note.
Props to @westonruter, @flixos90, @webcommsat, and @bph for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

Improvements to Object Caching in WordPress 6.4

In WordPress 6.4, the Performance team has introduced several enhancements centered around object caching, leading to better handling of filters, reduced database queries, and improved overall system efficiency. 

Change the position of notoptions lookup in get_option()

In the get_option() function, a cache lookup for the notoptions key is performed; this stores an array of keys for options known to not exist. This optimization prevents repeated database queries when certain options are requested. However, the cache lookup for notoptions was conducted before checking if the requested option exists in the cache. Given that it’s more likely that the option does exist, a change has been made to reorder the checks to first verify the option’s existence in the cache before confirming its absence. This adjustment reduces redundant queries and also eliminates an unnecessary cache lookup, improving overall performance.

Please refer to #58277 for additional details on these changes.

Improvements to WP_Query caching

Forcing split queries when object caching is enabled 

WordPress introduced the ability to perform split queries starting from version 3.1. A split query occurs when the main query in WP_Query is executed to retrieve post IDs only, rather than retrieving the entire post objects. To populate the post objects, the function _prime_post_caches() is invoked, fetching the complete post information if it is not already loaded into local memory.

Before this change, split queries were restricted to posts numbering less than 500. However, this restriction has been eliminated when persistent/external object caching is enabled. 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. becomes even more significant with the introduction of wp_cache_get_multiple() in WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. With this function, object caches can retrieve multiple objects in a single request, offering several benefits.

Benefits of Split Queries and Object Caching:

  • Performance Improvement: Object caches, which store frequently accessed data in memory, are substantially faster than traditional database queries. By using split queries, WordPress can fetch post IDs more efficiently, reducing the load time for your website.
  • Database Offloading: The implementation of split queries alleviates the strain on the database. With only the essential post IDs selected, less data needs to be retrieved from the database, and fewer database queries are executed.
  • Primery Key Loading: The primary key for loading the post object is the focus, ensuring that the necessary data is efficiently fetched from the object cache.

In summary, the ability to perform split queries, combined with object caching, enhances WordPress’s performance and reduces the load on your database. By opting for split queries, you can enjoy faster loading times and a smoother user experience for your WordPress website.

Please refer to #57296 for additional details on these changes.

Fix caching behavior if filters are used in WP_Query

The WP_Query class in WordPress allows developers to customize queries using various filters such as posts_fields_request, posts_request, and the_posts. These filters are instrumental in modifying both the queried fields and retrieved post objects. However, there have been cases where the use of these filters could result in incomplete or 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. post objects, lacking essential data. To address this issue and ensure data consistency and integrity, a change has been introduced.

In scenarios where the posts_fields_request, posts_request, or the_posts filters are active during a query, the get_posts() method now avoids caching post objects with the usual update_post_caches() function call. Instead, it opts for a call to _prime_post_caches(). This change is designed to prevent the caching of invalid post objects, prioritizing data consistency and integrity in filtered query scenarios.

While this enhancement ensures that invalid post objects are not cached, it may occasionally trigger new database queries to prime the post data cache. Developers should be aware that this might result in slightly increased database load in specific cases.

Please refer to #58599 for additional details on these changes.

Improved performance for WP_Query for id=>parents

In WordPress 6.2, a change was made to the WP_Query class (as documented in commit [53941]) which brought forth an unforeseen issue when querying to return fields id=>parent. This issue specifically affected websites with object caching enabled, especially when dealing with a substantial number of pages. During the second execution of this query, the _prime_post_caches() function for an id=>parent query was inadvertently triggered. This led to the unnecessary priming of post, 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., and term caches, even when the query only requested ID and parent information.

To address this problem and optimize query performance, a new function, _prime_post_parent_ids_caches(), has been introduced. This function is responsible for priming a dedicated cache for post parents during the initial query execution. Subsequently, the wp_cache_get_multiple() function is utilized to retrieve all post parent data in a single object cache request, ensuring a significant performance improvement. Post parent caches are invalidated when the clean_post_caches() function is called. If you are updating a post in the database, please ensure that this function is called after making this update. 

Please refer to #59188 for additional details on these changes.

Improvements to WP_Term_Query caching

New cache_results parameter in WP_Term_Query

The cache_results parameter has been introduced in WordPress to provide developers with more control over the caching behavior in WP_Term_Query queries. It allows you to determine whether or not to load results from the query cache, enabling you to obtain a complete uncached result when set to false.

The cache_results parameter is a boolean parameter, and it accepts the following values:

  • true (default): Load results from the query cache (cached results).
  • false: Retrieve a complete uncached result.

This parameter is primarily designed for developers who are working on highly custom solutions and need to bypass caching mechanisms for specific queries. In most cases, it is recommended to keep the cache_results parameter enabled (set to true) in production environments. Caching is an essential performance optimization in WordPress, and disabling it may lead to slower query execution times.

In the example above, the cache_results parameter is set to false, ensuring that the query results are retrieved without using the cache.

$args = array(
    'taxonomy' => 'category',
    'cache_results' => false, // This will fetch uncached results
);

$term_query = new WP_Term_Query($args);

The cache_results parameter in WP_Term_Query offers flexibility for developers working on specialized projects. While it can be useful in certain scenarios, exercise caution when disabling caching, as it may impact performance in a production environment. Always consider the specific requirements of your project before deciding to disable query caching.

Please refer to #52710 for additional details on these changes.

Fix caching behavior if term_clauses filters are used

When utilizing the terms_clauses or get_terms_fields filters within WP_Term_Query and the selected fields are modified, the entire term object is now cached. This change was necessary because filters can broaden the selected fields beyond just the term ID. Fields linked to the term object, such as the count or parent, may undergo modifications when queried. Caching the complete object ensures the accurate storage of these modified fields within the cache.

Please refer to #58116 for additional details on these changes.


Props to @spacedmonkey for writing, @westonruter for editing.
Props to @joemcgill, @tillkruss, @flixos90 @webcommsat for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

Improvements to Template Loading in WordPress 6.4

Improve performance of _register_theme_block_patterns() function

The _register_theme_block_patterns() function previously introduced a substantial resource overhead issue, especially noticeable when themes, such as Twenty Twenty Four, registered a large number of 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. patterns. This overhead was mainly due to the extensive file operations required by the function, including file system checks and file reads.

To address these performance issues, we have introduced caching using object cache in a new method called WP_Theme::get_block_patterns(). When theme development mode is disabled and a theme exists, the block patterns are stored in object cache. For sites using a persistent object cache, subsequent requests will use this cached data, eliminating the need for file lookups and reading files into memory, resulting in significant performance improvements.

It’s important to note that caches are also tied to the theme’s version number. Therefore, if you want to manually invalidate the cache, you can do so by changing the theme’s version number. This provides an additional method for cache management, especially useful during theme updates or changes.

Developers can bypass the pattern cache by enabling development mode for a theme, allowing for pattern additions and removals without the cache interfering. This is beneficial for accurate testing and development.

Cache invalidation occurs when themes are switched, the WP_Theme::cache_delete() method is called, or when the theme’s version number is updated. These mechanisms ensure that block patterns are not stored in the cache incorrectly.

Remove unnecessary checks if a theme file exists in the theme functions

In [56523] and [56357], WordPress theme functions were improved by removing unnecessary file existence checks, thereby enhancing performance and efficiency.

Background:
Previously, several functions and methods in the Themes 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. were designed to check for the existence of files within a child themeChild theme A Child Theme is a customized theme based upon a Parent Theme. It’s considered best practice to create a child theme if you want to modify the CSS of your theme. https://developer.wordpress.org/themes/advanced-topics/child-themes/. before falling back to the parent theme. 

However, these checks didn’t take into account whether the current theme was a child theme or not, leading to redundant file existence checks for non-child themes. This could result in unnecessary file system access and potentially slow down WordPress websites, especially in cases where there were numerous checks.

To address this issue and improve the overall performance of the Themes API, we’ve introduced an optimization that checks whether the current theme’s stylesheet directory matches the template directory before proceeding with file existence checks. This optimization significantly reduces the number of unnecessary file system accesses, as file existence checks can be resource-intensive in PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher.

As part of 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., we’ve updated the following functions and methods in WordPress:

  • WP_Theme::get_file_path()
  • get_theme_file_path()
  • get_theme_file_uri()
  • locate_template()

With these optimizations in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress., you can expect a noticeable improvement in the performance of your WordPress theme, especially if you have a non-child theme. The reduction in unnecessary file system access can lead to faster page load times and a smoother user experience.

Tickets: #59279, #58576

Improve performance of get_block_theme_folders()

The get_block_theme_folders() function suffered from suboptimal performance due to repeated file lookups using the file_exists() function. This resulted in unnecessary I/O operations and slowed down the process of locating block template folders within themes.

To address these performance issues and improve the efficiency of the get_block_theme_folders() function, the following changes have been implemented:

  • A new method, WP_Theme::get_block_template_folders(), has been added which incorporates basic caching, storing the result in the theme’s cache, similar to how block themes are cached in the block_theme property (as outlined in [55236]). The introduction of caching significantly reduces redundant file system lookups and optimizes the overall performance of the function.
  • Improved Error Handling: This update enhances error handling by first verifying the existence of a theme before attempting to look up the file. This proactive error handling approach helps avoid unnecessary file checks and enhances the overall reliability of the function.

Impact: 

The performance enhancement in the WP_Theme::get_block_template_folders() method will lead to quicker and more efficient lookups of block template folders within themes. WordPress developers and users can anticipate improved performance, reduced I/O overhead, and a smoother experience when working with block themes.

Ticketticket Created for both bug reports and feature development on the bug tracker.: #58319

Bundled Theme: Implement the_header_image_tag() function for enhanced compatibility for older core themes.

The the_header_image_tag() function was introduced in WordPress 4.4 as part of [35594]. It is used in all themes created since WordPress 4.4 that supported 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. images. The function get_header_image_tag() continues to get updated with new image features, like lazy loading, async decoding and fetch priority. To ensure our core themes maintain compatibility and benefit from these enhancements, a backward compatibility shim has been applied, integrating the the_header_image_tag() function into the following core themes:

  • Twenty Ten
  • Twenty Eleven
  • Twenty Twelve
  • Twenty Fourteen
  • Twenty Sixteen

This change ensures future compatibility and modern image features are applied for header images to these older themes.

Ticket: #58675

Props to @spacedmonkey for writing this 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..
Props to @westonruter for editing, @mikeschroder and @webcommsat for reviewing.

Edit: The implementation for caching theme patterns during pattern registration was changed from transients to standard object caches after this dev note was first published (See [56978]). This post has been updated to reflect those changes.

#6-4, #dev-notes, #dev-notes-6-4, #performance