| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Abilities API: core functions for registering and managing abilities. 4 * 5 * The Abilities API provides a unified, extensible framework for registering 6 * and executing discrete capabilities within WordPress. An "ability" is a 7 * self-contained unit of functionality with defined inputs, outputs, permissions, 8 * and execution logic. 9 * 10 * ## Overview 11 * 12 * The Abilities API enables developers to: 13 * 14 * - Register custom abilities with standardized interfaces. 15 * - Define permission checks and execution callbacks. 16 * - Organize abilities into logical categories. 17 * - Validate inputs and outputs using JSON Schema. 18 * - Expose abilities to clients such as the REST API. 19 * 20 * ## Working with Abilities 21 * 22 * Abilities must be registered on the `wp_abilities_api_init` action hook. 23 * Attempting to register an ability outside of this hook will fail and 24 * trigger a `_doing_it_wrong()` notice. 25 26 * Example: 27 * 28 * function my_plugin_register_abilities(): void { 29 * wp_register_ability( 30 * 'my-plugin/export-users', 31 * array( 32 * 'label' => __( 'Export Users', 'my-plugin' ), 33 * 'description' => __( 'Exports user data to CSV format.', 'my-plugin' ), 34 * 'category' => 'data-export', 35 * 'execute_callback' => 'my_plugin_export_users', 36 * 'permission_callback' => function(): bool { 37 * return current_user_can( 'export' ); 38 * }, 39 * 'input_schema' => array( 40 * 'type' => 'string', 41 * 'enum' => array( 'subscriber', 'contributor', 'author', 'editor', 'administrator' ), 42 * 'description' => __( 'Limits the export to users with this role.', 'my-plugin' ), 43 * 'required' => false, 44 * ), 45 * 'output_schema' => array( 46 * 'type' => 'string', 47 * 'description' => __( 'User data in CSV format.', 'my-plugin' ), 48 * 'required' => true, 49 * ), 50 * 'meta' => array( 51 * 'public' => true, 52 * ), 53 * ) 54 * ); 55 * } 56 * add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' ); 57 * 58 * Once registered, abilities can be checked, retrieved, and managed: 59 * 60 * // Checks if an ability is registered, and prints its label. 61 * if ( wp_has_ability( 'my-plugin/export-users' ) ) { 62 * $ability = wp_get_ability( 'my-plugin/export-users' ); 63 * 64 * echo $ability->get_label(); 65 * } 66 * 67 * // Gets all registered abilities. 68 * $all_abilities = wp_get_abilities(); 69 * 70 * // Unregisters when no longer needed. 71 * wp_unregister_ability( 'my-plugin/export-users' ); 72 * 73 * ## Best Practices 74 * 75 * - Always register abilities on the `wp_abilities_api_init` hook. 76 * - Use namespaced ability names to prevent conflicts. 77 * - Implement robust permission checks in permission callbacks. 78 * - Provide an `input_schema` to ensure data integrity and document expected inputs. 79 * - Define an `output_schema` to describe return values and validate responses. 80 * - Return `WP_Error` objects for failures rather than throwing exceptions. 81 * - Use internationalization functions for all user-facing strings. 82 * 83 * @package WordPress 84 * @subpackage Abilities_API 85 * @since 6.9.0 86 */ 87 88 declare( strict_types = 1 ); 89 90 /** 91 * Registers a new ability using the Abilities API. It requires three steps: 92 * 93 * 1. Hook into the `wp_abilities_api_init` action. 94 * 2. Call `wp_register_ability()` with a namespaced name and configuration. 95 * 3. Provide execute and permission callbacks. 96 * 97 * Example: 98 * 99 * function my_plugin_register_abilities(): void { 100 * wp_register_ability( 101 * 'my-plugin/analyze-text', 102 * array( 103 * 'label' => __( 'Analyze Text', 'my-plugin' ), 104 * 'description' => __( 'Performs sentiment analysis on provided text.', 'my-plugin' ), 105 * 'category' => 'text-processing', 106 * 'input_schema' => array( 107 * 'type' => 'string', 108 * 'description' => __( 'The text to be analyzed.', 'my-plugin' ), 109 * 'minLength' => 10, 110 * 'required' => true, 111 * ), 112 * 'output_schema' => array( 113 * 'type' => 'string', 114 * 'enum' => array( 'positive', 'negative', 'neutral' ), 115 * 'description' => __( 'The sentiment result: positive, negative, or neutral.', 'my-plugin' ), 116 * 'required' => true, 117 * ), 118 * 'execute_callback' => 'my_plugin_analyze_text', 119 * 'permission_callback' => 'my_plugin_can_analyze_text', 120 * 'meta' => array( 121 * 'annotations' => array( 122 * 'readonly' => true, 123 * ), 124 * 'public' => true, 125 * ), 126 * ) 127 * ); 128 * } 129 * add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' ); 130 * 131 * On failure, this function returns `null` and calls `_doing_it_wrong()` with the reason. 132 * By default, the resulting notice is displayed when `WP_DEBUG` is enabled. 133 * 134 * ### Naming Conventions 135 * 136 * Ability names must follow these rules: 137 * 138 * - Include a namespace prefix (e.g., `my-plugin/my-ability`). 139 * - Use only lowercase alphanumeric characters, dashes, and forward slashes. 140 * - Use descriptive, action-oriented names (e.g., `process-payment`, `generate-report`). 141 * 142 * ### Categories 143 * 144 * Abilities can be organized into categories. If no category is provided, the ability is 145 * assigned to the built-in `uncategorized` category. Custom categories must be registered 146 * before the abilities that reference them: 147 * 148 * function my_plugin_register_categories(): void { 149 * wp_register_ability_category( 150 * 'text-processing', 151 * array( 152 * 'label' => __( 'Text Processing', 'my-plugin' ), 153 * 'description' => __( 'Abilities for analyzing and transforming text.', 'my-plugin' ), 154 * ) 155 * ); 156 * } 157 * add_action( 'wp_abilities_api_categories_init', 'my_plugin_register_categories' ); 158 * 159 * ### Input and Output Schemas 160 * 161 * Schemas define the expected structure, type, and constraints for ability inputs 162 * and outputs using JSON Schema syntax. They serve two critical purposes: automatic 163 * validation of data passed to and returned from abilities, and self-documenting 164 * API contracts for developers. 165 * 166 * WordPress implements a validator based on a subset of the JSON Schema Version 4 167 * specification (https://json-schema.org/specification-links.html#draft-4). 168 * For details on supported JSON Schema properties and syntax, see the 169 * related WordPress REST API Schema documentation: 170 * https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics 171 * 172 * Defining schemas is mandatory when there is a value to pass or return. 173 * They ensure data integrity, improve developer experience, and enable 174 * better documentation: 175 * 176 * 'input_schema' => array( 177 * 'type' => 'string', 178 * 'description' => __( 'The text to be analyzed.', 'my-plugin' ), 179 * 'minLength' => 10, 180 * 'required' => true, 181 * ), 182 * 'output_schema' => array( 183 * 'type' => 'string', 184 * 'enum' => array( 'positive', 'negative', 'neutral' ), 185 * 'description' => __( 'The sentiment result: positive, negative, or neutral.', 'my-plugin' ), 186 * 'required' => true, 187 * ), 188 * 189 * ### Callbacks 190 * 191 * #### Execute Callback 192 * 193 * The execute callback performs the ability's core functionality. It receives 194 * optional input data and returns either a result or `WP_Error` on failure. 195 * 196 * function my_plugin_analyze_text( string $input ): string|WP_Error { 197 * $score = My_Plugin::perform_sentiment_analysis( $input ); 198 * if ( is_wp_error( $score ) ) { 199 * return $score; 200 * } 201 * return My_Plugin::interpret_sentiment_score( $score ); 202 * } 203 * 204 * #### Permission Callback 205 * 206 * The permission callback determines whether the ability can be executed. 207 * It receives the same input as the execute callback and must return a 208 * boolean or `WP_Error`. Common use cases include checking user capabilities, 209 * validating API keys, or verifying system state: 210 * 211 * function my_plugin_can_analyze_text( string $input ): bool|WP_Error { 212 * return current_user_can( 'edit_posts' ); 213 * } 214 * 215 * ### Client Exposure 216 * 217 * Set the high-level `public` flag to make an ability available to clients 218 * such as the REST API, MCP, or AI agents: 219 * 220 * 'meta' => array( 221 * 'public' => true, 222 * ), 223 * 224 * The `public` flag seeds the default for each per-channel flag. For the REST 225 * API it seeds `show_in_rest`, which lets the ability be invoked via HTTP 226 * requests. Set a per-channel flag directly to override that default. For 227 * example, keep a public ability out of the REST API: 228 * 229 * 'meta' => array( 230 * 'public' => true, 231 * 'show_in_rest' => false, 232 * ), 233 * 234 * @since 6.9.0 235 * @since 7.1.0 Added the `public` meta argument. 236 * @since 7.2.0 The `category` argument is now optional and defaults to `uncategorized`. 237 * 238 * @see WP_Abilities_Registry::register() 239 * @see wp_register_ability_category() 240 * @see wp_unregister_ability() 241 * 242 * @param string $name The name of the ability. Must be a namespaced string containing 243 * a prefix, e.g., `my-plugin/my-ability`. Can only contain lowercase 244 * alphanumeric characters, dashes, and forward slashes. 245 * @param array<string, mixed> $args { 246 * An associative array of arguments for configuring the ability. 247 * 248 * @type string $label Required. The human-readable label for the ability. 249 * @type string $description Required. A detailed description of what the ability does 250 * and when it should be used. 251 * @type string $category Optional. The ability category slug this ability belongs to. 252 * Defaults to `uncategorized`. Custom categories must be registered 253 * via `wp_register_ability_category()` before registering the ability. 254 * @type callable $execute_callback Required. A callback function to execute when the ability is invoked. 255 * Receives optional mixed input data and must return either a result 256 * value (any type) or a `WP_Error` object on failure. 257 * @type callable $permission_callback Required. A callback function to check permissions before execution. 258 * Receives optional mixed input data (same as `execute_callback`) and 259 * must return `true`/`false` for simple checks, or `WP_Error` for 260 * detailed error responses. 261 * @type array<string, mixed> $input_schema Optional. JSON Schema definition for validating the ability's input. 262 * Must be a valid JSON Schema object defining the structure and 263 * constraints for input data. Used for automatic validation and 264 * API documentation. 265 * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output. 266 * Describes the structure of successful return values from 267 * `execute_callback`. Used for documentation and validation. 268 * @type array<string, mixed> $meta { 269 * Optional. Additional metadata for the ability. 270 * 271 * @type array<string, bool|null> $annotations { 272 * Optional. Semantic annotations describing the ability's behavioral characteristics. 273 * These annotations are hints for tooling and documentation. 274 * 275 * @type bool|null $readonly Optional. If true, the ability does not modify its environment. 276 * @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment. 277 * If false, the ability performs only additive updates. 278 * @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments 279 * will have no additional effect on its environment. 280 * } 281 * @type bool $public Optional. Whether the ability is meant to be available to 282 * clients such as the REST API, MCP, or AI agents. Seeds 283 * the default for per-channel flags like `$show_in_rest`. 284 * Defaults to false. 285 * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. 286 * When true, the ability can be invoked via HTTP requests. 287 * Default is the value of `$public` when set, false otherwise. 288 * } 289 * @type string $ability_class Optional. Fully-qualified custom class name to instantiate 290 * instead of the default `WP_Ability` class. The custom class 291 * must extend `WP_Ability`. Useful for advanced customization 292 * of ability behavior. 293 * } 294 * @return WP_Ability|null The registered ability instance on success, `null` on failure. 295 * 296 * @phpstan-param lowercase-string&non-falsy-string $name 297 */ 298 function wp_register_ability( string $name, array $args ): ?WP_Ability { 299 if ( ! doing_action( 'wp_abilities_api_init' ) ) { 300 _doing_it_wrong( 301 __FUNCTION__, 302 sprintf( 303 /* translators: 1: wp_abilities_api_init, 2: string value of the ability name. */ 304 __( 'Abilities must be registered on the %1$s action. The ability %2$s was not registered.' ), 305 '<code>wp_abilities_api_init</code>', 306 '<code>' . esc_html( $name ) . '</code>' 307 ), 308 '6.9.0' 309 ); 310 return null; 311 } 312 313 $registry = WP_Abilities_Registry::get_instance(); 314 if ( null === $registry ) { 315 return null; 316 } 317 318 return $registry->register( $name, $args ); 319 } 320 321 /** 322 * Unregisters an ability from the Abilities API. 323 * 324 * Removes a previously registered ability from the global registry. Use this to 325 * disable abilities provided by other plugins or when an ability is no longer needed. 326 * 327 * Can be called at any time after the ability has been registered. 328 * 329 * Example: 330 * 331 * if ( wp_has_ability( 'other-plugin/some-ability' ) ) { 332 * wp_unregister_ability( 'other-plugin/some-ability' ); 333 * } 334 * 335 * @since 6.9.0 336 * 337 * @see WP_Abilities_Registry::unregister() 338 * @see wp_register_ability() 339 * 340 * @param string $name The name of the ability to unregister, including namespace prefix 341 * (e.g., 'my-plugin/my-ability'). 342 * @return WP_Ability|null The unregistered ability instance on success, `null` on failure. 343 */ 344 function wp_unregister_ability( string $name ): ?WP_Ability { 345 $registry = WP_Abilities_Registry::get_instance(); 346 if ( null === $registry ) { 347 return null; 348 } 349 350 return $registry->unregister( $name ); 351 } 352 353 /** 354 * Checks if an ability is registered. 355 * 356 * Use this for conditional logic and feature detection before attempting to 357 * retrieve or use an ability. 358 * 359 * Example: 360 * 361 * // Displays different UI based on available abilities. 362 * if ( wp_has_ability( 'premium-plugin/advanced-export' ) ) { 363 * echo 'Export with Premium Features'; 364 * } else { 365 * echo 'Basic Export'; 366 * } 367 * 368 * @since 6.9.0 369 * 370 * @see WP_Abilities_Registry::is_registered() 371 * @see wp_get_ability() 372 * 373 * @param string $name The name of the ability to check, including namespace prefix 374 * (e.g., 'my-plugin/my-ability'). 375 * @return bool `true` if the ability is registered, `false` otherwise. 376 */ 377 function wp_has_ability( string $name ): bool { 378 $registry = WP_Abilities_Registry::get_instance(); 379 if ( null === $registry ) { 380 return false; 381 } 382 383 return $registry->is_registered( $name ); 384 } 385 386 /** 387 * Retrieves a registered ability. 388 * 389 * Returns the ability instance for inspection or use. The instance provides access 390 * to the ability's configuration, metadata, and execution methods. 391 * 392 * Example: 393 * 394 * // Prints information about a registered ability. 395 * $ability = wp_get_ability( 'my-plugin/export-data' ); 396 * if ( $ability ) { 397 * echo $ability->get_label() . ': ' . $ability->get_description(); 398 * } 399 * 400 * @since 6.9.0 401 * 402 * @see WP_Abilities_Registry::get_registered() 403 * @see wp_has_ability() 404 * 405 * @param string $name The name of the ability, including namespace prefix 406 * (e.g., 'my-plugin/my-ability'). 407 * @return WP_Ability|null The registered ability instance, or `null` if not registered. 408 */ 409 function wp_get_ability( string $name ): ?WP_Ability { 410 $registry = WP_Abilities_Registry::get_instance(); 411 if ( null === $registry ) { 412 return null; 413 } 414 415 return $registry->get_registered( $name ); 416 } 417 418 /** 419 * Retrieves registered abilities, optionally filtered by the given arguments. 420 * 421 * When called without arguments, returns all registered abilities. When called 422 * with an $args array, returns only abilities that match every specified condition. 423 * 424 * Filtering pipeline (executed in order): 425 * 426 * 1. Declarative filters (`category`, `namespace`, `meta`) — per-item, AND logic between 427 * arg types. 428 * 2. `item_include_callback` — per-item, caller-scoped. Return true to include, false to exclude. 429 * 3. `wp_get_abilities_item_include` filter — per-item, ecosystem-scoped. Plugins can enforce 430 * universal inclusion rules regardless of what the caller passed. 431 * 4. `result_callback` — on the full matched array, caller-scoped. Sort, slice, or reshape. 432 * 5. `wp_get_abilities_result` filter — on the full array, ecosystem-scoped. 433 * 434 * Steps 1–3 run inside a single loop over the registry — no extra iteration. 435 * 436 * Examples: 437 * 438 * // All abilities (unchanged behaviour). 439 * $abilities = wp_get_abilities(); 440 * 441 * // Filter by category. 442 * $abilities = wp_get_abilities( array( 'category' => 'content' ) ); 443 * 444 * // Filter by namespace. 445 * $abilities = wp_get_abilities( array( 'namespace' => 'woocommerce' ) ); 446 * 447 * // Filter by meta. 448 * $abilities = wp_get_abilities( array( 'meta' => array( 'show_in_rest' => true ) ) ); 449 * 450 * // Combine filters (AND logic between arg types). 451 * $abilities = wp_get_abilities( array( 452 * 'category' => 'content', 453 * 'namespace' => 'core', 454 * 'meta' => array( 'show_in_rest' => true ), 455 * ) ); 456 * 457 * // Caller-scoped per-item callback. 458 * $abilities = wp_get_abilities( array( 459 * 'item_include_callback' => function ( WP_Ability $ability ) { 460 * return current_user_can( 'manage_options' ); 461 * }, 462 * ) ); 463 * 464 * // Caller-scoped result callback (sort + paginate). 465 * $abilities = wp_get_abilities( array( 466 * 'result_callback' => function ( array $abilities ) { 467 * usort( $abilities, fn( $a, $b ) => strcasecmp( $a->get_label(), $b->get_label() ) ); 468 * return array_slice( $abilities, 0, 10 ); 469 * }, 470 * ) ); 471 * 472 * The pipeline always runs, even when called with no arguments. This ensures that the 473 * `wp_get_abilities_item_include` and `wp_get_abilities_result` filters always fire, 474 * giving plugins a reliable place to enforce universal inclusion or shaping rules. 475 * For raw, unfiltered registry data that bypasses the filter pipeline entirely, use 476 * {@see WP_Abilities_Registry::get_all_registered()} directly. 477 * 478 * @since 6.9.0 479 * @since 7.1.0 Added the `$args` parameter for filtering support. 480 * 481 * @see WP_Abilities_Registry::get_all_registered() 482 * 483 * @param array $args { 484 * Optional. Arguments to filter the returned abilities. Default empty array (returns all). 485 * 486 * @type string $category Filter by category slug. Only abilities whose category 487 * exactly matches the given slug are included. 488 * @type string $namespace Filter by ability namespace prefix. Pass the namespace 489 * without a trailing slash, e.g. `'woocommerce'` matches 490 * `'woocommerce/create-order'`. 491 * @type array $meta Filter by meta key/value pairs. All conditions must 492 * match (AND logic). Supports nested arrays for structured 493 * meta, e.g. `array( 'mcp' => array( 'public' => true ) )`. 494 * @type callable $item_include_callback Optional. A callback invoked per ability after declarative 495 * filters. Receives a WP_Ability instance, returns bool. 496 * Return true to include, false to exclude. 497 * @type callable $result_callback Optional. A callback invoked once on the full matched 498 * array. Receives WP_Ability[], must return WP_Ability[]. 499 * Use for sorting, slicing, or reshaping the result. 500 * } 501 * @return WP_Ability[] An array of registered WP_Ability instances matching the given args, 502 * keyed by ability name. Returns an empty array if no abilities are 503 * registered, the registry is unavailable, or no abilities match the 504 * given args. 505 */ 506 function wp_get_abilities( array $args = array() ): array { 507 $registry = WP_Abilities_Registry::get_instance(); 508 if ( null === $registry ) { 509 return array(); 510 } 511 512 $abilities = $registry->get_all_registered(); 513 514 $category = isset( $args['category'] ) && is_string( $args['category'] ) ? $args['category'] : ''; 515 $namespace = isset( $args['namespace'] ) && is_string( $args['namespace'] ) ? rtrim( $args['namespace'], '/' ) . '/' : ''; 516 $meta = isset( $args['meta'] ) && is_array( $args['meta'] ) ? $args['meta'] : array(); 517 $item_include_callback = isset( $args['item_include_callback'] ) && is_callable( $args['item_include_callback'] ) ? $args['item_include_callback'] : null; 518 $result_callback = isset( $args['result_callback'] ) && is_callable( $args['result_callback'] ) ? $args['result_callback'] : null; 519 520 $matched = array(); 521 522 foreach ( $abilities as $name => $ability ) { 523 // Step 1a: Filter by category. 524 if ( '' !== $category && $ability->get_category() !== $category ) { 525 continue; 526 } 527 528 // Step 1b: Filter by namespace prefix. 529 if ( '' !== $namespace && ! str_starts_with( $ability->get_name(), $namespace ) ) { 530 continue; 531 } 532 533 // Step 1c: Filter by meta key/value pairs (AND logic, supports nested arrays). 534 if ( ! empty( $meta ) && ! _wp_get_abilities_match_meta( $ability->get_meta(), $meta ) ) { 535 continue; 536 } 537 538 // Step 2: Caller-scoped per-item callback. 539 $include = true; 540 if ( null !== $item_include_callback ) { 541 $include = (bool) call_user_func( $item_include_callback, $ability ); 542 } 543 544 /** 545 * Filters whether an individual ability should be included in the result set. 546 * 547 * Fires after the declarative filters and the caller-scoped item_include_callback. 548 * Plugins can use this to enforce universal inclusion rules regardless of 549 * what the caller passed in $args. 550 * 551 * @since 7.1.0 552 * 553 * @param bool $include Whether to include the ability. Default true (after declarative filters pass). 554 * @param WP_Ability $ability The ability instance being evaluated. 555 * @param array $args The full $args array passed to wp_get_abilities(). 556 */ 557 $include = (bool) apply_filters( 'wp_get_abilities_item_include', $include, $ability, $args ); 558 559 if ( $include ) { 560 $matched[ $name ] = $ability; 561 } 562 } 563 564 // Step 4: Caller-scoped result callback. 565 if ( null !== $result_callback ) { 566 $matched = (array) call_user_func( $result_callback, $matched ); 567 } 568 569 /** 570 * Filters the full list of matched abilities after all per-item filtering is complete. 571 * 572 * Fires after the caller-scoped result_callback. Plugins can use this to sort, 573 * paginate, or reshape the final result set universally. 574 * 575 * @since 7.1.0 576 * 577 * @param WP_Ability[] $matched The matched abilities after all filtering. 578 * @param array $args The full $args array passed to wp_get_abilities(). 579 */ 580 return (array) apply_filters( 'wp_get_abilities_result', $matched, $args ); 581 } 582 583 /** 584 * Checks whether an ability's meta array matches a set of required key/value conditions. 585 * 586 * All conditions must match (AND logic). Supports nested arrays for structured meta, 587 * e.g. `array( 'mcp' => array( 'public' => true ) )`. 588 * 589 * @since 7.1.0 590 * @access private 591 * 592 * @param array $meta The ability's meta array. 593 * @param array $conditions The required key/value conditions to match against. 594 * @return bool True if all conditions match, false otherwise. 595 */ 596 function _wp_get_abilities_match_meta( array $meta, array $conditions ): bool { 597 foreach ( $conditions as $key => $value ) { 598 if ( ! array_key_exists( $key, $meta ) ) { 599 return false; 600 } 601 602 if ( is_array( $value ) ) { 603 if ( ! is_array( $meta[ $key ] ) || ! _wp_get_abilities_match_meta( $meta[ $key ], $value ) ) { 604 return false; 605 } 606 } elseif ( $meta[ $key ] !== $value ) { 607 return false; 608 } 609 } 610 611 return true; 612 } 613 614 /** 615 * Registers a new ability category. 616 * 617 * Ability categories provide a way to organize and group related abilities for better 618 * discoverability and management. Custom categories must be registered before abilities 619 * that reference them. Abilities that omit a category are assigned to the built-in 620 * `uncategorized` category, which is intended as an escape hatch for simple or transitional 621 * registrations. 622 * 623 * Ability categories must be registered on the `wp_abilities_api_categories_init` action hook. 624 * 625 * Example: 626 * 627 * function my_plugin_register_categories() { 628 * wp_register_ability_category( 629 * 'content-management', 630 * array( 631 * 'label' => __( 'Content Management', 'my-plugin' ), 632 * 'description' => __( 'Abilities for managing and organizing content.', 'my-plugin' ), 633 * ) 634 * ); 635 * } 636 * add_action( 'wp_abilities_api_categories_init', 'my_plugin_register_categories' ); 637 * 638 * On failure, this function returns `null` and calls `_doing_it_wrong()` with the reason. 639 * By default, the resulting notice is displayed when `WP_DEBUG` is enabled. 640 * 641 * @since 6.9.0 642 * 643 * @see WP_Ability_Categories_Registry::register() 644 * @see wp_register_ability() 645 * @see wp_unregister_ability_category() 646 * 647 * @param string $slug The unique slug for the ability category. Must contain only lowercase 648 * alphanumeric characters and dashes (e.g., 'data-export'). 649 * @param array<string, mixed> $args { 650 * An associative array of arguments for the ability category. 651 * 652 * @type string $label Required. The human-readable label for the ability category. 653 * @type string $description Required. A description of what abilities in this category do. 654 * @type array<string, mixed> $meta Optional. Additional metadata for the ability category. 655 * } 656 * @return WP_Ability_Category|null The registered ability category instance on success, `null` on failure. 657 * 658 * @phpstan-param lowercase-string&non-empty-string $slug 659 */ 660 function wp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category { 661 if ( ! doing_action( 'wp_abilities_api_categories_init' ) ) { 662 _doing_it_wrong( 663 __FUNCTION__, 664 sprintf( 665 /* translators: 1: wp_abilities_api_categories_init, 2: ability category slug. */ 666 __( 'Ability categories must be registered on the %1$s action. The ability category %2$s was not registered.' ), 667 '<code>wp_abilities_api_categories_init</code>', 668 '<code>' . esc_html( $slug ) . '</code>' 669 ), 670 '6.9.0' 671 ); 672 return null; 673 } 674 675 $registry = WP_Ability_Categories_Registry::get_instance(); 676 if ( null === $registry ) { 677 return null; 678 } 679 680 return $registry->register( $slug, $args ); 681 } 682 683 /** 684 * Unregisters an ability category. 685 * 686 * Removes a previously registered ability category from the global registry. Use this to 687 * disable ability categories that are no longer needed. 688 * 689 * Can be called at any time after the ability category has been registered. 690 * 691 * Example: 692 * 693 * if ( wp_has_ability_category( 'deprecated-category' ) ) { 694 * wp_unregister_ability_category( 'deprecated-category' ); 695 * } 696 * 697 * @since 6.9.0 698 * 699 * @see WP_Ability_Categories_Registry::unregister() 700 * @see wp_register_ability_category() 701 * 702 * @param string $slug The slug of the ability category to unregister. 703 * @return WP_Ability_Category|null The unregistered ability category instance on success, `null` on failure. 704 */ 705 function wp_unregister_ability_category( string $slug ): ?WP_Ability_Category { 706 $registry = WP_Ability_Categories_Registry::get_instance(); 707 if ( null === $registry ) { 708 return null; 709 } 710 711 return $registry->unregister( $slug ); 712 } 713 714 /** 715 * Checks if an ability category is registered. 716 * 717 * Use this for conditional logic and feature detection before attempting to 718 * retrieve or use an ability category. 719 * 720 * Example: 721 * 722 * // Displays different UI based on available ability categories. 723 * if ( wp_has_ability_category( 'premium-features' ) ) { 724 * echo 'Premium Features Available'; 725 * } else { 726 * echo 'Standard Features'; 727 * } 728 * 729 * @since 6.9.0 730 * 731 * @see WP_Ability_Categories_Registry::is_registered() 732 * @see wp_get_ability_category() 733 * 734 * @param string $slug The slug of the ability category to check. 735 * @return bool `true` if the ability category is registered, `false` otherwise. 736 */ 737 function wp_has_ability_category( string $slug ): bool { 738 $registry = WP_Ability_Categories_Registry::get_instance(); 739 if ( null === $registry ) { 740 return false; 741 } 742 743 return $registry->is_registered( $slug ); 744 } 745 746 /** 747 * Retrieves a registered ability category. 748 * 749 * Returns the ability category instance for inspection or use. The instance provides access 750 * to the ability category's configuration and metadata. 751 * 752 * Example: 753 * 754 * // Prints information about a registered ability category. 755 * $ability_category = wp_get_ability_category( 'content-management' ); 756 * if ( $ability_category ) { 757 * echo $ability_category->get_label() . ': ' . $ability_category->get_description(); 758 * } 759 * 760 * @since 6.9.0 761 * 762 * @see WP_Ability_Categories_Registry::get_registered() 763 * @see wp_has_ability_category() 764 * @see wp_get_ability_categories() 765 * 766 * @param string $slug The slug of the ability category. 767 * @return WP_Ability_Category|null The ability category instance, or `null` if not registered. 768 */ 769 function wp_get_ability_category( string $slug ): ?WP_Ability_Category { 770 $registry = WP_Ability_Categories_Registry::get_instance(); 771 if ( null === $registry ) { 772 return null; 773 } 774 775 return $registry->get_registered( $slug ); 776 } 777 778 /** 779 * Retrieves all registered ability categories. 780 * 781 * Returns an array of all ability category instances currently registered in the system. 782 * Use this for discovery, debugging, or building administrative interfaces. 783 * 784 * Example: 785 * 786 * // Prints information about all available ability categories. 787 * $ability_categories = wp_get_ability_categories(); 788 * foreach ( $ability_categories as $ability_category ) { 789 * echo $ability_category->get_label() . ': ' . $ability_category->get_description() . "\n"; 790 * } 791 * 792 * @since 6.9.0 793 * 794 * @see WP_Ability_Categories_Registry::get_all_registered() 795 * @see wp_get_ability_category() 796 * 797 * @return WP_Ability_Category[] An array of registered ability category instances. Returns an empty array 798 * if no ability categories are registered or if the registry is unavailable. 799 */ 800 function wp_get_ability_categories(): array { 801 $registry = WP_Ability_Categories_Registry::get_instance(); 802 if ( null === $registry ) { 803 return array(); 804 } 805 806 return $registry->get_all_registered(); 807 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Oct 15 08:20:32 2026 | Cross-referenced by PHPXref |