Block themes, a new way to build themes in WordPress 5.9

WordPress 5.9 makes the official introduction to 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 in WordPress, which previously required the 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/ 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 work and prioritizes working with the latest Full Site Editing features. The new default theme, Twenty Twenty-Two, ships with WordPress 5.9 and is the very first default block theme, which makes it a beautiful reference theme to learn from.

If you aren’t ready for a block theme just yet, no worries – classic themes continue to exist and work as always. Keep in mind though that to use the latest and greatest with full site editing, you’ll need to use a block theme, which is tailored to the newest features coming to WordPress. If you’re a theme author, see if you can start to incorporate some of these latest features into your themes, like theme.jsonJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. or the template editor.

A collection of screenshots featuring the Twenty Twenty-Two theme

Overview

At a high level, block themes use blocks to define the templates that layout and structure your entire site. The new templates and template parts are defined in HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers., and use the custom styling offered through theme.json.

After enabling a block theme, a new menu item under Appearance called “Editor (betaBeta A pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process.)” will show. This editor unlocks the ability to visually edit your homepage, templates, and template parts allowing you to create more of what you want in more places.

Thanks to the new features block themes offer and the ability to edit your content directly, the CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. menu is hidden from the dashboard unless you’re using a plugin that requires it. Read this GitHub issue for additional context.

For any current block theme authors, please note that there are new, simplified directory names: The name for the directory containing template files will be templates, and the name for the directory containing template part files will be parts. The block- prefix was dropped from both.

Join the fun and submit your own block theme to the WordPress Theme directory using the full-site-editing 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.). Find the listing of available block themes here: https://wordpress.org/themes/tags/full-site-editing/

Building block themes

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. cannot cover completely how to build a block theme, but here are some pointers to the documentation and reference to get you started.

First off, WordPress 5.9 ships with a beautiful block theme, TwentyTwentyTwo. Look in your theme directory or browse the GitHub repo to review the source and see what a block theme looks like.

The theme-experiments repository has an Empty Theme that is a good boilerplate to start your first block theme from. There is also a command-line script to help generate a block theme to get you started:

git clone https://github.com/WordPress/theme-experiments
cd theme-experiments
php new-empty-theme.php

Follow the prompts to create your theme and then copy the generated folder to your wp-content/themes directory.

Block theme documentation

Theme blocks

Explore building with Theme blocks a set of special blocks included for full site editing and block themes. Theme blocks are dynamic blocks that pull in content similar to the template tag functions from PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher, for example the Post Title block mirrors the the_title() function.

Here are two examples using theme blocks in a block templates. A singular post template (singular.html) is loaded with the post data, so using a theme blocks can be used stand-alone, for example:

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<!-- wp:post-title /-->

<!-- wp:post-content {"layout":{"inherit":true}} /-->

When using theme blocks on an index or archive page, the theme blocks are used inside a 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. and Post Template, for example:

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<!-- wp:query {"queryId":1,"query":{"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","sticky":""}} -->
   <!-- wp:post-template -->
       <!-- wp:post-title {"isLink":true} /-->
       <!-- wp:post-excerpt /-->
   <!-- /wp:post-template -->
<!-- /wp:query -->

A pattern block can be used to insert translatable content inside a block template, since patterns are PHP-based, there is a mechanism to mark strings for translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. or supply dynamic URLs. See the internationalization documentation


Props to @annezazu for contributing and reviewing this post.

#5-9, #dev-notes

New API to access global settings & styles

Following the introduction of a theme.json API in WordPress 5.8 and its corresponding JavaScript API, WordPress 5.9 comes with a PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher public 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. to read data from theme.json.

Access to settings & styles

The following new functions give access to the settings and styles keys found in a theme.json:

wp_get_global_settings( $path = array() , $context = array() );
wp_get_global_styles( $path = array(), $context = array() );

where:

  • $path is a list with the path to the speciffic setting, e.g.: array( 'color', 'link' ) returns the setting stored at settings.color.link. If no path is provided, all settings are returned.
  • $context is a named array through which consumers can have a finer-grained control of the data returned:
    • using the block_name key, consumers can access the settings of a particular 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.. For example, array( 'block_name' => 'core/paragraph' ) returns only the settings of the paragraph block.
    • using the origin key, consumers can decide to ignore custom data coming from the user by setting it to base. Otherwise, the data returned will be the result of merging defaults, theme, and custom data.

Some examples:

// Returns all settings
// after merging defaults, theme, and user data.
wp_get_global_settings(); 


// Returns the blockGap setting
// after merging defaults, theme, and user data.
wp_get_global_settings( array( 'spacing', 'blockGap' ) );

// Returns the borderRadius style for the group block
// after merging default, theme, and user data.
wp_get_global_styles(
  array( 'border', 'radius' ),
  array( 'block_name' => 'core/group' )
); 

// Returns the background color for the block paragraph
// after merging defaults and theme data (ignoring user data).
wp_get_global_styles(
  array( 'color', 'background' ),
  array(
    'block_name' => 'core/paragraph',
    'origin'     => 'base',
  )
); 

Access to the resulting stylesheet

Additionally, there’s a function to generate the stylesheet resulting of merging defaults, theme, and custom settings and styles:

wp_get_global_stylesheet( $types = array() );

where $types is a list of the styles to generate. Valid values are:

  • presets, the classes coming from the presets, e.g.: .has-black-color { ... }.
  • styles, the classes coming from the styles section of the theme.jsonJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML., e.g.: .wp-block-group {...}.
  • variables, the CSSCSS Cascading Style Sheets. Custom Properties coming from the presets and the custom section of the theme.json, e.g.: --wp--preset--duotone--dark-grayscale.

This function also considers whether the theme has theme.json support or not, abstracting that implementation detail from the consumers. By default, all styles are returned if the theme supports theme.json; if it doesn’t, only styles for variables and presets are returned.

Note that this function only takes care of generating the stylesheet string, it doesn’t actually enqueue any styles.


Props to @oandregal for creating 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., it was review and published by me (@mkaz).

#5-9, #dev-notes

Miscellaneous block editor changes in WordPress 5.9

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. covers updates and changes made to the blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editors for WordPress 5.9.

Blocks

Post Title

A div wrapper element around the post title in the post editor has been removed to align it better with the post title block. Some theme styles targeting the post title (that include the removed div’s class name) may no longer apply. Please use the wp-block-post-title class to style the post title, which will also style the post title block.

Data Module

batch function

New in the data module, the batch function triggers multiple consecutive actions to one or several data stores without re-rendering the components or notifying any data store subscriber. The store listeners are only notified when the batch callback ends.

This can be a performance improvement in situations when it’s unnecessary to rerender all the components while all the actions haven’t triggered yet. As an example, when typing in the canvas, the block editor generally triggers two actions on each character being typed: one action to update the content of the post and another one to track the current text selection. To avoid unnecessary re-renders that can impact the typing performance we batch these two actions together.

Example:

import { useRegistry } from '@wordpress/data';

function Component() {
  const registry = useRegistry();
  function callback() {
    // This will only rerender the components once. 
    registry.batch( () => { 
      registry.dispatch( someStore ).someAction(); 
      registry.dispatch( someStore ).someOtherAction(); 
    } );
  }

  return <button onClick={ callback }>Click me</button>;
}

Components

ColorPicker

The property onChangeComplete was deprecated to make the component more consistent with the rest of the codebase. Developers should update their code to the onChange property, a function that receives just a string with the color code.

The property onChangeComplete received an object instead of a string. We tried for backward compatibility, and in many cases, onChangeComplete can still be used (although not recommended). The following properties of the object passed to onChangeComplete are still valid hex, rgb, hsv, hsl, source, oldHue. The property color that contained a tinycolor2 color object is no longer valid. Code that relied on the color property must be updated as the property is not present anymore because we removed the tinycolor2 library from the project.


This change should have almost no impact as the ColorPicker component is a very low-level component used to implement the picking of custom colors, this component, although useful internally, is not particularly useful to outside 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. Therefore, code that uses the higher level and useful components like ColorPalette or PanelColorGradientSettingsdoes do not require updates related to this change.

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

The properties in the editor.PostTaxonomyType filter is no longer passed a value other than slug.

These following properties are no longer available debouncedSpeak, hasAssignAction, hasCreateAction, instanceId, onUpdateTerms, speak, taxonomy, and terms. To retrieve these values use the hook useSelect.

For example:

import { addFilter } from '@wordpress/hooks';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';

const myEditorPostTaxonomiesFilter = ( OriginalComponent ) => {
    return ( props ) => {
        const { slug } = props;
        const { taxonomy, terms } = useSelect( ( select ) => {
            const { getEditedPostAttribute } = select( editorStore );
            const { getTaxonomy } = select( coreStore );

            const _taxonomy = getTaxonomy( slug );

            return {
                taxonomy: _taxonomy,
                terms: _taxonomy
                    ? getEditedPostAttribute( taxonomy.rest_base )
                    : [],
            };
        } );

        return <MyPostTaxonomyType terms={ terms } taxonomy={ taxonomy } />;
    };
};

addFilter(
    'editor.PostTaxonomyType',
    'myFilter/my-editor-post-taxonomies-filter',
    myEditorPostTaxonomiesFilter
);

Removed APIs

Some low-impact 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/. APIs have been removed. These APIs have been initially deprecated on WordPress 5.3 and a warning message was being printed in the console to warn the developers.

  • The menuLabel prop in DropdownMenu component: without it, the Dropdown menu still works, it just might render a slightly different aria label. Prefer the usage of the menuProps prop instead.
  • The position prop in DropdownMenu component: without it, the Dropdown menu still works but might render a slightly different position. Prefer the usage of the popoverProps prop instead.
  • The onClickOutside prop in the Popover component: the component still works without it, it might not close when you click outside it. Prefer the usage of the onFocusOutside prop instead.
  • The getAutosave and hasAutosave selectors from the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress./editor store: Prefer using the alternative selectors from the core data package. wp.data.select( 'core' ).getAutosave( postType, postId, userId ).

Props to @youknowriad, @jorgefilipecosta, @toro_unit, and @ellatrix for contributions to this post.

#5-9, #core-editor, #dev-notes

Enhanced lazy-loading performance in 5.9

Lazy-loading images was introduced in WordPress 5.5 and later expanded to also cover iframes in WordPress 5.7. In the upcoming WordPress 5.9 release, the implementation for both has received some fine tuning to improve performance.

A performance analysis from mid-2021 had discovered that the lazy-loading implementation from WordPress had been causing a slight performance regression in the Largest Contentful Paint metric (LCP). As outlined in the referenced post, the reason for that was that images and iframes in the initial viewport would be marked to lazy-load them, since the WordPress implementation would mark nearly all images with loading="lazy". The aforementioned post then further explains that this can be improved by skipping addition of loading="lazy" for the first content image or 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., which in the vast majority of cases will appear within the initial viewport. WordPress can only make educated guesses around that and not be 100% certain, but an analysis taking into account 50 popular themes showed that the enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. brought LCP improvements across the board, up to 30% faster page load. WordPress 5.9 now comes with this refinement implemented, leading to the corresponding enhanced LCP performance.

How it works

So far, fine tuning which images and iframes should be lazy-loaded or not has been possible using the wp_img_tag_add_loading_attr and wp_iframe_tag_add_loading_attr filters, and these are still available as before. However, to improve the performance out-of-the-box without requiring a developer to customize the behavior, WordPress will now skip the very first “content image or iframe” on the page from being lazy-loaded. The term “content image or iframe” here denotes any image or iframe that is found within content of any post in the current 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. as well any 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. of such a post. This applies to both “singular” and “archive” content: In a “singular” context the first image or iframe of the (only) post is not lazy-loaded, while in an “archive” context the first image or iframe of the first post in the query is not lazy-loaded.

Customizing the behavior

The approach of not lazy-loading the first content image or iframe enhances LCP performance correctly for the majority of themes, which use a single-column layout for post content. For themes with alternative layouts such as multi-column, a new 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. is now available and can be used to change how many of the first images/iframes should be skipped from being lazy-loaded – per the above, the default is 1. It is recommended for theme authors to use this filter in cases where based on the layout it is likely to have more than one content image/iframe in the initial viewport.

For example, a theme that uses a three-column grid of posts in its archives could leverage the filter to override the threshold to 3 on archive pages, which would then result in the first three content images/iframes not being lazy-loaded. The following code snippet illustrates what that could look like:

function skip_lazyloading_on_first_three_archive_images( $omit_threshold ) {
    if ( is_home() || is_archive() ) {
        return 3;
    }
    return $omit_threshold;
}
add_filter( 'wp_omit_loading_attr_threshold', 'skip_lazyloading_on_first_three_archive_images' );

The refinement of the lazy-loading implementation should notably improve LCP performance for most sites that rely on it, while not having adverse effects for sites where the default heuristics described above do not apply to. That is only a solid starting point though. In the future, specifically with the more semantic content specification that 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.-based themes will facilitate, we will be able to further fine tune the lazy-loading implementation by using the available block information.

For more background information on the changes described above, see #53675.

Props to @adamsilverstein and @audrasjb for review and proofreading.

#5-9, #dev-notes, #feature-lazyloading, #performance

Take more control over Inner Block Areas (as a block developer)

Prior to WordPress 5.9 the only way to work with inner 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. was to use the <InnerBlocks /> component. One downside of this approach is that in the editor this creates additional DOM nodes that wrap the markup of the inner blocks. This makes it more difficult to style these inner blocks areas to match what the enduser will see on the site.

With WordPress 5.9 we’re introducing a new reactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/. hook called useInnerBlocksProps that allows you to change this and take more control over the markup of inner blocks areas. The useInnerBlocksProps is exported from the @wordpress/block-editor package same as the InnerBlocks component itself and supports everything the component does. It also works like the useBlockProps hook introduced with apiVersion: 2 in WordPress 5.6.

To use the hook, take the object returned from calling the hook and spread it onto the host element you want to render the inner blocks into. For example:

function BlockEdit(props) {
    const blockProps = useBlockProps();
    const innerBlocksProps = useInnerBlocksProps();

    return (
        <section {...blockProps}>
             <div {...innerBlocksProps} />
        </section>
    );
}

The above code will render to the following markup in the editor:

<section>
    <div>
        <!-- Inner Blocks get inserted here -->
    </div>
</section>

The hook also accepts two arguments:

  1. The first argument accepts an object containing additional props that are added to the element in the dom. For example, to add a custom class name to the inner blocks area you pass { className: 'my-class' } as the first argument.
  2. The second argument is also an object and can contain any properties to control the behavior of the inner blocks area. It accepts all options like allowedBlocks, template, templateLock, renderAppender, etc. You can find the full list of supported properties in the InnerBlocks props documentation
useInnerBlocksProps( additionalWrapperProps, innerBlocksProps );

Saving the inner blocks

To save inner blocks content there is a special variant of the hook, this is similar to the component-based approach that uses <InnerBlocks.Content /> in the save method of a block. For 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., you add .save to the end of the hook like so:

function BlockSave( props ) {
    const innerBlocksProps = useInnerBlocksProps.save( { className: 'my-class' } );

    return (
        <section {...innerBlocksProps} ) />
    );
}

When you use the useInnerBlocksProps.save() hook it only accepts the first parameter, these are the options you want to add to the container element.

Why use hooks?

The hooks allow for new options and nice enhancements. For example, you can take the object returned from the useBlockProps hook—these contain the properties for the block’s wrapping container—and pass them to the useInnerBlocksProps hook. This reduces the number of elements we need to create. For example:

function BlockEdit(props) {
    const blockProps = useBlockProps();
    const innerBlocksProps = useInnerBlocksProps( blockProps );

    return (
        <section {...innerBlocksProps} />
    );
}
<section>
    <!-- Inner Blocks get inserted here -->
</section>

Another benefit to using the hook approach is using the returned value, which is just an object, and deconstruct to get the react children from the object. This property contains the actual child inner blocks thus we can place elements on the same level as our inner blocks.

function BlockEdit(props) {
    const blockProps = useBlockProps();
    const { children, ...innerBlocksProps } = useInnerBlocksProps( blockProps );

    return (
        <section {...innerBlocksProps}>
            { children }
            <!-- Insert any arbitrary html here at the same level as the children -->
        </section>
    );
}
<section>
    <!-- Inner Blocks get inserted here -→
    <!-- The custom html gets rendered on the same level -->
</section>

Example:

block.json

{
    "apiVersion": 2,
    "name": "example/block",
    "title": "Example",
    "category": "text",
    "editorScript": "file:./index.js"
}

index.js

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';

import metadata from './block.json';

function BlockEdit(props) {
    const blockProps = useBlockProps( { className: 'my-class' } );
    const innerBlocksProps = useInnerBlocksProps(
        blockProps,
        { allowedBlocks: [ 'core/heading', 'core/paragraph', 'core/image' ] }
    );

    return (
        <section {...innerBlocksProps} />
    );
}

function BlockSave(props) {
    const blockProps = useBlockProps.save( { className: 'my-class' } );
    const innerBlocksProps = useInnerBlocksProps.save( blockProps );

    return <section {...innerBlocksProps} />
}

registerBlockType( metadata, {
    edit: BlockEdit,
    save: BlockSave,
} )

Thank you, @gziolo and @mkaz for reviewing and proofreading this post.

#5-9, #dev-notes

Introducing new language switcher on the login screen in WP 5.9

Edited on December 21, 2021 after [52404].

Edited on January 3, 2022 after [52432]: two filters were renamed.

Edited on January 4, 2022 after [52435]: the dropdown 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 renamed.

Since WordPress 4.9, it’s possible to control the language of the login screen using the wp_lang query variable (example: wp-login.php?wp_lang=fr_FR). This is a hidden feature that was only used by the interim login modal.

WordPress 5.9 introduces a new language switcher available on the login screen. This dropdown allows users to use the login screen, the password reset screen, and the registration screen in their own language.

Please note that the language list in the dropdown is based on the currently installed languages of the WordPress installation. To install a new language, websites administrators can use the language selector available on Settings > General. Go to site languages drop down, select a language to install and save changes. This installs and switches the site language, you can switch back if you do not want that as the default language.

Filters default arguments for the Languages dropdown

The hook login_language_dropdown_args can be used to filter the default arguments passed to the languages dropdown displayed on the login screen, using an array of arguments for the Languages select input. Here are the default parameters of the $args parameter:

$args = array(
	'id'                          => 'language-switcher-locales',
	'name'                        => 'wp_lang',
	'selected'                    => determine_locale(), // Use the current locale by default.
	'show_available_translations' => false, // Do not show languages that are not installed.
	'explicit_option_en_us'       => true, // Use an explicit value of the 'en_US' option instead of an empty value.
	'languages'                   => $languages, // Contains the list of installed languages.
);

For example, on a website with several languages installed, only show fr_FR and de_DE in the dropdown:

function wporg_filter_login_language_dropdown_args( $args ) {
	$args['languages'] = array( 'fr_FR', 'de_DE' );
	return $args;
}
add_filter( 'login_language_dropdown_args', 'wporg_filter_login_language_dropdown_args', 10, 1 );

Remove the Language dropdown entirely

If you want to remove the Language dropdown entirely, you can use the login_display_language_dropdown filter.

Usage example:

add_filter( 'login_display_language_dropdown', '__return_false' );

Related 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.: #43700. See also [52404], [52432]

Props @hellofromtonya and @mkaz for proofreading.

#5-9, #dev-notes

Posts, Post types and Taxonomy changes in WordPress 5.9

In WordPress 5.9, new hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. and functions are added to help developers to work with Posts, Post types and Taxonomies.

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

WP 5.9 introduces the new is_post_type_viewable filter to allow developers to hook into is_post_type_viewable() to override the check performed by this function.

This filter exposes the $post_type object to allow to return either true or false depending on their needs. The expected filtered value is a boolean. As filtered values can change, including the data type, this commit includes a is_bool() check, thus ensuring backwards-compatibility and guard against potential type errors in PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher 8.1+. Non-boolean values (even falsey and truthy values) will result in the function returning false.

Usage:

/**
 * Override is_post_type_viewable() value for the "Books" custom post type. 
 */
function wporg_books_is_not_a_viewable_post_type( $is_viewable, $post_type ) {
	if ( __( 'Books', 'my-plugin' ) === $post_type->label ) {
		return false;
	}
	return $is_viewable;
}
add_filter( 'is_post_type_viewable', 'wporg_books_is_not_a_viewable_post_type', 10, 2 );

Related ticketticket Created for both bug reports and feature development on the bug tracker. on TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.: #49628.

is_post_status_viewable filter

Similarly to is_post_type_viewable, the is_post_status_viewable filter allow developers to hook into the related PHP function. This filter exposes the $post_status object to allow to return either true or false depending on their needs.

Usage:

/**
 * Override is_post_status_viewable() value for the "Unread" custom post status. 
 */
function wporg_unread_is_not_a_viewable_post_status( $is_viewable, $post_status ) {
	if ( __( 'Unread', 'my-plugin' ) === $post_status->label ) {
		return false;
	}
	return $is_viewable;
}
add_filter( 'is_post_status_viewable', 'wporg_unread_is_not_a_viewable_post_status', 10, 2 );

Related ticket on Trac: #54375.

post_thumbnail_url filter

WP 5.9 Introduces the new filter post_thumbnail_url which allows overriding the default url returned from wp_get_attachement_image_url() function. It passes the following parameters:

  • $thumbnail_url: The Post thumbnail URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org (or false if the post does not exist)
  • $post: The Post ID or WP_Post object. Default is global $post
  • $size: The Registered image size to retrieve the source for or a flat array of height and width dimensions. Default value: post-thumbnail

Usage:

/**
 * Override the post thumbnail URL for a specific template.
 */
function wporg_change_post_thumbnail_url_for_about_template( $thumbnail_url, $post, $size ) {
	if ( 'templates/about.php' !== get_page_template_slug( $post ) ) {
		return wp_get_attachment_image_url( get_template_directory . '/images/my-specific-image.png' );
	}
	return $thumbnail_url;
}
add_filter( 'post_thumbnail_url', 'wporg_change_post_thumbnail_url_for_about_template', 10, 3 );

Related ticket on Trac: #40547.

post_thumbnail_id filter

Similarly, WP 5.9 introduces the new post_thumbnail_id filter which allows overriding the default id returned from get_post_thumbnail_id(). It passes the following parameters:

  • $thumbnail_id: The Post thumbnail ID (or false if the post does not exist)
  • $post: The Post ID or WP_Post object. Default is global $post

Related ticket on Trac: #23983.

New labels available in register_taxonomy()

In WP 5.9, some static strings were replaced with additional label options to allow developers further flexibility for customizing the Edit {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.} screen.

The following labels were added:

  • name_field_description: Description for the Name field on Edit Tags screen. Default: “The name is how it appears on your site.”
  • parent_field_description: Description for the Parent field on Edit Tags screen. Default: “Assign a parent term to create a hierarchy. The term Jazz, for example, would be the parent of Bebop and Big Band.”
  • slug_field_description: Description for the Slug field on Edit Tags screen. Default: “The « slug » is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.”
  • desc_field_description: Description for the Description field on Edit Tags screen. Default: “The description is not prominent by default; however, some themes may show it.”

Related ticket on Trac: #43060.

New function to get the URL for existing 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. of a post: wp_get_post_revisions_url()

Since WP 5.9, the wp_get_post_revisions_url() function can be used to get a link to a given post’s revisions.

Parameters:

  • $post_id (optional): Post ID or WP_Post object. Default is global $post.

This function returns the URL for editing revisions on the given post (or null otherwise).

Related ticket on Trac: #39062.

New built-in post types in WP 5.9

Please note that WordPress 5.9 introduces four new built-in post types related to the new full site editing experience and are used when 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. Theme is activated.

  • wp_template: The templates to include in your theme.
  • wp_template_part: The template parts to include in your templates.
  • wp_global_styles: The styles created and saved by the site adminadmin (and super admin) for the current theme.
  • wp_navigation: The navigation menus that can be inserted into the site.

Additional 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. will be published to introduce the new full site editing experience. Note the above post types are reserved terms for WordPress internal usage.

Thanks @mkaz for proofreading.

#5-9, #dev-notes

Changes to the WordPress Core PHP Test Suite

Why were changes needed?

Dev Wapuu
Image credits: @marktimemedia

The WordPress test suite uses the industry standard PHPUnit tool to run the PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher unit tests.

Over the years, PHPUnit has seen a number of changes, new assertions being introduced, different annotations and more, but most notably, as of PHPUnit 8.0, a void return type was added to the typical fixture setUp() and tearDown() methods.

This void return type is problematic in the context of WordPress, as return types in general were only introduced in PHP 7.0 and the void return type wasn’t introduced until PHP 7.1.
While WordPress still has a minimum PHP version of PHP 5.6, the void return type can not be introduced in the test suite as it would inhibit the tests from being run on PHP 5.6 and 7.0.

At the same time, having to run the tests on older PHPUnit versions (PHPUnit < 8.0) made it increasingly difficult to get the test suite to run on new PHP versions, like PHP 8.0 and the upcoming PHP 8.1 (expected end of November) as these older PHPUnit versions are no longer supported and are not being made compatible with newer PHP versions anymore.

Over the past few years, a number of different solution directions were explored and rejected, largely due to the large maintenance burden these would add to the small team of WordPress contributors maintaining the test framework.

With the upcoming release of PHP 8.1 as a driver, we took another look at this problem and the available tooling in the wider PHP field and a solution has now been implemented which should future-proof the test suite for, at least, a number of years.

The solution

The implemented solution is based on the external PHPUnit Polyfills library, which “allows for creating PHPUnit cross-version compatible tests by offering a number of polyfills for functionality which was introduced, split up or renamed in PHPUnit”.

The PHPUnit Polyfills also solves the void conundrum via a tailored TestCase using snake_case methods.

In effect, this means that the WP CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. test suite can now run on all PHPUnit versions between PHPUnit 5.7.21 up to the latest release (at the time of writing: PHPUnit 9.5.10), which allows for running the test suite against all supported PHP versions using the most appropriate PHPUnit version for that PHP version.

It also means that, as of mid August, the tests are being run against PHP 8.1 and fixes for PHP 8.1 compatibility are currently being made.

With the PHPUnit Polyfills in place, tests can now be written using the feature set of the highest supported version of PHPUnit.
The Polyfills library will fill in the gaps and ensure the tests can still run on lower versions of PHPUnit without problems.

What has changed?

  1. The Composer lock file has been removed.
    The version constraints in the composer.json file have been made stricter to ensure the developer experience is not negatively impacted by this with regards to coding standards checks.
  2. The PHPUnit Polyfills library at version ^1.0.1 has been added as a Composer dev dependency.
  3. All WordPress Core tests now use PHPUnit 9.x assertions and expectations.
  4. All WordPress Core tests now use snake_case fixture methods, i.e. set_up() instead of setUp() and tear_down() instead of tearDown().
  5. The minimum supported PHPUnit version has been raised to PHPUnit 5.7.21 (was 5.4.0).
  6. The WordPress Core test bootstrap file will no longer throw an error when the tests are being run with PHPUnit 8.x or 9.x.
  7. The WordPress Core test bootstrap file will throw an error when the PHPUnit Polyfills are not available or do not comply with the minimum version requirements.
  8. All WP Core native assertions now have an extra, optional $message parameter, just like all PHPUnit native assertions.
    Please use this parameter in all tests which contain more than one assertion to make debugging tests easier.
  9. The WP_UnitTestCase_Base::setExpectedException() method is deprecated and should no longer be used.
  10. The WP_UnitTestCase_Base::checkRequirements() method is deprecated and no longer functional, and in reality hasn’t been for a long time for anyone using it in combination with PHPUnit 7.0+.
  11. The copies of the PHPUnit SpeedTrapListener classes have been removed as they were never actively used in Core.
    Anyone who still wants to use the SpeedTrapListener can install it separately.
  12. The copies of the PHPUnit 9.x MockObject classes which were introduced in the WP Core test suite in WP 5.6 have been removed, as they are no longer needed when the tests are run on the appropriate PHPUnit version for the PHP version used.

While the above changes have been made in WordPress 5.9, a minimal selection of these changes has been backported to WordPress 5.2 – 5.8:

  1. The PHPUnit Polyfills at version ^1.0.1 is now a requirement for the test suites in WP 5.2 – 5.8 and this requirement will be enforced via the test bootstrap.
  2. … which makes all the polyfills for PHPUnit 9.x assertions and expectations available when running tests against WP 5.2 – 5.8.
  3. Additionally, snake_case wrapper methods have been added for the camelCase fixture method names, meaning that for WP 5.2 – 5.8, the snake_case fixture method names will work without needing further work-arounds, both for fixture declarations as well as for calling the parent::set_up() and the likes.

    There is one caveat to this: the backported implementation presumes a fixed order for calling the parent (camelCase) methods versus the child (snake_case) methods: for set_up*() methods: parent first, child second; for tear_down*() methods: child first, parent second.
    This is the standard order, but if you have a fixture method which diverges from this or doesn’t call the parent, you may get unexpected results.

These backports allow for backporting future (security) fixes for WordPress itself without having to make the accompanying tests compatible with older PHPUnit versions.

These backports will also make it more straightforward for extenders to continue to test their 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 against multiple WordPress versions.

For full details about all the changes, please have a read through Trac ticket 46149.

Changes under the hood which should not be noticeable:

A new PHPUnit_Adapter_TestCase class has been added. This class is nested in-between the WP_UnitTestCase_Base class and the PHPUnit TestCase class and provides the PHPUnit cross-version adapter layer.

All PHPUnit assertion polyfill methods which previously existed in WP Core have been removed as they are no longer necessary now this functionality is provided via the PHPUnit Polyfills library.
All polyfills for assertions/expectations which were previously in WP are still available, they are now just provided via the Polyfills package.

As for the Docker set up: the PHPUnit container is no longer needed and has been removed from the docker-compose config.

What hasn’t changed:

  • The PHPUnit class aliases (for support of PHPUnit 5), which WP provided are still available, though shouldn’t be needed anymore.
  • You can still extend the WP_UnitTestCase class for your tests and will receive access to everything which was available before + more (i.e. a complete set of polyfills).

Future changes

There is a ticket open to rename some of the WordPress native test helper methods to handle the “doing it wrong” and WP native deprecation notices, as the current method names (too) closely resemble a PHPUnit native method name, which can easily lead to confusion and use of the wrong methods in tests.

When that ticketticket Created for both bug reports and feature development on the bug tracker. is actioned, this dev-note will be updated with the relevant information.

What does this mean for contributors to WordPress Core?

In general:

If you use Composer locally, please run composer update --with-all-dependencies (or composer update -W for short) from the root of your WordPress clone now to make sure your install is updated and to get the most appropriate versions of the dependencies for the PHP version you are running on.

Go on, do that now. This dev-note will wait patiently for you to come back.

You will need to run this command semi-regularly in the future (whenever the composer.json file has been updated).

For WP 5.9 and higher, please don’t use composer install anymore.

If, for example for backports, you need to install the dependencies for WP 5.8 or lower, in that case, you still need to run composer install.

🎓 Why?

The first time you run composer install locally, it creates a composer.lock file and when you run Composer again, it will look at your composer.lock file to install the “locked” versions again.

Previously, with the committed composer.lock file, the lock file was managed and updated centrally. However, that also meant that you often would be running the dev tools at a version which wasn’t the most appropriate one for the PHP version you are working under. This was getting more and more problematic for running the tests, which is why the file was removed.

Now the composer.lock file is no longer committed, you have to update it yourself to make sure you receive the latest version of the dev dependencies appropriate for your PHP version and within the version constraints set in the composer.json file.

For running the Core tests:

If you usually run the Core tests via Docker using the npm run test:php command, you can continue to do so and all should still work as expected.

If you usually run the Core tests via a Composer installed version of PHPUnit, again, you can continue to do so and all should still work as expected as long as you followed the above instructions to run composer update -W first.

If you usually run the Core tests via a PHAR file, you either have to run composer update -W once in a while or you have to set up a clone of the PHPUnit Polyfills repo. For more information about this last option, please see the set up information in the handbook.
If you are running locally on PHP 7.2 or higher, you may want to download a more recent PHPUnit PHAR file (PHPUnit 8 or 9) to benefit from the advances which have been made in PHPUnit.

If you are running the tests locally on PHP 7.2 or higher, you may notice the test runs being faster and the output being enhanced as the tests will now run on a more recent PHPUnit version.

💡 Pro-tip:

Now might also be a good time to verify that your local wp-tests-config.php file is still in sync with the wp-tests-config-sample.php file.

Similarly, if you use a local phpunit.xml overload configuration file, it is strongly recommended to verify that any changes made in the phpunit.xml.dist (and multisite.xml) file are synced into your local configuration.

For writing tests for Core:

You can now use the full range of assertions as available in PHPUnit 9.5 in your tests. Please use the most appropriate assertion available.

Test fixture methods MUST use snake_case method names from now on as per the below table.

Old nameNew name
setUpBeforeClass()set_up_before_class()
setUp()set_up()
assertPreConditions()assert_pre_conditions()
assertPostConditions()assert_post_conditions()
tearDown()tear_down()
tearDownAfterClass()tear_down_after_class()

The Make Core handbook page about writing tests has been updated with this information.
The page has also been enhanced with more handy tips and tricks, so please have a read through!

What does this mean for plugins/themes running integration tests based on the WP Core test suite?

It is a known fact that there are a lot of plugins/themes which use the WordPress Core test framework as a basis for their integration tests.

If your plugin/theme is one of them, these changes will impact you as well.

Step-by-step: how to make your test setup compatible with these changes and with higher PHPUnit versions:

  1. Run your tests against PHP 7.4 with PHPUnit 7.x and WP 5.8.1 and make sure there are no pre-existing errors/failures.
  2. Add PHPUnit Polyfills as a Composer require-dev dependency (or inherit it from WP).
  3. If you add the Polyfills as a requirement and only support WP 5.9 and higher, remove the requirement for PHPUnit in favour of letting the Polyfills handle it. This will prevent potential future version constraint conflicts.
    • If you still need/want to run your tests against older WP versions, keep the PHPUnit requirement and make sure it is set to ^5.7.21 || ^6.5 || ^7.5 and let CI (continuous integration script) handle removing that requirement for WP 5.9.
    • Or do it in reverse and remove the requirement for dev and add it back in CI for older WP versions.
  4. Make sure the Polyfills autoloader is wired in to your test bootstrap.
    • If you’ve chosen to “inherit the Polyfills from WP”, in this context that means that you use a full clone of WordPress and will install the Composer dependencies for WordPress before running the tests. In that case, you should be all set.
    • If you use only a partial clone of WordPress, like when your tests have been set up using the WP-CLIWP-CLI WP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is http://wp-cli.org/ https://make.wordpress.org/cli/ scaffold command, or if you don’t run WordPress’ Composer setup, please make sure you load the Polyfills autoloader in your test bootstrap before running the WP native test bootstrap.
      • If you include your Composer vendor/autoload.php file as your test bootstrap before you run the WP native test bootstrap, you’re all set already, the Polyfills autoloader will be included automatically.
      • Alternatively, you can add a require_once 'path/to/vendor/yoast/phpunit-polyfills/phpunitpolyfills-autoload.php'; in your test bootstrap before including the WP native test bootstrap.
      • As a last alternative, you can declare a WP_TESTS_PHPUNIT_POLYFILLS_PATH constant containing the absolute path to the root directory of the PHPUnit Polyfills installation in your plugin/theme’s own test bootstrap file.
        Again, this constant must be declared prior to running the WP native test bootstrap file.
  5. Search your codebase for declarations of the fixture methods, as well as calls to (parent) fixture methods, and replace camelCase with snake_case in the method names.
    Example:
    // Old:
    public function setUp() {
         parent::setUp();
         // Do something.
    }
    
    // New:
    public function set_up() {
        parent::set_up();
        // Do something.
    }
  6. Verify your tests run without errors after the changes by running them against PHP 7.4 on PHPUnit 7.x with WP 5.8 .
  7. Verify your tests run without errors after the changes by running them against PHP 7.4 on PHPUnit 7.x with WP trunk (= WP 5.9).
  8. While using WP trunk/5.9, switch to PHPUnit 8.x – look out for deprecation notices PHPUnit throws and just literally do what they tell you to do.
  9. While still using WP trunk/5.9, switch to PHPUnit 9.x – look out for deprecation notices PHPUnit throws and just literally do what they tell you to do.

Once you’ve run through these steps, your tests should be cross-version compatible with PHPUnit 5.7.21 – 9.5, able to run against the WordPress 5.2 to 5.9 branches and able to run on PHP 5.6 – 8.1.

Next, you may want to run your tests against PHP 8.0 and 8.1 using PHPUnit 9.x with WP 5.9 to see if your plugin/theme is compatible with these more recent PHP versions.

🚨 Pro-tip:

If you want your CI build to fail when PHPUnit encounters PHP native deprecation notices, make sure to add convertDeprecationsToExceptions="true" to your PHPUnit configuration file as the default value for this setting has been changed to false in PHPUnit 9.5.10/8.5.21.

Enabling this setting is strongly recommended for testing your plugin/theme against PHP 8.1, as PHP 8.1 introduces a lot of new deprecations.

What to do when running tests in CI against multiple WP/PHP combinations?

If you are running your plugin/theme integration tests against multiple WordPress and PHP combinations, you will most likely need to make some adjustments to your Continuous Integration (CI) script(s).

Which exact changes you need to make depends entirely on your specific setup. There is no “one size fits all” solution.

As a general rule of thumb:

  • WP 5.2 – 5.5 is able to run tests against PHP 5.6 – 7.4 with PHPUnit 5.x (PHP 5.6 and 7.0) – 7.x (PHP 7.1 and higher).
  • WP 5.6 – 5.8 is able to run tests against PHP 5.6 – 8.0 with PHPUnit 5.x (PHP 5.6 and 7.0) – 7.x (PHP 7.1 and higher).
  • WP 5.9 and higher is able to run tests against PHP 5.6 – 8.1 with PHPUnit 5.x – 9.x (use the most appropriate PHPUnit version for each PHP version).

Also see the PHP Compatibility and WordPress Versions and PHPUnit Compatibility and WordPress Versions pages in the Make Core handbook.

Other typical things to take into account and to work around when needed:

  • Is there a config - platform - php setting in your composer.json which fixes the PHP version to a specific version – typically PHP 5.6 – for installing dependencies ?
    If so, you may need to either selectively remove this setting or run Composer with --ignore-platform-reqs for certain WP/PHP combinations in your test matrix.
  • Has the composer.lock file been committed ?
    In that case, you may need to either selectively remove that file in CI before running composer install; or run composer update -W for certain WP/PHP combinations in your test matrix.
  • Do you use a complete clone of WP ?
    For WP 5.2 – 5.8, you’ll need to install the WP dependencies by using composer install.
    For WP 5.9 and higher, you’ll need to install the WP dependencies by using composer update -W.

To make sure you run the test against the right PHPUnit version, you may need to run (a variation on):

composer remove --dev phpunit/phpunit
composer update --dev yoast/phpunit-polyfills --with-dependencies --ignore-platform-reqs

💡 Did you know ?

If you use 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/ Actions to run your tests for continuous integration and PHPUnit and the PHPUnit Polyfills are your only external test dependencies, as of Setup-PHP 2.15.0 (expected soon), you can use the tools key in the shivammathur/setup-php action to install these:

- name: Setup PHP with tools
  uses: shivammathur/setup-php@v2
  with:
    php-version: '8.0'
    tools: phpunit-polyfills

For more information about the tools key for setup-php, see the action documentation.

For more information on how to wire in the PHPUnit Polyfills when installed via setup-php, see the FAQ section of the Polyfills documentation.

Anticipating some frequently asked questions

I’m a plugin/theme maintainer, but don’t use Composer, can I still run my integration tests?

Yes, but you do need to make sure that either the Polyfills are available via a Composer global or local installLocal Install A local install of WordPress is a way to create a staging environment by installing a LAMP or LEMP stack on your local computer. or via some other manner, like a clone of the repo.

If you haven’t looked at Composer before, now might be a good time to take a look at it.

I’m running my tests via another tool stack (like BrainMonkey, WP Mock, PHP Mock, WP Browser, PestPHP), how do the changes made to the WordPress test suite affect me?

Short answer: They don’t.

Long answer: if you want to run your tests against multiple PHP and PHPUnit combinations, you may still find the PHPUnit Polyfills library helpful to you.

If you’ve not heard of the above mentioned tools before and want to read up on them, here are some links:

I used the WP-CLI scaffold command to set up my integration tests. How do the changes made to the WordPress test suite affect me?

There is no automated way right now to adapt existing tests for which the initial was created via the WP-CLI scaffold command, to make use of the new setup.

The current recommendation is to go through the steps in “What does this mean for plugins/themes running integration tests based on the WP Core test suite?“, which might be more or less involved depending on what version of the scaffolded test setup you are currently using.

A future version of the WP-CLI scaffold plugin-tests command will provide an upgrade mechanism to automatically upgrade an existing test setup to the new requirements. This will include adding a fully Composer-based testing setup as a replacement for the current bootstrap logic, making a composer update possible in the future to keep up with further test setup changes.

If you’re interested in learning more about these plans for the future, please subscribe to the issue on GitHub to stay informed.

I’m using WP Test Utils for my unit and integration tests. How do the changes made to the WordPress test suite affect me?

WP Test Utils is a library offering utilities for both unit testing and integration testing for WordPress plugins and themes. WP Test Utils already includes the PHPUnit Polyfills.

For the unit testing part, which is based on BrainMonkey, you are not affected by the changes.

If you use the integration testing utilities, you will need to make the change from camelCase to snake_case for the fixture methods in your test suite and you can now potentially widen the PHPUnit version requirements for your integration tests (also see the information about this in step 3 of the “Step by step” guide and the information about adjusting CI scripts).

Presuming you were already using the PHPUnit Polyfills provided by the Test Utils to use modern assertions, that’s it. You’re done.

WP Test Utils will continue to handle the integration test bootstrapping, which allows for running the tests against multiple WordPress and PHP versions.

The first version of WP Test Utils which has full support for the test framework changes made in WP 5.9, is WP Test Utils 1.0.0.

WP Test Utils 1.0.0 also includes improved support for integration tests which were created using the WP-CLI scaffold command and support for running tests against WP versions which don’t include the backports, like WP 5.2 – 5.8 point releases released before today, as well as WP < 5.2.


Props to @hellofromtonya, @johnbillion, @dingo_d, @netweb, @sergeybiryukov, @swissspidy, @schlessera for reviewing ahead of publication.

#5-9, #build-test-tools, #dev-notes, #phpunit, #unit-tests

Gallery Block Refactor Dev Note

The problem

If you have ever added a custom link to an image 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. and then tried to do the same on a Gallery image, you will understand the frustration and confusion of not having consistency between different types of image blocks. This inconsistency is because the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. Gallery block stores the details of the included images as nested <img> elements within the block content. Therefore, the images within a gallery look and behave different from images within an individual image block. There are some long-standing open issues related to this:

The only way to fix this with the Gallery block in its current state is to try and replicate the additional functionality that the Image block has in the Gallery block, and vice versa. While this would be possible, it would lead to an additional maintenance overhead to keep the UIUI User interface and underlying code in sync between the two blocks.

Changes made

To make the behavior of images consistent between the Image Block and Gallery, while avoiding code duplication, the core Gallery block has been refactored to save the individual images as nested core Image blocks using the standard core innerBlocks APIs. To make this work with the innerBlocks 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., the gallery structure also had to change from an ordered list to a collection of figure elements within a figure. This structure change also brings the core Gallery block into line with the W3C WAI guidelines on the grouping of images.

The structure change means that Gallery images now have the ability to add custom links, or custom styles, for each image. An example of the flexible Gallery layouts this opens up can be seen below.

Gallery images will also automatically inherit new functionality that is added to the Image block, including those added by plugins. Below is an example of a Gallery block making us of the Image wave style and vintage 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. option added by the CoBlocks 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.

Other benefits include being able to use the standard move, drag and drop, copy, duplicate, and remove block functionalities. Keyboard navigation also benefits from the adoption of the standard block model by the Gallery block.

Accessing the new Gallery Block

To access the new Gallery block you will need to download the latest version of the 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/ plugin. At the time of writing the change is still under an experimental flag, so once the plugin is installed check for an experimental setting ‘Enable the refactored gallery block’ and enable it if it is present:

If this flag is not present it means that the changes have been taken out of the experimental phase.

Currently, only new gallery blocks added after the experimental flag is turned on will be in the new format, all existing galleries will be editable and viewable in the current format. You can manually transform an old format gallery to the new format by clicking the ‘Update’ button that will appear in the block toolbar.

What theme and plugin authors need to do before 5.9

To support the new Gallery block format, plugin and theme authors should aim to do the following before the December release of this change in WordPress 5.9.

  • Add additional selectors to target the new nested image block in both the editor and front end (existing selectors must remain to support the existing gallery block content). The new structure can be seen below.
<figure class="wp-block-gallery blocks-gallery-grid has-nested-images columns-default is-cropped">
	<figure class="wp-block-image size-large">
		<img
			src="http://localhost/image1"
			alt="Image gallery image"
			class="wp-image-71"
		/>
	</figure>
	<figure class="wp-block-image size-large">
		<img
			src="http://localhost/image2"
			alt="Image gallery image"
			class="wp-image-70"
		/>
	</figure>
</figure>

Example CSSCSS Cascading Style Sheets. with selectors for old and new gallery formats:

.wp-block-gallery .blocks-gallery-item img,
.wp-block-gallery .wp-block-image img {

.wp-block-gallery .blocks-gallery-item figcaption,
.wp-block-gallery.has-nested-images .wp-block-image figcaption {

Test block transformations. Temporary transformation filters have been added to handle the transformation of 3rd party blocks to and from the new core gallery block format. However, block plugins with gallery transforms should still be tested with the Gutenberg plugin to ensure these transformations work correctly.

In the future, the temporary transformation filters may be deprecated, and plugin authors will then need to update their transformations to handle the new Gallery format. Notice will be given ahead of this change being made.

Additional context and considerations

Other existing solutions

Third-party block developers are currently solving some of the problems caused by the limitations of the core Gallery block by implementing their custom Gallery blocks. These include some of the missing functionality, like the ability to add custom links to individual images. This can be problematic for site owners and content editors due to a large number of Gallery blocks that offer very similar functionality, but none of which appear to provide a close match to the functionality available with individual core Image blocks.

There do not appear to be any examples of plugins that already solve this problem in a way that utilizes Image blocks as inner blocks.

Backwards compatibility considerations

This is a breaking change due to the fundamental change in the underlying Gallery structure. Due to the large number of Gallery blocks already in use, along with themes and plugins that may rely on the existing structure, the following steps have been taken to mitigate the impact of this change on theme and plugin developers as much as possible:

  • For the initial release in the Gutenberg plugin, there will be no automatic migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. of existing gallery content. This means that all existing gallery content will behave the same in the editor and front end as it does now, so will be compatible with existing plugins and themes. Only new gallery blocks added after this change will have the new structure in the editor and the front-end.  The plan is to add automatic migration of existing gallery content when loaded in the editor to the 5.9 WordPress release.
  • Two temporary transformation filters have been added that will handle the transformation of 3rd party blocks to and from the new core gallery block format.

Possible edge cases

The refactored Gallery format has been tested against the following sample block libraries that have existing transforms to and from the core Gallery block:

The following themes have also been tested to make sure that both the existing gallery content and the new format galleries display correctly:

  • TwentyNineteen
  • TwentyTwenty
  • TwentyTwentyOne
  • Astra
  • Arbutus

While the refactored gallery works effectively with these plugins and themes, there may be edge cases in other plugins and themes that have not been accounted for. Themes that heavily modify the gallery CSS based on the existing <ul><li></li></ul> will definitely need to be updated if the same style changes need to be applied to the new gallery format.  Therefore, it is recommended that theme and plugin authors test the changed gallery block well in advance of the 5.9 release.

Additional details about this change 

Previous discussions about this change can be found on the main pull request or call for testing.


Updated Oct 18 with additional information on Transformation, and CSS selectors.
Updated Oct 25 with instructions on how to access the new Gallery block while experimental

#5-9, #core-editor, #dev-notes, #gallery, #gutenberg