Skip to content

Abilities: Add support for ability deprecation - #10507

Open
JasonTheAdams wants to merge 3 commits into
WordPress:trunkfrom
JasonTheAdams:deprecated-abilities
Open

Abilities: Add support for ability deprecation#10507
JasonTheAdams wants to merge 3 commits into
WordPress:trunkfrom
JasonTheAdams:deprecated-abilities

Conversation

@JasonTheAdams

@JasonTheAdams JasonTheAdams commented Nov 11, 2025

Copy link
Copy Markdown
Member

This PR adds a standard way to deprecate an ability without removing it.

Proposed changes

  • Add a meta.deprecated property. Its default value is false.
  • Use an array for deprecated abilities. The array can contain since, replacement, and message.
  • Require at least one deprecation detail and validate the values during registration.
  • Add _deprecated_ability() and show a deprecation notice when the ability runs.
  • Keep deprecated abilities available by exact name for backward compatibility.
  • Include deprecated abilities in discovery by default. Callers can include or exclude them with wp_get_abilities() or REST API filters.
  • Expose deprecation details in the REST API schema.

Why use structured metadata?

false clearly means that an ability is active. An array keeps all migration details together and makes them available to PHP, REST API clients, and other tools without parsing a message.

This follows the general pattern used by @wordpress/deprecated, which also stores deprecation details as structured options. The proposed ability API uses a smaller set of fields that fits server-side ability metadata.

Tests

The tests cover metadata defaults and validation, execution notices, exact-name execution, discovery filtering, and REST API behavior.

Trac ticket: https://core.trac.wordpress.org/ticket/64209

@github-actions

Copy link
Copy Markdown

Hi @JasonTheAdams! 👋

Thank you for your contribution to WordPress! 💖

It looks like this is your first pull request to wordpress-develop. Here are a few things to be aware of that may help you out!

No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description.

Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making.

More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook.

Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook.

If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook.

The Developer Hub also documents the various coding standards that are followed:

Thank you,
The WordPress Project

@JasonTheAdams

Copy link
Copy Markdown
Member Author

Some topics to discuss:

  1. Should we add a way for an Ability to mark which version it was deprecated in?
  2. Should we add a way for an Ability to specify which Ability should be used to replace it?
  3. How do we want to handle filtering in wp_get_abiliites()?

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@JasonTheAdams

Copy link
Copy Markdown
Member Author

Version

When looking over _deprecated_function() I saw that it has a $version parameter. I'm honestly not sure how useful that is here, especially since most abilities won't be introduced by WordPress.

If it is useful, then we'd need a way for an Ability to note the version, since this function is called automatically within do_execute(). I threw in a meta.deprecated_version in there for now, but I could also see allowing:

meta.deprecated = false;

meta.deprecated = true;

meta.deprecated = array(
	'version' => '1.2.3',
	'replacement' => 'foo/v2/my-ability'
); // implied true with further information

I'm kind of partial to the array structure as it only adds a single property to meta and is nice and clear.

Replacement

This does feel more valuable, as it would be nice for someone using an Ability to know which Ability is intended to be its successor, if there is one available. As above, we could have a meta.deprecated_replacement type of property, or else include it in the array structure.

Filtering in wp_get_abilities()

We purposely put off filtering in a previous bit of work because we realized it shouldn't act like WP_Query or something like that since this is all in memory. We put it off as we didn't see an immediate need to provide filtering since the person could just do array_filter( wp_get_abilities(), function() { ... } to get what they want.

That argument still applies here, but if the intent is to have deprecated abilities hidden by default, then that method falls apart. So we either have to introduce filtering or else not include it and have it such that folks filter it out themselves if that's their intent. The REST endpoint would work fine this way. The issue with this is that if we introduce filtering later and want deprecated abilities filtered by default, then we're changing the function's behavior.

We could just introduce a wp_get_abilities( array $args ) which is array( 'deprecated' => false ' ) by default, so passing wp_get_abilities( array() ) would get everything. At that point we can think about further filters later. But I could even be talked into a plain $include_deprecated = false boolean parameter, as I still think that most filtering is better done with something like array_filter().

Open to thoughts!

@justlevine

Copy link
Copy Markdown

Hey @JasonTheAdams I touched a bit on your more general comments over on https://core.trac.wordpress.org/ticket/64209#comment:13, but tl;dr I agree with your comparison to ability filtering, and think any deprecation pattern should also be done as a holistic enhancement once the needs are more clearly defined. Which yeah will prob only come after/alongside a basic mechanism for filtering (but IMO no need to tunnel vision that in the rush to ship this)

(Syndication is one directional to Trac, replying here to signpost future visitors.)

PS: congrats on your "first contribution to WordPress" 🎉🙃🎉

@gziolo

gziolo commented Aug 18, 2026

Copy link
Copy Markdown
Member

The branch has been updated and rebased onto the latest trunk. Here is a short summary of the changes now proposed:

  • Add meta.deprecated, with false as the default value.
  • Store deprecation details in an array containing since, replacement, and/or message.
  • Validate the metadata during ability registration.
  • Show a standard WordPress deprecation notice when a deprecated ability runs.
  • Keep deprecated abilities available by exact name for backward compatibility.
  • Keep them in discovery by default, with explicit filtering available through wp_get_abilities() and the REST API.
  • Expose the deprecation details in the REST API schema.

The structured metadata follows the general pattern used by @wordpress/deprecated. It gives PHP and REST API clients useful migration details without requiring them to parse a message.

Tests cover validation, defaults, execution notices, exact-name execution, discovery filtering, and REST API behavior. The new and changed API documentation uses @since 7.2.0.

A fresh CI run was triggered by the updated branch.

Feedback welcome

@justlevine and @JasonTheAdams, could you please review this revised version when you have time? I would especially value your feedback on the meta.deprecated structure and the default discovery behavior. I am happy to iterate on the API and implementation based on your feedback.

This comment was marked as outdated.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/wp-includes/abilities-api/class-wp-ability.php:399

  • replacement is documented and exposed as a namespaced ability name, but this validation accepts any non-empty string (for example, not an ability). That leaves consumers and deprecation notices with an unusable migration target. Validate this field with the same ability-name rule used by WP_Abilities_Registry::register(); existence need not be required because registration order can vary.
				if ( ! is_string( $args['meta']['deprecated'][ $key ] ) || '' === $args['meta']['deprecated'][ $key ] ) {
					throw new InvalidArgumentException(
						sprintf(
							/* translators: %s: Deprecation metadata key. */
							__( 'The ability deprecation `%s` value should be a non-empty string.' ),
							$key
						)
					);

src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php:410

  • The schema accepts true as a deprecation-status filter, but registered abilities can only store false or an array. get_items() forwards the resulting boolean to _wp_get_abilities_match_meta(), whose strict comparison means meta[deprecated]=true can never match a deprecated ability. Normalize true to the existing array() sentinel before calling wp_get_abilities() (and cover that REST query), or constrain this schema and provide another REST-representable way to request only deprecated abilities.
					'deprecated'  => array(
						'description'          => __( 'Limit results by deprecation status or details.' ),
						'type'                 => array( 'boolean', 'object' ),

@gziolo
gziolo marked this pull request as ready for review August 19, 2026 07:52
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props gziolo, jason_the_adams, justlevine.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants