Opened 4 weeks ago
Last modified 3 hours ago
#65823 new enhancement
Code Quality: Use the null coalescing assignment operator (??=)
| Reported by: | Soean | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.2 |
| Component: | General | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: | coding-standards |
Description
Core still contains a number of places using the verbose "assign a default only if not already set" pattern, either as a self-referencing null coalescing assignment or as an isset() guard:
<?php $args['title'] = $args['title'] ?? ''; if ( ! isset( $metadata['sizes'] ) ) { $metadata['sizes'] = array(); }
These can be written with the null coalescing assignment operator (??=):
<?php $args['title'] ??= ''; $metadata['sizes'] ??= array();
Both forms are functionally identical, but ??= is shorter and states the intent directly.
Notes:
??=requires PHP 7.4, which is already the minimum supported version for WordPress.- The operator is already used in core, e.g. in
wp-includes/l10n.phpandwp-includes/view-config.php, so this only makes existing usage consistent. - No behavior change; this is a purely internal simplification.
Change History (11)
This ticket was mentioned in PR #12603 on WordPress/wordpress-develop by @Soean.
4 weeks ago
#1
- Keywords has-unit-tests added
This ticket was mentioned in PR #12889 on WordPress/wordpress-develop by @Soean.
4 weeks ago
#2
## Description
Replaces the null-guard singleton pattern in get_instance() accessors with the null coalescing assignment operator (??=):
// Before public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } // After public static function get_instance() { self::$instance ??= new self(); return self::$instance; }
Both forms are functionally identical; ??= is shorter and states the intent directly.
## Why this is safe
??=requires PHP 7.4, which is already the minimum supported version.- The operator short-circuits: the right-hand side is only evaluated when the left-hand side is
nullor not set, so the object is still instantiated exactly once. - The operator is already used elsewhere in core, e.g. in
wp-includes/l10n.phpandwp-includes/view-config.php, so this only makes existing usage consistent. - No behavior change.
## Intentionally not changed
WP_Abilities_Registry::get_instance() and WP_Ability_Categories_Registry::get_instance() use the same guard, but their blocks contain additional side effects beyond the assignment:
if ( null === self::$instance ) { self::$instance = new self(); WP_Ability_Categories_Registry::get_instance(); do_action( 'wp_abilities_api_init', self::$instance ); }
Converting these to ??= would cause the action and the nested registry initialization to run on every call instead of only on the first, so both are left as-is.
Trac ticket: https://core.trac.wordpress.org/ticket/65823
This ticket was mentioned in PR #12911 on WordPress/wordpress-develop by @Soean.
4 weeks ago
#3
## Description
Replacing further isset()/null guards that assign a default value with the null coalescing assignment operator (??=):
// Before if ( ! isset( $size_data['width'] ) ) { $size_data['width'] = null; } if ( ! isset( $size_data['height'] ) ) { $size_data['height'] = null; } if ( ! isset( $size_data['crop'] ) ) { $size_data['crop'] = false; } // After $size_data['width'] ??= null; $size_data['height'] ??= null; $size_data['crop'] ??= false;
Both forms are functionally identical; ??= is shorter and turns a nine-line block into three lines that read as a single list of defaults.
## Scope
This is deliberately limited to blocks where *every* guard in a contiguous run can be converted, so no mixed styles are left behind within one function:
WP_Image_Editor_GD::resize()andWP_Image_Editor_Imagick::resize()— the$size_datadefaults (identical blocks in both classes).WP_Date_Query::build_mysql_datetime()— the$datetimedefaults.WP_Translation_Controller— the$localefallback inload_file(),is_textdomain_loaded(),get_files()andhas_translation().register_post_status()— the$argsdefaults.
## Why this is safe
??=requires PHP 7.4, which is already the minimum supported version.??=and! isset()test the same condition, so the default is applied in exactly the same cases.- Assignment order is preserved everywhere it matters. In
register_post_status()the compound guard above the block is left untouched and still runs first,$args->internal ??= false;still follows it, andpublicly_queryableis still derived afterpublichas been resolved. - In
WP_Date_Query::build_mysql_datetime()the precedingarray_map( 'absint', $datetime )turns any existingnullinto0, which bothisset()and??=treat as set. - No behavior change.
## Not converted
Similar runs in WP_Query::parse_query(), WP_Post_Type::set_props() and WP_Taxonomy::set_props() contain guards that cannot be expressed as ??= — an elseif branch in the first case, compound conditions such as if ( null === $args['show_in_menu'] || ! $args['show_ui'] ) in the other two. Converting only the neighbouring guards would leave two styles interleaved in the same block, so those are left alone.
Trac ticket: https://core.trac.wordpress.org/ticket/65823
This ticket was mentioned in PR #12912 on WordPress/wordpress-develop by @Soean.
4 weeks ago
#4
## Description
Covering one recognizable idiom: initializing a container to an empty array before writing into it.
-if ( ! isset( $wp_meta_boxes[ $page ] ) ) { - $wp_meta_boxes[ $page ] = array(); -} +$wp_meta_boxes[ $page ] ??= array();
Every one of the 58 changed sites has exactly this shape, an isset() guard whose entire body is a single = array(); assignment to the tested expression. There is no per-site judgement involved, which makes the change easy to verify: confirm the rule once, then scan.
## Why this is safe
??=requires PHP 7.4, which is already the minimum supported version.??=and! isset()test the same condition, so the array is created in exactly the same cases.- Only blocks whose body is a single assignment were converted. Any guard containing further statements was skipped, since
??=would let those statements run unconditionally. - Nested initializations keep their order, so parent arrays are still created before their children:
$wp_meta_boxes ??= array(); $wp_meta_boxes[ $page ] ??= array(); $wp_meta_boxes[ $page ][ $context ] ??= array();
- No behavior change.
Trac ticket: https://core.trac.wordpress.org/ticket/65823
@SergeyBiryukov commented on PR #12889:
4 days ago
#7
Thanks for the PR! Merged in r63443.
@SergeyBiryukov commented on PR #12911:
33 hours ago
#9
Thanks for the PR! Merged in r63501.
@SergeyBiryukov commented on PR #12603:
3 hours ago
#11
Thanks for the PR! Merged in r63509.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
## Description
Replaces verbose "assign a default only if not already set" patterns with the null coalescing assignment operator (
??=):Both forms are functionally identical;
??=is shorter and clearer.## Why this is safe
??=requires PHP 7.4, which is already the minimum.wp-includes/l10n.php,wp-includes/view-config.php), so this only makes existing usage consistent.Trac ticket: https://core.trac.wordpress.org/ticket/65823