The WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. development team builds WordPress! Follow this site for general updates, status reports, and the occasional code debate. There’s lots of ways to contribute:
Found a bugbugA bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority.?Create a ticket in the bug tracker.
New execution lifecycle filters for the Abilities API in WordPress 7.1
WordPress 7.1 introduces four filters that allow plugins to customise the execution lifecycle of abilities registered with the Abilities APIAPIAn 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 Abilities API previously provided the wp_before_execute_ability and wp_after_execute_ability actions. These actions are useful for observing execution, but they cannot change its behaviour.
The new filters allow developers to:
Short-circuit ability execution.
Transform normalised input.
Apply additional authorisation rules.
Transform or recover an execution result.
These changes were introduced in TracTracAn open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.ticketticketCreated for both bug reports and feature development on the bug tracker.#64989 and changeset [62397].
Updated execution lifecycle
The filters are applied in the following order:
wp_pre_execute_ability
│
├── short-circuit when an override is returned
↓
WP_Ability::normalize_input()
↓
wp_ability_normalize_input
↓
WP_Ability::validate_input()
↓
WP_Ability::check_permissions()
↓
wp_ability_permission_result
↓
wp_before_execute_ability
↓
Registered execute callback
↓
wp_ability_execute_result
↓
WP_Ability::validate_output()
↓
wp_after_execute_ability
↓
Return result
Input and output transformations occur before their respective schema-validation steps. Transformed values must therefore continue to satisfy the ability’s registered schemas.
The exception is wp_pre_execute_ability, which bypasses the rest of the pipeline completely.
Short-circuiting execution with wp_pre_execute_ability
The wp_pre_execute_abilityfilterFilterFilters 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. runs at the beginning of WP_Ability::execute(), before input normalization, validation or permission checks.
/**
* Filters whether to short-circuit ability execution.
*
* @param mixed $pre Precomputed result. Return it unchanged to
* continue normal execution.
* @param string $ability_name Name of the ability.
* @param mixed $input Raw input passed to execute().
* @param WP_Ability $ability Ability instance.
*/
apply_filters(
'wp_pre_execute_ability',
$pre,
$ability_name,
$input,
$ability
);
Returning $pre unchanged allows execution to continue. Returning any other value short-circuits execution and returns that value directly to the caller.
The filter uses a unique internal sentinel as its default. This means that any PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher value—including null, false, or an object—can be used as a legitimate short-circuit result.
The filter can be used for caching, rate limiting, maintenance mode, approval workflows and test mocking.
Temporarily disabling an ability
The filter can short-circuit selected abilities during maintenance without running input validation, permission checks, or the registered callback:
add_filter(
'wp_pre_execute_ability',
function ( $pre, $ability_name, $input, $ability ) {
if ( 'my-plugin/sync-catalog' !== $ability_name ) {
return $pre;
}
if ( ! get_option( 'my_plugin_maintenance_mode', false ) ) {
return $pre;
}
return new WP_Error(
'ability_temporarily_unavailable',
__( 'This operation is temporarily unavailable due to maintenance.', 'my-plugin' ),
array(
'status' => 503,
)
);
},
10,
4
);
Returning $pre unchanged continues normal execution. When maintenance mode is enabled, the WP_Error is returned immediately, and the remaining ability pipeline is bypassed.
Because this filter runs before permission checks and validation, it should make only narrow decisions that do not depend on validated input or the current ability authorisation result.
Transforming input with wp_ability_normalize_input
The wp_ability_normalize_input filter runs inside WP_Ability::normalize_input(), after the method has applied any defaults declared by the input schema.
Add defaults that cannot be expressed through JSONJSONJSON, 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. Schema.
The transformed input is subsequently checked against the ability’s input_schema.
Returning a WP_Error stops execution before input validation, permission checks and the registered callback:
add_filter(
'wp_ability_normalize_input',
function ( $input, $ability_name ) {
if ( 'my-plugin/process-content' !== $ability_name ) {
return $input;
}
if ( my_plugin_rate_limit_exceeded() ) {
return new WP_Error(
'ability_rate_limit_exceeded',
__( 'The ability rate limit has been exceeded.', 'my-plugin' ),
array( 'status' => 429 )
);
}
return $input;
},
10,
2
);
When execution occurs through the Abilities REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”)
https://developer.wordpress.org/rest-api/, a WP_Error returned during normalization is now propagated by the REST controller. It defaults to HTTPHTTPHTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. status 400 unless the error specifies another status, such as 422 or 429.
Filtering permission results with wp_ability_permission_result
The wp_ability_permission_result filter runs inside WP_Ability::check_permissions(), after the registered permission_callback has executed.
/**
* Filters the result of an ability permission check.
*
* @param bool|WP_Error $permission Result from permission_callback.
* @param string $ability_name Name of the ability.
* @param mixed $input Input used for the permission check.
* @param WP_Ability $ability Ability instance.
*/
apply_filters(
'wp_ability_permission_result',
$permission,
$ability_name,
$input,
$ability
);
The filter may return:
true to permit execution.
false to deny execution.
A WP_Error to deny execution with a specific error and message.
Any other return value is converted to false.
Because the filter is part of check_permissions(), it also applies when permission checks are performed independently of execute(), including REST API and WP-CLIWP-CLIWP-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/ integrations.
Applying an additional authorisation policy
add_filter(
'wp_ability_permission_result',
function ( $permission, $ability_name, $input, $ability ) {
if ( 'my-plugin/delete-records' !== $ability_name ) {
return $permission;
}
// preserve both false and WP_Error results
if ( false === $permission || is_wp_error( $permission ) ) {
return $permission;
}
if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error(
'ability_additional_permission_required',
__( 'This operation requires administrator access.', 'my-plugin' )
);
}
return true;
},
10,
4
);
Plugins should exercise particular care with this filter because returning true can override a denial from the ability’s original permission_callback.
Transforming results with wp_ability_execute_result
The wp_ability_execute_result filter runs after the ability’s registered execution callback and before output validation.
/**
* Filters the result returned by an ability execute callback.
*
* @param mixed $result Result returned by the execute callback,
* or WP_Error when execution failed.
* @param string $ability_name Name of the ability.
* @param mixed $input Normalized input.
* @param WP_Ability $ability Ability instance.
*/
apply_filters(
'wp_ability_execute_result',
$result,
$ability_name,
$input,
$ability
);
The filtered result is subsequently validated against the ability’s output_schema.
Recovering from selected execution failures
The filter receives WP_Error values produced by the registered callback, so plugins may implement narrowly scoped recovery behaviour:
add_filter(
'wp_ability_execute_result',
function ( $result, $ability_name, $input, $ability ) {
if (
'my-plugin/get-remote-data' !== $ability_name ||
! is_wp_error( $result ) ||
'remote_service_unavailable' !== $result->get_error_code()
) {
return $result;
}
$fallback = my_plugin_get_fallback_data();
/*
* The fallback must conform to the ability's registered
* output_schema because it will be validated after this filter.
*/
return $fallback;
},
10,
4
);
Any recovered value must still conform to the registered output_schema.
New WP_Filter_Sentinel class
WordPress 7.1 also introduces WP_Filter_Sentinel, a reusable marker class loaded alongside WP_Hook.
CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. uses a unique sentinel instance as the default value for wp_pre_execute_ability. Comparing object identity allows Core to distinguish an unchanged default from every possible user-supplied value, including null, false, arrays and arbitrary objects.
Developers using wp_pre_execute_ability do not need to instantiate this class. To continue normal execution, callbacks should simply return the received $pre value unchanged.
Backward compatibility
These changes are additive:
Existing abilities require no changes.
Existing wp_before_execute_ability and wp_after_execute_ability callbacks continue to work.
Ability callbacks and permission callbacks retain their existing behaviour when none of the new filters is used.
Input and output schemas remain the final validation boundaries for normal execution.
Plugins that already provide their own ability-execution hooksHooksIn 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. may wish to evaluate whether some functionality can now use these Core filters. Protocol- or domain-specific hooks can still be layered on top when they need more specialised context.
Summary
WordPress 7.1 adds the following Abilities API filters:
Filter
Purpose
wp_pre_execute_ability
Short-circuit the complete execution pipeline
wp_ability_normalize_input
Transform normalized input before validation
wp_ability_permission_result
Modify or override permission results
wp_ability_execute_result
Transform or recover results before output validation
These filters make the Abilities API more extensibleExtensibleThis is the ability to add additional functionality to the code. Plugins extend the WordPress core software. for AI integrations, automation systems, protocol adapters, authorisation layers and other tools that mediate ability execution.
For additional context, see Trac ticket #64989 and changeset [62397].