| [ 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 through 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 * 'show_in_rest' => 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 * 'show_in_rest' => true, 125 * ), 126 * ) 127 * ); 128 * } 129 * add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' ); 130 * 131 * ### Naming Conventions 132 * 133 * Ability names must follow these rules: 134 * 135 * - Include a namespace prefix (e.g., `my-plugin/my-ability`). 136 * - Use only lowercase alphanumeric characters, dashes, and forward slashes. 137 * - Use descriptive, action-oriented names (e.g., `process-payment`, `generate-report`). 138 * 139 * ### Categories 140 * 141 * Abilities must be organized into categories. Ability categories provide better 142 * discoverability and must be registered before the abilities that reference them: 143 * 144 * function my_plugin_register_categories(): void { 145 * wp_register_ability_category( 146 * 'text-processing', 147 * array( 148 * 'label' => __( 'Text Processing', 'my-plugin' ), 149 * 'description' => __( 'Abilities for analyzing and transforming text.', 'my-plugin' ), 150 * ) 151 * ); 152 * } 153 * add_action( 'wp_abilities_api_categories_init', 'my_plugin_register_categories' ); 154 * 155 * ### Input and Output Schemas 156 * 157 * Schemas define the expected structure, type, and constraints for ability inputs 158 * and outputs using JSON Schema syntax. They serve two critical purposes: automatic 159 * validation of data passed to and returned from abilities, and self-documenting 160 * API contracts for developers. 161 * 162 * WordPress implements a validator based on a subset of the JSON Schema Version 4 163 * specification (https://json-schema.org/specification-links.html#draft-4). 164 * For details on supported JSON Schema properties and syntax, see the 165 * related WordPress REST API Schema documentation: 166 * https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics 167 * 168 * Defining schemas is mandatory when there is a value to pass or return. 169 * They ensure data integrity, improve developer experience, and enable 170 * better documentation: 171 * 172 * 'input_schema' => array( 173 * 'type' => 'string', 174 * 'description' => __( 'The text to be analyzed.', 'my-plugin' ), 175 * 'minLength' => 10, 176 * 'required' => true, 177 * ), 178 * 'output_schema' => array( 179 * 'type' => 'string', 180 * 'enum' => array( 'positive', 'negative', 'neutral' ), 181 * 'description' => __( 'The sentiment result: positive, negative, or neutral.', 'my-plugin' ), 182 * 'required' => true, 183 * ), 184 * 185 * ### Callbacks 186 * 187 * #### Execute Callback 188 * 189 * The execute callback performs the ability's core functionality. It receives 190 * optional input data and returns either a result or `WP_Error` on failure. 191 * 192 * function my_plugin_analyze_text( string $input ): string|WP_Error { 193 * $score = My_Plugin::perform_sentiment_analysis( $input ); 194 * if ( is_wp_error( $score ) ) { 195 * return $score; 196 * } 197 * return My_Plugin::interpret_sentiment_score( $score ); 198 * } 199 * 200 * #### Permission Callback 201 * 202 * The permission callback determines whether the ability can be executed. 203 * It receives the same input as the execute callback and must return a 204 * boolean or `WP_Error`. Common use cases include checking user capabilities, 205 * validating API keys, or verifying system state: 206 * 207 * function my_plugin_can_analyze_text( string $input ): bool|WP_Error { 208 * return current_user_can( 'edit_posts' ); 209 * } 210 * 211 * ### REST API Integration 212 * 213 * Abilities can be exposed through the REST API by setting `show_in_rest` 214 * to `true` in the meta configuration: 215 * 216 * 'meta' => array( 217 * 'show_in_rest' => true, 218 * ), 219 * 220 * This allows abilities to be invoked via HTTP requests to the WordPress REST API. 221 * 222 * @since 6.9.0 223 * 224 * @see WP_Abilities_Registry::register() 225 * @see wp_register_ability_category() 226 * @see wp_unregister_ability() 227 * 228 * @param string $name The name of the ability. Must be a namespaced string containing 229 * a prefix, e.g., `my-plugin/my-ability`. Can only contain lowercase 230 * alphanumeric characters, dashes, and forward slashes. 231 * @param array<string, mixed> $args { 232 * An associative array of arguments for configuring the ability. 233 * 234 * @type string $label Required. The human-readable label for the ability. 235 * @type string $description Required. A detailed description of what the ability does 236 * and when it should be used. 237 * @type string $category Required. The ability category slug this ability belongs to. 238 * The ability category must be registered via `wp_register_ability_category()` 239 * before registering the ability. 240 * @type callable $execute_callback Required. A callback function to execute when the ability is invoked. 241 * Receives optional mixed input data and must return either a result 242 * value (any type) or a `WP_Error` object on failure. 243 * @type callable $permission_callback Required. A callback function to check permissions before execution. 244 * Receives optional mixed input data (same as `execute_callback`) and 245 * must return `true`/`false` for simple checks, or `WP_Error` for 246 * detailed error responses. 247 * @type array<string, mixed> $input_schema Optional. JSON Schema definition for validating the ability's input. 248 * Must be a valid JSON Schema object defining the structure and 249 * constraints for input data. Used for automatic validation and 250 * API documentation. 251 * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output. 252 * Describes the structure of successful return values from 253 * `execute_callback`. Used for documentation and validation. 254 * @type array<string, mixed> $meta { 255 * Optional. Additional metadata for the ability. 256 * 257 * @type array<string, bool|null> $annotations { 258 * Optional. Semantic annotations describing the ability's behavioral characteristics. 259 * These annotations are hints for tooling and documentation. 260 * 261 * @type bool|null $readonly Optional. If true, the ability does not modify its environment. 262 * @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment. 263 * If false, the ability performs only additive updates. 264 * @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments 265 * will have no additional effect on its environment. 266 * } 267 * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. 268 * When true, the ability can be invoked via HTTP requests. 269 * Default false. 270 * } 271 * @type string $ability_class Optional. Fully-qualified custom class name to instantiate 272 * instead of the default `WP_Ability` class. The custom class 273 * must extend `WP_Ability`. Useful for advanced customization 274 * of ability behavior. 275 * } 276 * @return WP_Ability|null The registered ability instance on success, `null` on failure. 277 */ 278 function wp_register_ability( string $name, array $args ): ?WP_Ability { 279 if ( ! doing_action( 'wp_abilities_api_init' ) ) { 280 _doing_it_wrong( 281 __FUNCTION__, 282 sprintf( 283 /* translators: 1: wp_abilities_api_init, 2: string value of the ability name. */ 284 __( 'Abilities must be registered on the %1$s action. The ability %2$s was not registered.' ), 285 '<code>wp_abilities_api_init</code>', 286 '<code>' . esc_html( $name ) . '</code>' 287 ), 288 '6.9.0' 289 ); 290 return null; 291 } 292 293 $registry = WP_Abilities_Registry::get_instance(); 294 if ( null === $registry ) { 295 return null; 296 } 297 298 return $registry->register( $name, $args ); 299 } 300 301 /** 302 * Unregisters an ability from the Abilities API. 303 * 304 * Removes a previously registered ability from the global registry. Use this to 305 * disable abilities provided by other plugins or when an ability is no longer needed. 306 * 307 * Can be called at any time after the ability has been registered. 308 * 309 * Example: 310 * 311 * if ( wp_has_ability( 'other-plugin/some-ability' ) ) { 312 * wp_unregister_ability( 'other-plugin/some-ability' ); 313 * } 314 * 315 * @since 6.9.0 316 * 317 * @see WP_Abilities_Registry::unregister() 318 * @see wp_register_ability() 319 * 320 * @param string $name The name of the ability to unregister, including namespace prefix 321 * (e.g., 'my-plugin/my-ability'). 322 * @return WP_Ability|null The unregistered ability instance on success, `null` on failure. 323 */ 324 function wp_unregister_ability( string $name ): ?WP_Ability { 325 $registry = WP_Abilities_Registry::get_instance(); 326 if ( null === $registry ) { 327 return null; 328 } 329 330 return $registry->unregister( $name ); 331 } 332 333 /** 334 * Checks if an ability is registered. 335 * 336 * Use this for conditional logic and feature detection before attempting to 337 * retrieve or use an ability. 338 * 339 * Example: 340 * 341 * // Displays different UI based on available abilities. 342 * if ( wp_has_ability( 'premium-plugin/advanced-export' ) ) { 343 * echo 'Export with Premium Features'; 344 * } else { 345 * echo 'Basic Export'; 346 * } 347 * 348 * @since 6.9.0 349 * 350 * @see WP_Abilities_Registry::is_registered() 351 * @see wp_get_ability() 352 * 353 * @param string $name The name of the ability to check, including namespace prefix 354 * (e.g., 'my-plugin/my-ability'). 355 * @return bool `true` if the ability is registered, `false` otherwise. 356 */ 357 function wp_has_ability( string $name ): bool { 358 $registry = WP_Abilities_Registry::get_instance(); 359 if ( null === $registry ) { 360 return false; 361 } 362 363 return $registry->is_registered( $name ); 364 } 365 366 /** 367 * Retrieves a registered ability. 368 * 369 * Returns the ability instance for inspection or use. The instance provides access 370 * to the ability's configuration, metadata, and execution methods. 371 * 372 * Example: 373 * 374 * // Prints information about a registered ability. 375 * $ability = wp_get_ability( 'my-plugin/export-data' ); 376 * if ( $ability ) { 377 * echo $ability->get_label() . ': ' . $ability->get_description(); 378 * } 379 * 380 * @since 6.9.0 381 * 382 * @see WP_Abilities_Registry::get_registered() 383 * @see wp_has_ability() 384 * 385 * @param string $name The name of the ability, including namespace prefix 386 * (e.g., 'my-plugin/my-ability'). 387 * @return WP_Ability|null The registered ability instance, or `null` if not registered. 388 */ 389 function wp_get_ability( string $name ): ?WP_Ability { 390 $registry = WP_Abilities_Registry::get_instance(); 391 if ( null === $registry ) { 392 return null; 393 } 394 395 return $registry->get_registered( $name ); 396 } 397 398 /** 399 * Retrieves all registered abilities. 400 * 401 * Returns an array of all ability instances currently registered in the system. 402 * Use this for discovery, debugging, or building administrative interfaces. 403 * 404 * Example: 405 * 406 * // Prints information about all available abilities. 407 * $abilities = wp_get_abilities(); 408 * foreach ( $abilities as $ability ) { 409 * echo $ability->get_label() . ': ' . $ability->get_description() . "\n"; 410 * } 411 * 412 * @since 6.9.0 413 * 414 * @see WP_Abilities_Registry::get_all_registered() 415 * 416 * @return WP_Ability[] An array of registered WP_Ability instances. Returns an empty 417 * array if no abilities are registered or if the registry is unavailable. 418 */ 419 function wp_get_abilities(): array { 420 $registry = WP_Abilities_Registry::get_instance(); 421 if ( null === $registry ) { 422 return array(); 423 } 424 425 return $registry->get_all_registered(); 426 } 427 428 /** 429 * Registers a new ability category. 430 * 431 * Ability categories provide a way to organize and group related abilities for better 432 * discoverability and management. Ability categories must be registered before abilities 433 * that reference them. 434 * 435 * Ability categories must be registered on the `wp_abilities_api_categories_init` action hook. 436 * 437 * Example: 438 * 439 * function my_plugin_register_categories() { 440 * wp_register_ability_category( 441 * 'content-management', 442 * array( 443 * 'label' => __( 'Content Management', 'my-plugin' ), 444 * 'description' => __( 'Abilities for managing and organizing content.', 'my-plugin' ), 445 * ) 446 * ); 447 * } 448 * add_action( 'wp_abilities_api_categories_init', 'my_plugin_register_categories' ); 449 * 450 * @since 6.9.0 451 * 452 * @see WP_Ability_Categories_Registry::register() 453 * @see wp_register_ability() 454 * @see wp_unregister_ability_category() 455 * 456 * @param string $slug The unique slug for the ability category. Must contain only lowercase 457 * alphanumeric characters and dashes (e.g., 'data-export'). 458 * @param array<string, mixed> $args { 459 * An associative array of arguments for the ability category. 460 * 461 * @type string $label Required. The human-readable label for the ability category. 462 * @type string $description Required. A description of what abilities in this category do. 463 * @type array<string, mixed> $meta Optional. Additional metadata for the ability category. 464 * } 465 * @return WP_Ability_Category|null The registered ability category instance on success, `null` on failure. 466 */ 467 function wp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category { 468 if ( ! doing_action( 'wp_abilities_api_categories_init' ) ) { 469 _doing_it_wrong( 470 __FUNCTION__, 471 sprintf( 472 /* translators: 1: wp_abilities_api_categories_init, 2: ability category slug. */ 473 __( 'Ability categories must be registered on the %1$s action. The ability category %2$s was not registered.' ), 474 '<code>wp_abilities_api_categories_init</code>', 475 '<code>' . esc_html( $slug ) . '</code>' 476 ), 477 '6.9.0' 478 ); 479 return null; 480 } 481 482 $registry = WP_Ability_Categories_Registry::get_instance(); 483 if ( null === $registry ) { 484 return null; 485 } 486 487 return $registry->register( $slug, $args ); 488 } 489 490 /** 491 * Unregisters an ability category. 492 * 493 * Removes a previously registered ability category from the global registry. Use this to 494 * disable ability categories that are no longer needed. 495 * 496 * Can be called at any time after the ability category has been registered. 497 * 498 * Example: 499 * 500 * if ( wp_has_ability_category( 'deprecated-category' ) ) { 501 * wp_unregister_ability_category( 'deprecated-category' ); 502 * } 503 * 504 * @since 6.9.0 505 * 506 * @see WP_Ability_Categories_Registry::unregister() 507 * @see wp_register_ability_category() 508 * 509 * @param string $slug The slug of the ability category to unregister. 510 * @return WP_Ability_Category|null The unregistered ability category instance on success, `null` on failure. 511 */ 512 function wp_unregister_ability_category( string $slug ): ?WP_Ability_Category { 513 $registry = WP_Ability_Categories_Registry::get_instance(); 514 if ( null === $registry ) { 515 return null; 516 } 517 518 return $registry->unregister( $slug ); 519 } 520 521 /** 522 * Checks if an ability category is registered. 523 * 524 * Use this for conditional logic and feature detection before attempting to 525 * retrieve or use an ability category. 526 * 527 * Example: 528 * 529 * // Displays different UI based on available ability categories. 530 * if ( wp_has_ability_category( 'premium-features' ) ) { 531 * echo 'Premium Features Available'; 532 * } else { 533 * echo 'Standard Features'; 534 * } 535 * 536 * @since 6.9.0 537 * 538 * @see WP_Ability_Categories_Registry::is_registered() 539 * @see wp_get_ability_category() 540 * 541 * @param string $slug The slug of the ability category to check. 542 * @return bool `true` if the ability category is registered, `false` otherwise. 543 */ 544 function wp_has_ability_category( string $slug ): bool { 545 $registry = WP_Ability_Categories_Registry::get_instance(); 546 if ( null === $registry ) { 547 return false; 548 } 549 550 return $registry->is_registered( $slug ); 551 } 552 553 /** 554 * Retrieves a registered ability category. 555 * 556 * Returns the ability category instance for inspection or use. The instance provides access 557 * to the ability category's configuration and metadata. 558 * 559 * Example: 560 * 561 * // Prints information about a registered ability category. 562 * $ability_category = wp_get_ability_category( 'content-management' ); 563 * if ( $ability_category ) { 564 * echo $ability_category->get_label() . ': ' . $ability_category->get_description(); 565 * } 566 * 567 * @since 6.9.0 568 * 569 * @see WP_Ability_Categories_Registry::get_registered() 570 * @see wp_has_ability_category() 571 * @see wp_get_ability_categories() 572 * 573 * @param string $slug The slug of the ability category. 574 * @return WP_Ability_Category|null The ability category instance, or `null` if not registered. 575 */ 576 function wp_get_ability_category( string $slug ): ?WP_Ability_Category { 577 $registry = WP_Ability_Categories_Registry::get_instance(); 578 if ( null === $registry ) { 579 return null; 580 } 581 582 return $registry->get_registered( $slug ); 583 } 584 585 /** 586 * Retrieves all registered ability categories. 587 * 588 * Returns an array of all ability category instances currently registered in the system. 589 * Use this for discovery, debugging, or building administrative interfaces. 590 * 591 * Example: 592 * 593 * // Prints information about all available ability categories. 594 * $ability_categories = wp_get_ability_categories(); 595 * foreach ( $ability_categories as $ability_category ) { 596 * echo $ability_category->get_label() . ': ' . $ability_category->get_description() . "\n"; 597 * } 598 * 599 * @since 6.9.0 600 * 601 * @see WP_Ability_Categories_Registry::get_all_registered() 602 * @see wp_get_ability_category() 603 * 604 * @return WP_Ability_Category[] An array of registered ability category instances. Returns an empty array 605 * if no ability categories are registered or if the registry is unavailable. 606 */ 607 function wp_get_ability_categories(): array { 608 $registry = WP_Ability_Categories_Registry::get_instance(); 609 if ( null === $registry ) { 610 return array(); 611 } 612 613 return $registry->get_all_registered(); 614 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue May 5 08:20:14 2026 | Cross-referenced by PHPXref |