From f12128ee75e3d4b02e878cac5c254eeb9933d161 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 16:06:52 +0000 Subject: [PATCH 1/5] Exclude wp-compat false positives from the PHPStan config The johnbillion/wp-compat extension checks every WordPress symbol against the WordPress 4.9 baseline that wp-cli-tests configures, and only recognizes function_exists() and method_exists() guards. It therefore cannot see the `before_invoke` version checks in entity-command.php or the polyfills in src/Compat/, which made it report 106 errors. Ignore the false positives per file and per error identifier, documenting the reason for each group: * WP_Block_Processor is polyfilled in src/Compat/ and loaded on demand. * `wp font *`, `wp user application-password`, `wp user privacy-request`, `wp site meta` and `wp post block` all abort on older WordPress versions through `before_invoke`. * Only the uppercase 'ID' alias for the $field parameter of get_term_by() is WordPress 5.5+, and these call sites pass 'id', 'term_id' or 'slug'. * WordPress 6.0 and 5.3 merely formalized the already documented `...$args` parameters of apply_filters() and wpdb::prepare(). * Older WordPress versions silently ignore the extra $force_cache argument passed to wp_load_alloptions(). Two of the reported errors are not false positives. They are ignored in separate, narrowly scoped entries so that the check stays enabled for the rest of those files: * serialize_blocks() arrived in WordPress 5.3.1, later than the WordPress 5.0 `before_invoke` check on `wp post block`, so the subcommands that write post content back fatal on WordPress 5.0 - 5.3.0. * `wp post` has no `before_invoke` check at all, so `wp post has-block` fatals on WordPress 4.9. Co-authored-by: Pascal Birchler --- phpstan.neon.dist | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 1ebe3bd32..a7f5f1bd9 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -19,3 +19,99 @@ parameters: - identifier: missingType.property - identifier: missingType.parameter - identifier: missingType.return + + # johnbillion/wp-compat checks every WordPress symbol against the WordPress 4.9 + # baseline that wp-cli-tests configures. It only recognizes function_exists() and + # method_exists() guards, so it cannot see the `before_invoke` version checks in + # entity-command.php or the polyfills in src/Compat/, and reports those as errors. + + # `WP_Block_Processor` and its dependencies are polyfilled in src/Compat/ and + # loaded on demand through `BlockProcessorLoader::load()`, so they are available + # on every supported WordPress version, not just 6.9+. + - + identifier: WPCompat.methodNotAvailable + path: src/Block_Processor_Helper.php + + # `wp font collection|face|family` aborts on WordPress < 6.5 via `before_invoke`. + - + identifier: WPCompat.methodNotAvailable + paths: + - src/Font_Collection_Command.php + - src/Font_Family_Command.php + + # `wp user application-password` aborts on WordPress < 5.6 via `before_invoke`. + # The single WordPress 5.7 method is additionally behind a `wp_version_compare()` + # check in `application_name_exists_for_user()`. + - + identifier: WPCompat.methodNotAvailable + path: src/User_Application_Password_Command.php + + # `wp user privacy-request` aborts on WordPress < 4.9.6 via `before_invoke`. + - + identifier: WPCompat.functionNotAvailable + path: src/User_Privacy_Request_Command.php + + # `wp site meta` aborts via `before_invoke` unless `is_site_meta_supported()` + # exists, which is the WordPress 5.1 function that introduced site meta support + # along with the `*_site_meta()` functions used here. + - + identifier: WPCompat.functionNotAvailable + path: src/Site_Meta_Command.php + + # `wp post block` aborts on WordPress < 5.0 via `before_invoke`. + - + identifier: WPCompat.functionNotAvailable + message: '#^(has_blocks|parse_blocks|render_block)\(\) is only available since WordPress version 5\.0\.0\.$#' + path: src/Post_Block_Command.php + + # Only the uppercase `'ID'` alias for the `$field` parameter of `get_term_by()` + # is WordPress 5.5+. These call sites pass `'id'`, `'term_id'` or `'slug'`, all + # supported since WordPress 2.3. + - + identifier: WPCompat.parameterNotAvailable.gettermby.field + paths: + - src/Menu_Item_Command.php + - src/Term_Command.php + - src/WP_CLI/CommandWithTerms.php + + # WordPress 6.0 only formalized the already documented `...$args` parameter of + # `apply_filters()` by adding it to the function signature. Additional arguments + # were collected through `func_get_args()` before that. + - + identifier: WPCompat.parameterNotAvailable.applyfilters.args + path: src/Post_Block_Command.php + + # Same for `wpdb::prepare()`, where WordPress 5.3 formalized the already + # documented `...$args` parameter. Passing a single array of values is even + # handled explicitly by `wpdb::prepare()` itself. + - + identifier: WPCompat.parameterNotAvailable.wpdbprepare.args + path: src/Site_Command.php + + # The `$force_cache` parameter of `wp_load_alloptions()` is WordPress 5.3.1+, but + # older versions simply ignore the extra argument. The cache refresh in + # `wp option set-autoload` degrades to a no-op there instead of failing. + - + identifier: WPCompat.parameterNotAvailable.wploadalloptions.forcecache + path: src/Option_Command.php + + # The two entries below are not false positives. They are real gaps against the + # WordPress 4.9 baseline, ignored here so that wp-compat stays enabled for the + # rest of these files. + + # `serialize_blocks()` arrived in WordPress 5.3.1, later than the WordPress 5.0 + # `before_invoke` check on `wp post block`, so the subcommands that write post + # content back fatal on WordPress 5.0 - 5.3.0. Raising that check to 5.3.1 fixes it. + - + identifier: WPCompat.functionNotAvailable + message: '#^serialize_blocks\(\) is only available since WordPress version 5\.3\.1\.$#' + path: src/Post_Block_Command.php + + # `wp post` has no `before_invoke` version check, so `wp post has-block` fatals on + # WordPress < 5.0. Its sibling `wp post has-blocks` goes through + # `Block_Processor_Helper::has_blocks()`; `Block_Processor_Helper::has_block()` + # exists and would do the same job here. + - + identifier: WPCompat.functionNotAvailable + message: '#^has_block\(\) is only available since WordPress version 5\.0\.0\.$#' + path: src/Post_Command.php From d7ea5dfcb899f364e351a3b16aac08f5fcf25001 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 18:50:19 +0000 Subject: [PATCH 2/5] Guard the block functions that the command version checks miss Two of the wp-compat findings were real gaps rather than false positives, so guard the calls instead of ignoring them. `function_exists()` is what wp-compat understands, so the guards double as the fix and remove the need for the two ignores. `serialize_blocks()` was introduced in WordPress 5.3.1, later than the WordPress 5.0 `before_invoke` check on `wp post block`, so `update`, `insert`, `remove`, `move`, `replace`, `clone` and `import` fataled on WordPress 5.0 - 5.3.0. Route those seven call sites through a private wrapper that checks for the function first. Keeping the check in the wrapper rather than raising the `before_invoke` version leaves the read-only subcommands working on WordPress 5.0 - 5.3.0. `wp post` carries no `before_invoke` version check at all, so `wp post has-block` fataled on WordPress 4.9. Check for `has_block()` up front and error out with the usual message instead. Co-authored-by: Pascal Birchler --- phpstan.neon.dist | 25 +++---------------------- src/Post_Block_Command.php | 33 ++++++++++++++++++++++++++------- src/Post_Command.php | 4 ++++ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a7f5f1bd9..00f66aa9b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -58,7 +58,9 @@ parameters: identifier: WPCompat.functionNotAvailable path: src/Site_Meta_Command.php - # `wp post block` aborts on WordPress < 5.0 via `before_invoke`. + # `wp post block` aborts on WordPress < 5.0 via `before_invoke`. Deliberately + # scoped to these three functions: `serialize_blocks()` is WordPress 5.3.1+ and is + # guarded separately, so it must keep being reported if that guard ever goes away. - identifier: WPCompat.functionNotAvailable message: '#^(has_blocks|parse_blocks|render_block)\(\) is only available since WordPress version 5\.0\.0\.$#' @@ -94,24 +96,3 @@ parameters: - identifier: WPCompat.parameterNotAvailable.wploadalloptions.forcecache path: src/Option_Command.php - - # The two entries below are not false positives. They are real gaps against the - # WordPress 4.9 baseline, ignored here so that wp-compat stays enabled for the - # rest of these files. - - # `serialize_blocks()` arrived in WordPress 5.3.1, later than the WordPress 5.0 - # `before_invoke` check on `wp post block`, so the subcommands that write post - # content back fatal on WordPress 5.0 - 5.3.0. Raising that check to 5.3.1 fixes it. - - - identifier: WPCompat.functionNotAvailable - message: '#^serialize_blocks\(\) is only available since WordPress version 5\.3\.1\.$#' - path: src/Post_Block_Command.php - - # `wp post` has no `before_invoke` version check, so `wp post has-block` fatals on - # WordPress < 5.0. Its sibling `wp post has-blocks` goes through - # `Block_Processor_Helper::has_blocks()`; `Block_Processor_Helper::has_block()` - # exists and would do the same job here. - - - identifier: WPCompat.functionNotAvailable - message: '#^has_block\(\) is only available since WordPress version 5\.0\.0\.$#' - path: src/Post_Command.php diff --git a/src/Post_Block_Command.php b/src/Post_Block_Command.php index ebca6e454..89732a8e6 100644 --- a/src/Post_Block_Command.php +++ b/src/Post_Block_Command.php @@ -280,7 +280,7 @@ public function update( $args, $assoc_args ) { $blocks[ $original_idx ] = $block; // @phpstan-ignore argument.type - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -398,7 +398,7 @@ public function move( $args, $assoc_args ) { // Insert at new position. array_splice( $blocks, $insert_pos, 0, [ $block_to_move ] ); - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -691,7 +691,7 @@ function ( $block ) { } } - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -975,7 +975,7 @@ public function clone_block( $args, $assoc_args ) { array_splice( $blocks, (int) $insert_pos, 0, [ $cloned_block ] ); // @phpstan-ignore argument.type - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ @@ -1424,7 +1424,7 @@ public function insert( $args, $assoc_args ) { } // @phpstan-ignore argument.type - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1567,7 +1567,7 @@ function ( $idx ) use ( $index_map ) { return; } - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1681,7 +1681,7 @@ public function replace( $args, $assoc_args ) { } // @phpstan-ignore argument.type - $new_content = serialize_blocks( $blocks ); + $new_content = $this->serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1702,6 +1702,25 @@ public function replace( $args, $assoc_args ) { } } + /** + * Serializes blocks back into post content. + * + * The command itself requires WordPress 5.0, but `serialize_blocks()` was only + * introduced in WordPress 5.3.1. + * + * @param array $blocks Array of blocks. + * @return string Serialized post content. + * + * @phpstan-param array $blocks + */ + private function serialize_blocks( $blocks ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + + return serialize_blocks( $blocks ); + } + /** * Strips innerHTML and innerContent from blocks recursively. * diff --git a/src/Post_Command.php b/src/Post_Command.php index 1880f24b5..540883cae 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -1430,6 +1430,10 @@ public function has_blocks( $args, $assoc_args ) { * @subcommand has-block */ public function has_block( $args, $assoc_args ) { + if ( ! function_exists( 'has_block' ) ) { + WP_CLI::error( 'Requires WordPress 5.0 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $block_name = $args[1]; From f7441a82d431c6f46906d67f7007784f796a6053 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 19:04:56 +0000 Subject: [PATCH 3/5] Bail out of the block-mutating subcommands before doing any work The previous approach routed the seven mutating subcommands through a private `serialize_blocks()` wrapper, which meant the WordPress 5.3.1 check only ran after the command had already fetched the post, parsed every block and applied the requested changes. For `wp post block update` that also fired the `wp_cli_post_block_update_html` filter, so third-party callbacks loaded through `--require` ran for a command that was always going to fail. Drop the wrapper and check for `serialize_blocks()` as the first statement of `update`, `insert`, `remove`, `move`, `replace`, `clone` and `import` instead, so they abort before touching anything. The read-only subcommands keep working on WordPress 5.0 - 5.3.0, and wp-compat still sees the guard, so no PHPStan ignore is needed. Co-authored-by: Pascal Birchler --- src/Post_Block_Command.php | 61 ++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/Post_Block_Command.php b/src/Post_Block_Command.php index 89732a8e6..4607e66d1 100644 --- a/src/Post_Block_Command.php +++ b/src/Post_Block_Command.php @@ -224,6 +224,10 @@ public function get( $args, $assoc_args ) { * @subcommand update */ public function update( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $index = (int) $args[1]; $blocks = parse_blocks( $post->post_content ); @@ -280,7 +284,7 @@ public function update( $args, $assoc_args ) { $blocks[ $original_idx ] = $block; // @phpstan-ignore argument.type - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -340,6 +344,10 @@ public function update( $args, $assoc_args ) { * @subcommand move */ public function move( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $from_index = (int) $args[1]; $to_index = (int) $args[2]; @@ -398,7 +406,7 @@ public function move( $args, $assoc_args ) { // Insert at new position. array_splice( $blocks, $insert_pos, 0, [ $block_to_move ] ); - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -607,6 +615,10 @@ function ( $block ) { * @subcommand import */ public function import( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $file = Utils\get_flag_value( $assoc_args, 'file', null ); $position = Utils\get_flag_value( $assoc_args, 'position', 'end' ); @@ -691,7 +703,7 @@ function ( $block ) { } } - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -914,6 +926,10 @@ public function count( $args, $assoc_args ) { * @subcommand clone */ public function clone_block( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $source_index = (int) $args[1]; $position = Utils\get_flag_value( $assoc_args, 'position', 'after' ); @@ -975,7 +991,7 @@ public function clone_block( $args, $assoc_args ) { array_splice( $blocks, (int) $insert_pos, 0, [ $cloned_block ] ); // @phpstan-ignore argument.type - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ @@ -1393,6 +1409,10 @@ public function list_( $args, $assoc_args ) { * @subcommand insert */ public function insert( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $block_name = $args[1]; $content = Utils\get_flag_value( $assoc_args, 'content', '' ); @@ -1424,7 +1444,7 @@ public function insert( $args, $assoc_args ) { } // @phpstan-ignore argument.type - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1492,6 +1512,10 @@ public function insert( $args, $assoc_args ) { * @subcommand remove */ public function remove( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $block_name = isset( $args[1] ) ? $args[1] : null; $indices = Utils\get_flag_value( $assoc_args, 'index', null ); @@ -1567,7 +1591,7 @@ function ( $idx ) use ( $index_map ) { return; } - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1637,6 +1661,10 @@ function ( $idx ) use ( $index_map ) { * @subcommand replace */ public function replace( $args, $assoc_args ) { + if ( ! function_exists( 'serialize_blocks' ) ) { + WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); + } + $post = $this->fetcher->get_check( $args[0] ); $old_block_name = $args[1]; $new_block_name = $args[2]; @@ -1681,7 +1709,7 @@ public function replace( $args, $assoc_args ) { } // @phpstan-ignore argument.type - $new_content = $this->serialize_blocks( $blocks ); + $new_content = serialize_blocks( $blocks ); $result = wp_update_post( [ 'ID' => $post->ID, @@ -1702,25 +1730,6 @@ public function replace( $args, $assoc_args ) { } } - /** - * Serializes blocks back into post content. - * - * The command itself requires WordPress 5.0, but `serialize_blocks()` was only - * introduced in WordPress 5.3.1. - * - * @param array $blocks Array of blocks. - * @return string Serialized post content. - * - * @phpstan-param array $blocks - */ - private function serialize_blocks( $blocks ) { - if ( ! function_exists( 'serialize_blocks' ) ) { - WP_CLI::error( 'Requires WordPress 5.3.1 or greater.' ); - } - - return serialize_blocks( $blocks ); - } - /** * Strips innerHTML and innerContent from blocks recursively. * From c24929442996628c2edfeef18773e8dbfd2c783e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 19:17:43 +0000 Subject: [PATCH 4/5] Cover the block version guards on WordPress 4.9 The functional test matrix runs WordPress 4.9, so the `wp post has-block` guard can be asserted directly. The scenario also pins the deliberate asymmetry between the two subcommands: `wp post has-blocks` keeps working because it goes through the bundled WP_Block_Processor polyfill, while `wp post has-block` depends on the WordPress 5.0 `has_block()` function and now reports that requirement instead of fataling. The WordPress 5.3.1 `serialize_blocks()` guard cannot be covered the same way. It only triggers between WordPress 5.0 and 5.3.0, and the matrix runs 4.9, 6.9, latest and trunk, so a scenario for it would never execute. Co-authored-by: Pascal Birchler --- features/post-block.feature | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/features/post-block.feature b/features/post-block.feature index cf2ff2301..e0b5daac2 100644 --- a/features/post-block.feature +++ b/features/post-block.feature @@ -47,6 +47,27 @@ Feature: Manage blocks in post content """ And the return code should be 1 + @less-than-wp-5.0 + Scenario: Checking for a specific block requires WordPress 5.0 + Given a WP install + When I run `wp post create --post_title="Block Post" --post_content="

Hello

" --porcelain` + Then save STDOUT as {POST_ID} + + # has-blocks goes through the bundled WP_Block_Processor polyfill, so it keeps working. + When I run `wp post has-blocks {POST_ID}` + Then STDOUT should contain: + """ + Success: Post {POST_ID} contains blocks. + """ + + # has-block relies on the has_block() function that WordPress only added in 5.0. + When I try `wp post has-block {POST_ID} core/paragraph` + Then STDERR should contain: + """ + Error: Requires WordPress 5.0 or greater. + """ + And the return code should be 1 + @require-wp-5.0 Scenario: Parse blocks in a post Given a WP install From facabab1aea1a39b2cad22881bc09ce5c0c75878 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 21:25:13 +0000 Subject: [PATCH 5/5] Scope the wp-compat ignores to the symbols they cover The remaining file-wide entries matched every `WPCompat.methodNotAvailable` or `WPCompat.functionNotAvailable` diagnostic in their files. Each command really is gated on a `before_invoke` version check, but a future call to something introduced later than that gate would have been swallowed silently. Match on the message as well, naming both the symbols and the version each command is gated on, so anything newer keeps being reported. The `wp user application-password` group splits in two as a result, separating the WordPress 5.6 methods covered by `before_invoke` from the single 5.7 method that has its own `wp_version_compare()` check. Co-authored-by: Pascal Birchler --- phpstan.neon.dist | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 00f66aa9b..05ea1c2eb 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -20,35 +20,49 @@ parameters: - identifier: missingType.parameter - identifier: missingType.return + # johnbillion/wp-compat checks every WordPress symbol against the WordPress 4.9 # baseline that wp-cli-tests configures. It only recognizes function_exists() and # method_exists() guards, so it cannot see the `before_invoke` version checks in # entity-command.php or the polyfills in src/Compat/, and reports those as errors. + # + # Every entry below matches on the message as well as the identifier, so that a call + # to something introduced later than the version a command is gated on still gets + # reported rather than swallowed by a file-wide ignore. - # `WP_Block_Processor` and its dependencies are polyfilled in src/Compat/ and - # loaded on demand through `BlockProcessorLoader::load()`, so they are available - # on every supported WordPress version, not just 6.9+. + # `WP_Block_Processor` and its dependencies are polyfilled in src/Compat/ and loaded + # on demand through `BlockProcessorLoader::load()`, so they are available on every + # supported WordPress version, not just 6.9+. - identifier: WPCompat.methodNotAvailable + message: '#^WP_Block_Processor::(allocate_and_return_parsed_attributes|extract_full_block_and_advance|get_block_type|get_delimiter_type|get_depth|get_span|next_block)\(\) is only available since WordPress version 6\.9\.0\.$#' path: src/Block_Processor_Helper.php # `wp font collection|face|family` aborts on WordPress < 6.5 via `before_invoke`. - identifier: WPCompat.methodNotAvailable + message: '#^(WP_Font_Collection::get_data|WP_Font_Library::(get_font_collection|get_font_collections|get_instance))\(\) is only available since WordPress version 6\.5\.0\.$#' paths: - src/Font_Collection_Command.php - src/Font_Family_Command.php # `wp user application-password` aborts on WordPress < 5.6 via `before_invoke`. + - + identifier: WPCompat.methodNotAvailable + message: '#^WP_Application_Passwords::(create_new_application_password|delete_all_application_passwords|delete_application_password|get_user_application_password|get_user_application_passwords|record_application_password_usage|update_application_password)\(\) is only available since WordPress version 5\.6\.0\.$#' + path: src/User_Application_Password_Command.php + # The single WordPress 5.7 method is additionally behind a `wp_version_compare()` - # check in `application_name_exists_for_user()`. + # check in `application_name_exists_for_user()`, which reimplements it for 5.6. - identifier: WPCompat.methodNotAvailable + message: '#^WP_Application_Passwords::application_name_exists_for_user\(\) is only available since WordPress version 5\.7\.0\.$#' path: src/User_Application_Password_Command.php # `wp user privacy-request` aborts on WordPress < 4.9.6 via `before_invoke`. - identifier: WPCompat.functionNotAvailable + message: '#^(_wp_privacy_completed_request|wp_create_user_request|wp_get_user_request_data|wp_privacy_exports_dir|wp_privacy_generate_personal_data_export_file|wp_send_user_request)\(\) is only available since WordPress version 4\.9\.6\.$#' path: src/User_Privacy_Request_Command.php # `wp site meta` aborts via `before_invoke` unless `is_site_meta_supported()` @@ -56,11 +70,12 @@ parameters: # along with the `*_site_meta()` functions used here. - identifier: WPCompat.functionNotAvailable + message: '#^(add_site_meta|delete_site_meta|get_site_meta|update_site_meta)\(\) is only available since WordPress version 5\.1\.0\.$#' path: src/Site_Meta_Command.php - # `wp post block` aborts on WordPress < 5.0 via `before_invoke`. Deliberately - # scoped to these three functions: `serialize_blocks()` is WordPress 5.3.1+ and is - # guarded separately, so it must keep being reported if that guard ever goes away. + # `wp post block` aborts on WordPress < 5.0 via `before_invoke`. `serialize_blocks()` + # is WordPress 5.3.1+ and is guarded separately in the mutating subcommands, so it + # must keep being reported if that guard ever goes away. - identifier: WPCompat.functionNotAvailable message: '#^(has_blocks|parse_blocks|render_block)\(\) is only available since WordPress version 5\.0\.0\.$#'