Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Abilities API: Add a core/read-settings ability#12141

Open
jorgefilipecosta wants to merge 16 commits into
WordPress:trunkfrom
jorgefilipecosta:add/core-settings-ability
Open

Abilities API: Add a core/read-settings ability#12141
jorgefilipecosta wants to merge 16 commits into
WordPress:trunkfrom
jorgefilipecosta:add/core-settings-ability

Conversation

@jorgefilipecosta

@jorgefilipecosta jorgefilipecosta commented Jun 9, 2026

Copy link
Copy Markdown
Member

Part of: WordPress/ai#40
Ticket: https://core.trac.wordpress.org/ticket/64605

Summary

Adds a read-only core/read-settings ability to the Abilities API. It returns WordPress settings — those flagged with a new show_in_abilities registration arg — as a flat name => value map, with per-setting metadata (type, title, description, default) carried in the output schema. Callers can optionally filter by settings group, by setting names (fields), or both (narrowing to their intersection). Requires manage_options.

This is a flat-output alternative to #10747. The logic lives in a new internal WP_Settings_Abilities class (src/wp-includes/abilities/class-wp-settings-abilities.php), structured so a future core/manage-settings write ability can reuse its helpers (get_exposed_settings(), value_schema(), cast_value()).

AI PRs: WordPress/ai#691, WordPress/ai#806

Test plan

In wp-admin, open the browser console and call the ability's run endpoint with wp.apiFetch:

// All exposed settings (flat name => value map).
await wp.apiFetch( {
	path: wp.url.addQueryArgs( '/wp-abilities/v1/abilities/core/read-settings/run' ),
} );

// Filter by group.
await wp.apiFetch( {
	path: wp.url.addQueryArgs( '/wp-abilities/v1/abilities/core/read-settings/run', {
		input: { group: 'reading' },
	} ),
} );

// Filter by specific settings (by name).
await wp.apiFetch( {
	path: wp.url.addQueryArgs( '/wp-abilities/v1/abilities/core/read-settings/run', {
		input: { fields: [ 'blogname', 'posts_per_page' ] },
	} ),
} );
  • Returns a flat name => value map with typed values (e.g. posts_per_page is an int, use_smilies a bool)
  • group and fields filters narrow the result; supplying both returns their intersection
  • A non-admin user (no manage_options) is denied

PHPUnit coverage added in tests/phpunit/tests/abilities-api/wpRegisterCoreReadSettingsAbility.php (including a test that a setting registered with show_in_abilities is exposed by the ability).

@github-actions

github-actions Bot commented Jun 9, 2026

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 jorgefilipecosta, gziolo, justlevine.

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

@github-actions

github-actions Bot commented Jun 9, 2026

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

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

@jorgefilipecosta jorgefilipecosta changed the title Abilities API: Add a core/settings ability [in progress] Abilities API: Add a core/settings ability Jun 9, 2026
@justlevine

Copy link
Copy Markdown

@jorgefilipecosta can you link this to the trac ticket please? I don't have edit perms in this repo.
https://core.trac.wordpress.org/ticket/64605

@jorgefilipecosta jorgefilipecosta force-pushed the add/core-settings-ability branch from 073df67 to c948c7a Compare June 12, 2026 15:01
@gziolo gziolo self-requested a review June 15, 2026 12:31
@jorgefilipecosta

Copy link
Copy Markdown
Member Author

@jorgefilipecosta can you link this to the trac ticket please? I don't have edit perms in this repo. core.trac.wordpress.org/ticket/64605

Nice catch the ticket mention was added.

@jorgefilipecosta jorgefilipecosta force-pushed the add/core-settings-ability branch from c948c7a to a28c976 Compare June 15, 2026 19:32
@jorgefilipecosta jorgefilipecosta changed the title [in progress] Abilities API: Add a core/settings ability Abilities API: Add a core/settings ability Jun 16, 2026
@gziolo

gziolo commented Jun 17, 2026

Copy link
Copy Markdown
Member

Let's run development, review, and testing through WordPress/ai#691, then sync all agreed refinements here.

@jorgefilipecosta jorgefilipecosta force-pushed the add/core-settings-ability branch 3 times, most recently from 24bee86 to 6419a95 Compare June 23, 2026 15:23
@jorgefilipecosta jorgefilipecosta changed the title Abilities API: Add a core/settings ability Abilities API: Add a core/read-settings ability Jul 1, 2026

@gziolo gziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I left my feedback.

Two questions regarding core/get-site-info:

  • It covers some similar settings but is scoped to the site. Should it also respect the show_in_abilities check wherever applicable?
  • Should it get the default value in the schema aligned to (object) array() as here?

Comment thread src/wp-includes/abilities/class-wp-settings-abilities.php Outdated
Comment thread src/wp-includes/abilities/class-wp-settings-abilities.php
*/
private function register_get_settings(): void {
// Compute once; execute_get_settings() reuses this exact structure.
$this->exposed_settings = $this->get_exposed_settings();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I noticed that Core settings get registered on the rest_api_init hook:

add_action( 'rest_api_init', 'register_initial_settings', 10 );

It's worth double-checking whether there won't be a race condition with wp_abilities_api_init.

The long-term question is whether get_exposed_settings() could be computed lazily inside the execute/schema callbacks instead of being cached at registration? That would remove the ordering coupling and match the sibling's live‑compute model.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch on the ordering. wp_abilities_api_init fires lazily and isn't ordered relative to rest_api_init, so register() now calls register_initial_settings() before computing the snapshot whenever rest_api_init hasn't fired (or is mid-fire before priority 10). Added the guard in ae862f0 and documented the timing in 59e1819; re-registering later on rest_api_init is harmless.

On computing it lazily: I kept the snapshot cached at registration on purpose. The input schema, output schema, and execute callback all have to derive from the exact same set of exposed settings, so computing it once avoids walking get_registered_settings() three times per request and any risk of the schema and the output drifting apart. With the ordering handled explicitly there's no coupling left to remove, so I dropped the unreachable recompute branch in 13f4d31.

Comment thread tests/phpunit/tests/abilities-api/wpRegisterCoreSettingsAbility.php Outdated
jorgefilipecosta and others added 10 commits July 2, 2026 18:27
Add a read-only core/settings ability that returns WordPress settings as a flat
name => value map. Only settings flagged with the new show_in_abilities
registration arg are exposed; callers can filter by settings group or by name
(mutually exclusive). Requires the manage_options capability.

The logic lives in a new internal WP_Settings_Abilities class, structured so a
future core/manage-settings write ability can reuse its helpers.
'settings' reads more naturally than 'slugs' for filtering an abilities-exposed
settings map by name.
… tests

Register a setting with show_in_abilities and assert it is exposed in the
ability's input enum, output schema, and execute output.
- Simplify the input schema: replace the group-XOR-name `oneOf` with optional,
  combinable `group` and `fields` filters (rename `slugs` -> `fields`, matching
  core/get-site-info). Default to an empty object so the type:object schema default
  serializes as {}.
- Memoize the exposed settings so the input/output schema and execute() derive from a
  single walk of get_registered_settings().
- Cast object-typed values to objects so they serialize as {} (not []) and satisfy the
  output schema validated by execute().
- Harden value handling against loosely-typed registration data.
- Tests: assert keys order-insensitively and cover combined group+fields filtering.
…cblock

The $groups and $field_names params are sequential string lists
(array_keys() and an appended array), so list<string> is the precise
type rather than string[].
…ettings()

The exposed-settings cache is always populated in register_get_settings()
before the ability is registered, so the recompute fallback was dead code.
Read the cache directly and keep a defensive null guard so the unexpected
case is explicit rather than silently recomputing.
…tract

The core/settings ability snapshots the exposed settings when it registers
on wp_abilities_api_init, so a setting must be registered before that hook
fires to be exposed. Note this on the show_in_abilities register_setting()
argument so integrators know their register_setting() has to run earlier.
Make WP_Settings_Abilities final and tighten member visibility so the
intended surface is enforced rather than only documented via @access
private. Only the externally-invoked entry points remain public
(register(), execute_get_settings(), has_permission()); register_get_settings()
and the shared helpers become private, and CATEGORY becomes a private const.
Method bodies and the static cache are unchanged.
Move register() and the ability callbacks (execute_get_settings(),
has_permission()) from public static to public methods, and the internal
helpers to private methods; the exposed-settings cache becomes an instance
property. wp_register_core_abilities() now registers the ability via
( new WP_Settings_Abilities() )->register(), reducing the static surface
of this @access private class.
@jorgefilipecosta jorgefilipecosta force-pushed the add/core-settings-ability branch from 8469c64 to 6f4b639 Compare July 2, 2026 17:27
Syncs the core PR with the WordPress/ai plugin, where the ability was
renamed in WordPress/ai#806. Renames the ability,
its label (Get Settings -> Read Settings), the docblock references in
option.php and abilities.php, and the test file, and adds the plugin's
new assertion on the registered ability name.
…ass.

Per review, spell out in the class docblock why WP_Settings_Abilities
departs from the self-contained closures in wp_register_core_abilities():
the exposed-settings snapshot is shared between the schemas and the
execute callback, and the helpers are meant to back a future
core/manage-settings write ability. Also documents the snapshot timing
contract with register_initial_settings() on rest_api_init.
Aligns the core/get-site-info, core/get-user-info, and
core/get-environment-info input schema defaults with core/read-settings:
(object) array() instead of array(), so the serialized default is {},
consistent with type:object.
Per review, all the tests added by this PR should reference the ticket
that introduces the ability.
…apshots them.

The wp_abilities_api_init hook fires lazily on first use of the abilities
registry, which is not ordered relative to rest_api_init (where core
registers its initial settings) and can happen without it entirely, e.g.
on cron or WP-CLI. When the registry initialized first, core/read-settings
captured an empty settings snapshot for the rest of the request.

Ensure register_initial_settings() has run before the snapshot is
computed, and register the ability under the previously broken ordering
in the tests so they cover the regression.
@jorgefilipecosta

Copy link
Copy Markdown
Member Author

I left my feedback.

Two questions regarding core/get-site-info:

  • It covers some similar settings but is scoped to the site. Should it also respect the show_in_abilities check wherever applicable?
  • Should it get the default value in the schema aligned to (object) array() as here?

Hi @gziolo I think the answer is yes to both, but I would prefer to do that in a separate PR to avoid this one being too huge.

@jorgefilipecosta

Copy link
Copy Markdown
Member Author

All the reviews comments were applied this is ready for another look.

…t their schema.

Run each setting value through rest_validate_value_from_schema() and
rest_sanitize_value_from_schema() instead of a type-only cast, mirroring
WP_REST_Settings_Controller::prepare_value(). A value that does not conform to
its setting's schema (for example an enum or format-constrained setting whose
stored option is absent or has drifted) is now dropped from the response rather
than left to fail output validation for the entire core/read-settings call, so
one bad value can no longer make every setting unreadable. Removes the bespoke
cast_value() helper.
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.

3 participants