| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Abilities API 4 * 5 * Defines WP_Ability class. 6 * 7 * @package WordPress 8 * @subpackage Abilities API 9 * @since 6.9.0 10 */ 11 12 declare( strict_types = 1 ); 13 14 /** 15 * Encapsulates the properties and methods related to a specific ability in the registry. 16 * 17 * @since 6.9.0 18 * 19 * @see WP_Abilities_Registry 20 */ 21 class WP_Ability { 22 23 /** 24 * The default value for the `show_in_rest` meta. 25 * 26 * @since 6.9.0 27 * @var bool 28 */ 29 protected const DEFAULT_SHOW_IN_REST = false; 30 31 /** 32 * The default ability annotations. 33 * They are not guaranteed to provide a faithful description of ability behavior. 34 * 35 * @since 6.9.0 36 * @var array<string, bool|null> 37 */ 38 protected static $default_annotations = array( 39 // If true, the ability does not modify its environment. 40 'readonly' => null, 41 /* 42 * If true, the ability may perform destructive updates to its environment. 43 * If false, the ability performs only additive updates. 44 */ 45 'destructive' => null, 46 /* 47 * If true, calling the ability repeatedly with the same arguments will have no additional effect 48 * on its environment. 49 */ 50 'idempotent' => null, 51 ); 52 53 /** 54 * The name of the ability, with its namespace. 55 * Example: `my-plugin/my-ability`. 56 * 57 * @since 6.9.0 58 * @var string 59 */ 60 protected $name; 61 62 /** 63 * The human-readable ability label. 64 * 65 * @since 6.9.0 66 * @var string 67 */ 68 protected $label; 69 70 /** 71 * The detailed ability description. 72 * 73 * @since 6.9.0 74 * @var string 75 */ 76 protected $description; 77 78 /** 79 * The ability category. 80 * 81 * @since 6.9.0 82 * @var string 83 */ 84 protected $category; 85 86 /** 87 * The optional ability input schema. 88 * 89 * @since 6.9.0 90 * @var array<string, mixed> 91 */ 92 protected $input_schema = array(); 93 94 /** 95 * The optional ability output schema. 96 * 97 * @since 6.9.0 98 * @var array<string, mixed> 99 */ 100 protected $output_schema = array(); 101 102 /** 103 * The ability execute callback. 104 * 105 * @since 6.9.0 106 * @var callable(mixed): (mixed|WP_Error) 107 */ 108 protected $execute_callback; 109 110 /** 111 * The optional ability permission callback. 112 * 113 * @since 6.9.0 114 * @var callable(mixed): (bool|WP_Error) 115 */ 116 protected $permission_callback; 117 118 /** 119 * The optional ability metadata. 120 * 121 * @since 6.9.0 122 * @var array<string, mixed> 123 */ 124 protected $meta; 125 126 /** 127 * Constructor. 128 * 129 * Do not use this constructor directly. Instead, use the `wp_register_ability()` function. 130 * 131 * @access private 132 * 133 * @since 6.9.0 134 * 135 * @see wp_register_ability() 136 * 137 * @param string $name The name of the ability, with its namespace. 138 * @param array<string, mixed> $args { 139 * An associative array of arguments for the ability. 140 * 141 * @type string $label The human-readable label for the ability. 142 * @type string $description A detailed description of what the ability does. 143 * @type string $category The ability category slug this ability belongs to. 144 * @type callable $execute_callback A callback function to execute when the ability is invoked. 145 * Receives optional mixed input and returns mixed result or WP_Error. 146 * @type callable $permission_callback A callback function to check permissions before execution. 147 * Receives optional mixed input and returns bool or WP_Error. 148 * @type array<string, mixed> $input_schema Optional. JSON Schema definition for the ability's input. 149 * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output. 150 * @type array<string, mixed> $meta { 151 * Optional. Additional metadata for the ability. 152 * 153 * @type array<string, bool|null> $annotations { 154 * Optional. Semantic annotations describing the ability's behavioral characteristics. 155 * These annotations are hints for tooling and documentation. 156 * 157 * @type bool|null $readonly Optional. If true, the ability does not modify its environment. 158 * @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment. 159 * If false, the ability performs only additive updates. 160 * @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments 161 * will have no additional effect on its environment. 162 * } 163 * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. Default false. 164 * } 165 * } 166 */ 167 public function __construct( string $name, array $args ) { 168 $this->name = $name; 169 170 $properties = $this->prepare_properties( $args ); 171 172 foreach ( $properties as $property_name => $property_value ) { 173 if ( ! property_exists( $this, $property_name ) ) { 174 _doing_it_wrong( 175 __METHOD__, 176 sprintf( 177 /* translators: %s: Property name. */ 178 __( 'Property "%1$s" is not a valid property for ability "%2$s". Please check the %3$s class for allowed properties.' ), 179 '<code>' . esc_html( $property_name ) . '</code>', 180 '<code>' . esc_html( $this->name ) . '</code>', 181 '<code>' . __CLASS__ . '</code>' 182 ), 183 '6.9.0' 184 ); 185 continue; 186 } 187 188 $this->$property_name = $property_value; 189 } 190 } 191 192 /** 193 * Prepares and validates the properties used to instantiate the ability. 194 * 195 * Errors are thrown as exceptions instead of WP_Errors to allow for simpler handling and overloading. They are then 196 * caught and converted to a WP_Error by WP_Abilities_Registry::register(). 197 * 198 * @since 6.9.0 199 * 200 * @see WP_Abilities_Registry::register() 201 * 202 * @param array<string, mixed> $args { 203 * An associative array of arguments used to instantiate the ability class. 204 * 205 * @type string $label The human-readable label for the ability. 206 * @type string $description A detailed description of what the ability does. 207 * @type string $category The ability category slug this ability belongs to. 208 * @type callable $execute_callback A callback function to execute when the ability is invoked. 209 * Receives optional mixed input and returns mixed result or WP_Error. 210 * @type callable $permission_callback A callback function to check permissions before execution. 211 * Receives optional mixed input and returns bool or WP_Error. 212 * @type array<string, mixed> $input_schema Optional. JSON Schema definition for the ability's input. Required if ability accepts an input. 213 * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output. 214 * @type array<string, mixed> $meta { 215 * Optional. Additional metadata for the ability. 216 * 217 * @type array<string, bool|null> $annotations { 218 * Optional. Semantic annotations describing the ability's behavioral characteristics. 219 * These annotations are hints for tooling and documentation. 220 * 221 * @type bool|null $readonly Optional. If true, the ability does not modify its environment. 222 * @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment. 223 * If false, the ability performs only additive updates. 224 * @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments 225 * will have no additional effect on its environment. 226 * } 227 * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. Default false. 228 * } 229 * } 230 * @return array<string, mixed> { 231 * An associative array of arguments with validated and prepared properties for the ability class. 232 * 233 * @type string $label The human-readable label for the ability. 234 * @type string $description A detailed description of what the ability does. 235 * @type string $category The ability category slug this ability belongs to. 236 * @type callable $execute_callback A callback function to execute when the ability is invoked. 237 * Receives optional mixed input and returns mixed result or WP_Error. 238 * @type callable $permission_callback A callback function to check permissions before execution. 239 * Receives optional mixed input and returns bool or WP_Error. 240 * @type array<string, mixed> $input_schema Optional. JSON Schema definition for the ability's input. 241 * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output. 242 * @type array<string, mixed> $meta { 243 * Additional metadata for the ability. 244 * 245 * @type array<string, bool|null> $annotations { 246 * Semantic annotations describing the ability's behavioral characteristics. 247 * These annotations are hints for tooling and documentation. 248 * 249 * @type bool|null $readonly If true, the ability does not modify its environment. 250 * @type bool|null $destructive If true, the ability may perform destructive updates to its environment. 251 * If false, the ability performs only additive updates. 252 * @type bool|null $idempotent If true, calling the ability repeatedly with the same arguments 253 * will have no additional effect on its environment. 254 * } 255 * @type bool $show_in_rest Whether to expose this ability in the REST API. Default false. 256 * } 257 * } 258 * @throws InvalidArgumentException if an argument is invalid. 259 */ 260 protected function prepare_properties( array $args ): array { 261 // Required args must be present and of the correct type. 262 if ( empty( $args['label'] ) || ! is_string( $args['label'] ) ) { 263 throw new InvalidArgumentException( 264 __( 'The ability properties must contain a `label` string.' ) 265 ); 266 } 267 268 if ( empty( $args['description'] ) || ! is_string( $args['description'] ) ) { 269 throw new InvalidArgumentException( 270 __( 'The ability properties must contain a `description` string.' ) 271 ); 272 } 273 274 if ( empty( $args['category'] ) || ! is_string( $args['category'] ) ) { 275 throw new InvalidArgumentException( 276 __( 'The ability properties must contain a `category` string.' ) 277 ); 278 } 279 280 // If we are not overriding `ability_class` parameter during instantiation, then we need to validate the execute_callback. 281 if ( get_class( $this ) === self::class && ( empty( $args['execute_callback'] ) || ! is_callable( $args['execute_callback'] ) ) ) { 282 throw new InvalidArgumentException( 283 __( 'The ability properties must contain a valid `execute_callback` function.' ) 284 ); 285 } 286 287 // If we are not overriding `ability_class` parameter during instantiation, then we need to validate the permission_callback. 288 if ( get_class( $this ) === self::class && ( empty( $args['permission_callback'] ) || ! is_callable( $args['permission_callback'] ) ) ) { 289 throw new InvalidArgumentException( 290 __( 'The ability properties must provide a valid `permission_callback` function.' ) 291 ); 292 } 293 294 // Optional args only need to be of the correct type if they are present. 295 if ( isset( $args['input_schema'] ) && ! is_array( $args['input_schema'] ) ) { 296 throw new InvalidArgumentException( 297 __( 'The ability properties should provide a valid `input_schema` definition.' ) 298 ); 299 } 300 301 if ( isset( $args['output_schema'] ) && ! is_array( $args['output_schema'] ) ) { 302 throw new InvalidArgumentException( 303 __( 'The ability properties should provide a valid `output_schema` definition.' ) 304 ); 305 } 306 307 if ( isset( $args['meta'] ) && ! is_array( $args['meta'] ) ) { 308 throw new InvalidArgumentException( 309 __( 'The ability properties should provide a valid `meta` array.' ) 310 ); 311 } 312 313 if ( isset( $args['meta']['annotations'] ) && ! is_array( $args['meta']['annotations'] ) ) { 314 throw new InvalidArgumentException( 315 __( 'The ability meta should provide a valid `annotations` array.' ) 316 ); 317 } 318 319 if ( isset( $args['meta']['show_in_rest'] ) && ! is_bool( $args['meta']['show_in_rest'] ) ) { 320 throw new InvalidArgumentException( 321 __( 'The ability meta should provide a valid `show_in_rest` boolean.' ) 322 ); 323 } 324 325 // Set defaults for optional meta. 326 $args['meta'] = wp_parse_args( 327 $args['meta'] ?? array(), 328 array( 329 'annotations' => static::$default_annotations, 330 'show_in_rest' => self::DEFAULT_SHOW_IN_REST, 331 ) 332 ); 333 $args['meta']['annotations'] = wp_parse_args( 334 $args['meta']['annotations'], 335 static::$default_annotations 336 ); 337 338 return $args; 339 } 340 341 /** 342 * Retrieves the name of the ability, with its namespace. 343 * Example: `my-plugin/my-ability`. 344 * 345 * @since 6.9.0 346 * 347 * @return string The ability name, with its namespace. 348 */ 349 public function get_name(): string { 350 return $this->name; 351 } 352 353 /** 354 * Retrieves the human-readable label for the ability. 355 * 356 * @since 6.9.0 357 * 358 * @return string The human-readable ability label. 359 */ 360 public function get_label(): string { 361 return $this->label; 362 } 363 364 /** 365 * Retrieves the detailed description for the ability. 366 * 367 * @since 6.9.0 368 * 369 * @return string The detailed description for the ability. 370 */ 371 public function get_description(): string { 372 return $this->description; 373 } 374 375 /** 376 * Retrieves the ability category for the ability. 377 * 378 * @since 6.9.0 379 * 380 * @return string The ability category for the ability. 381 */ 382 public function get_category(): string { 383 return $this->category; 384 } 385 386 /** 387 * Retrieves the input schema for the ability. 388 * 389 * @since 6.9.0 390 * 391 * @return array<string, mixed> The input schema for the ability. 392 */ 393 public function get_input_schema(): array { 394 return $this->input_schema; 395 } 396 397 /** 398 * Retrieves the output schema for the ability. 399 * 400 * @since 6.9.0 401 * 402 * @return array<string, mixed> The output schema for the ability. 403 */ 404 public function get_output_schema(): array { 405 return $this->output_schema; 406 } 407 408 /** 409 * Retrieves the metadata for the ability. 410 * 411 * @since 6.9.0 412 * 413 * @return array<string, mixed> The metadata for the ability. 414 */ 415 public function get_meta(): array { 416 return $this->meta; 417 } 418 419 /** 420 * Retrieves a specific metadata item for the ability. 421 * 422 * @since 6.9.0 423 * 424 * @param string $key The metadata key to retrieve. 425 * @param mixed $default_value Optional. The default value to return if the metadata item is not found. Default `null`. 426 * @return mixed The value of the metadata item, or the default value if not found. 427 */ 428 public function get_meta_item( string $key, $default_value = null ) { 429 return array_key_exists( $key, $this->meta ) ? $this->meta[ $key ] : $default_value; 430 } 431 432 /** 433 * Normalizes the input for the ability, applying the default value from the input schema when needed. 434 * 435 * When no input is provided and the input schema is defined with a top-level `default` key, this method returns 436 * the value of that key. If the input schema does not define a `default`, or if the input schema is empty, 437 * this method returns null. If input is provided, it is returned as-is. 438 * 439 * The {@see 'wp_ability_normalize_input'} filter fires after the built-in default-value handling, 440 * allowing plugins to transform the result. 441 * 442 * @since 6.9.0 443 * @since 7.1.0 Added the `wp_ability_normalize_input` filter. 444 * 445 * @param mixed $input Optional. The raw input provided for the ability. Default `null`. 446 * @return mixed The normalized input, or a `WP_Error` if a filter returned one. 447 */ 448 public function normalize_input( $input = null ) { 449 if ( null === $input ) { 450 $input_schema = $this->get_input_schema(); 451 if ( array_key_exists( 'default', $input_schema ) ) { 452 $input = $input_schema['default']; 453 } 454 } 455 456 /** 457 * Filters the normalized input for an ability. 458 * 459 * Fires after `normalize_input()` has applied any default value declared in the input schema, 460 * giving plugins a chance to adjust the input before it is consumed downstream. Common uses 461 * include defaulting beyond what JSON Schema can express, prompt enrichment, and injecting 462 * caller metadata. 463 * 464 * Returning a `WP_Error` causes callers that propagate it (such as `execute()`) to halt 465 * before validation, permission checks, and the registered execute callback. 466 * 467 * @since 7.1.0 468 * 469 * @param mixed $input The normalized input data. 470 * @param string $ability_name The name of the ability. 471 * @param WP_Ability $ability The ability instance. 472 */ 473 return apply_filters( 'wp_ability_normalize_input', $input, $this->name, $this ); 474 } 475 476 /** 477 * Validates input data against the input schema. 478 * 479 * @since 6.9.0 480 * 481 * @param mixed $input Optional. The input data to validate. Default `null`. 482 * @return true|WP_Error Returns true if valid or the WP_Error object if validation fails. 483 */ 484 public function validate_input( $input = null ) { 485 $input_schema = $this->get_input_schema(); 486 if ( empty( $input_schema ) ) { 487 if ( null === $input ) { 488 return true; 489 } 490 491 return new WP_Error( 492 'ability_missing_input_schema', 493 sprintf( 494 /* translators: %s ability name. */ 495 __( 'Ability "%s" does not define an input schema required to validate the provided input.' ), 496 $this->name 497 ) 498 ); 499 } 500 501 $valid_input = rest_validate_value_from_schema( $input, $input_schema, 'input' ); 502 if ( is_wp_error( $valid_input ) ) { 503 $is_valid = new WP_Error( 504 'ability_invalid_input', 505 sprintf( 506 /* translators: %1$s ability name, %2$s error message. */ 507 __( 'Ability "%1$s" has invalid input. Reason: %2$s' ), 508 $this->name, 509 $valid_input->get_error_message() 510 ) 511 ); 512 } else { 513 $is_valid = true; 514 } 515 516 /** 517 * Filters the input validation result for an ability. 518 * 519 * Allows developers to add custom validation logic on top of the default 520 * JSON Schema validation. If default validation already failed, the filter 521 * receives the WP_Error object and can add additional error information or 522 * override it. If default validation passed, the filter can add additional 523 * validation checks and return a WP_Error if those checks fail. 524 * 525 * @since 7.1.0 526 * 527 * @param true|WP_Error $is_valid The validation result from default validation. 528 * @param mixed $input The input data being validated. 529 * @param string $ability_name The name of the ability. 530 */ 531 $validity = apply_filters( 'wp_ability_validate_input', $is_valid, $input, $this->name ); 532 if ( false === $validity ) { 533 return new WP_Error( 'ability_invalid_input', __( 'Invalid input.' ) ); 534 } 535 if ( is_wp_error( $validity ) && $validity->has_errors() ) { 536 return $validity; 537 } 538 return true; 539 } 540 541 /** 542 * Invokes a callable, ensuring the input is passed through only if the input schema is defined. 543 * 544 * @since 6.9.0 545 * 546 * @param callable $callback The callable to invoke. 547 * @param mixed $input Optional. The input data for the ability. Default `null`. 548 * @return mixed The result of the callable execution, or a `WP_Error` if the callback threw. 549 */ 550 protected function invoke_callback( callable $callback, $input = null ) { 551 $args = array(); 552 if ( ! empty( $this->get_input_schema() ) ) { 553 $args[] = $input; 554 } 555 556 try { 557 return $callback( ...$args ); 558 } catch ( Throwable $e ) { 559 return new WP_Error( 560 'ability_callback_exception', 561 sprintf( 562 /* translators: 1: Ability name, 2: Exception message. */ 563 __( 'Ability "%1$s" callback threw an exception: %2$s' ), 564 $this->name, 565 esc_html( $e->getMessage() ) 566 ) 567 ); 568 } 569 } 570 571 /** 572 * Checks whether the ability has the necessary permissions. 573 * 574 * Please note that input is not automatically validated against the input schema. 575 * Use `validate_input()` method to validate input before calling this method if needed. 576 * 577 * The {@see 'wp_ability_permission_result'} filter fires after the registered 578 * `permission_callback` returns, allowing plugins to override the result. 579 * 580 * @since 6.9.0 581 * @since 7.1.0 Added the `wp_ability_permission_result` filter. 582 * 583 * @see validate_input() 584 * 585 * @param mixed $input Optional. The valid input data for permission checking. Default `null`. 586 * @return bool|WP_Error Whether the ability has the necessary permission. 587 */ 588 public function check_permissions( $input = null ) { 589 if ( ! is_callable( $this->permission_callback ) ) { 590 return new WP_Error( 591 'ability_invalid_permission_callback', 592 /* translators: %s ability name. */ 593 sprintf( __( 'Ability "%s" does not have a valid permission callback.' ), $this->name ) 594 ); 595 } 596 597 $permission = $this->invoke_callback( $this->permission_callback, $input ); 598 599 /** 600 * Filters the result of an ability's permission check. 601 * 602 * Fires after the registered `permission_callback` returns. Plugins can use this to layer 603 * additional authorization rules on top of the ability's own permission logic — for example, 604 * multi-factor authorization gates or temporary permission elevation for trusted contexts. 605 * 606 * Filters can return `true` to grant, `false` to deny, or a `WP_Error` to deny with a specific 607 * error code and message. The filter receives whatever the `permission_callback` produced. 608 * Any other return value is coerced to `false`. 609 * 610 * @since 7.1.0 611 * 612 * @param bool|WP_Error $permission The permission result returned by `permission_callback`. 613 * @param string $ability_name The name of the ability. 614 * @param mixed $input The input data for the permission check. 615 * @param WP_Ability $ability The ability instance. 616 */ 617 $result = apply_filters( 'wp_ability_permission_result', $permission, $this->name, $input, $this ); 618 if ( ! is_bool( $result ) && ! is_wp_error( $result ) ) { 619 $result = false; 620 } 621 return $result; 622 } 623 624 /** 625 * Executes the ability callback. 626 * 627 * The {@see 'wp_ability_execute_result'} filter fires before this method returns, allowing 628 * plugins to transform the result produced by the registered `execute_callback`. 629 * 630 * @since 6.9.0 631 * @since 7.1.0 Added the `wp_ability_execute_result` filter. 632 * 633 * @param mixed $input Optional. The input data for the ability. Default `null`. 634 * @return mixed|WP_Error The result of the ability execution, or WP_Error on failure. 635 */ 636 protected function do_execute( $input = null ) { 637 if ( ! is_callable( $this->execute_callback ) ) { 638 $result = new WP_Error( 639 'ability_invalid_execute_callback', 640 /* translators: %s ability name. */ 641 sprintf( __( 'Ability "%s" does not have a valid execute callback.' ), $this->name ) 642 ); 643 } else { 644 $result = $this->invoke_callback( $this->execute_callback, $input ); 645 } 646 647 /** 648 * Filters the result returned by an ability's execute callback. 649 * 650 * Fires after the registered execute callback runs. Plugins can use this to transform the 651 * result — response formatting, stripping internal metadata, content safety filtering, 652 * response enrichment, or recovering from a failure by returning a successful value. 653 * 654 * The filter receives whatever the registered callback produced, including a `WP_Error` 655 * if execution failed. Filters may pass the `WP_Error` through unchanged, override it with 656 * a recovered result, or convert a successful result into a `WP_Error`. 657 * 658 * @since 7.1.0 659 * 660 * @param mixed $result The result returned by the registered `execute_callback`, 661 * or a `WP_Error` if execution failed. 662 * @param string $ability_name The name of the ability. 663 * @param mixed $input The normalized input data. 664 * @param WP_Ability $ability The ability instance. 665 */ 666 return apply_filters( 'wp_ability_execute_result', $result, $this->name, $input, $this ); 667 } 668 669 /** 670 * Validates output data against the output schema. 671 * 672 * @since 6.9.0 673 * 674 * @param mixed $output The output data to validate. 675 * @return true|WP_Error Returns true if valid, or a WP_Error object if validation fails. 676 */ 677 protected function validate_output( $output ) { 678 $output_schema = $this->get_output_schema(); 679 if ( empty( $output_schema ) ) { 680 $is_valid = true; 681 } else { 682 $valid_output = rest_validate_value_from_schema( $output, $output_schema, 'output' ); 683 if ( is_wp_error( $valid_output ) ) { 684 $is_valid = new WP_Error( 685 'ability_invalid_output', 686 sprintf( 687 /* translators: %1$s ability name, %2$s error message. */ 688 __( 'Ability "%1$s" has invalid output. Reason: %2$s' ), 689 $this->name, 690 $valid_output->get_error_message() 691 ) 692 ); 693 } else { 694 $is_valid = true; 695 } 696 } 697 698 /** 699 * Filters the output validation result for an ability. 700 * 701 * Allows developers to add custom validation logic on top of the default 702 * JSON Schema validation. If default validation already failed, the filter 703 * receives the WP_Error object and can add additional error information or 704 * override it. If default validation passed, the filter can add additional 705 * validation checks and return a WP_Error if those checks fail. 706 * 707 * @since 7.1.0 708 * 709 * @param true|WP_Error $is_valid The validation result from default validation. 710 * @param mixed $output The output data being validated. 711 * @param string $ability_name The name of the ability. 712 */ 713 $validity = apply_filters( 'wp_ability_validate_output', $is_valid, $output, $this->name ); 714 if ( false === $validity ) { 715 return new WP_Error( 'ability_invalid_output', __( 'Invalid output.' ) ); 716 } 717 if ( is_wp_error( $validity ) && $validity->has_errors() ) { 718 return $validity; 719 } 720 return true; 721 } 722 723 /** 724 * Executes the ability after input validation and running a permission check. 725 * Before returning the return value, it also validates the output. 726 * 727 * @since 6.9.0 728 * @since 7.1.0 Added the `wp_ability_invoked` action. 729 * @since 7.1.0 Added the `wp_pre_execute_ability` filter. 730 * 731 * @param mixed $input Optional. The input data for the ability. Default `null`. 732 * @return mixed|WP_Error The result of the ability execution, or WP_Error on failure. 733 */ 734 public function execute( $input = null ) { 735 /** 736 * Fires when an ability is invoked, before any processing takes place. 737 * 738 * This action fires for every call regardless of outcome (validation failure, 739 * permission denial, short-circuit, or successful execution), and before input 740 * normalization so the raw input is captured as-is. 741 * 742 * @since 7.1.0 743 * 744 * @param string $ability_name The name of the ability. 745 * @param mixed $input The raw input data for the ability, before normalization. 746 * @param WP_Ability $ability The ability instance. 747 */ 748 do_action( 'wp_ability_invoked', $this->name, $input, $this ); 749 750 /** 751 * Filters whether to short-circuit ability execution. 752 * 753 * Returning a value other than the received default bypasses the rest of `execute()` — 754 * input normalization, input validation, permission checks, the registered execute callback, 755 * output validation, and the surrounding actions — and the value is returned to the caller 756 * as-is. Useful for cached responses, rate limiting, maintenance mode, and test mocking. 757 * 758 * To continue with normal execution, return `$pre` unchanged. This preserves any value 759 * (including `null`, `false`, or arbitrary objects) as a valid short-circuit result. 760 * 761 * Because validation is bypassed, callers that short-circuit are responsible for the 762 * integrity of any value they consume from `$input`. 763 * 764 * @since 7.1.0 765 * 766 * @param mixed $pre The pre-computed result. Return this value unchanged to continue execution. 767 * Default `WP_Filter_Sentinel` instance unique to this invocation. 768 * @param string $ability_name The name of the ability. 769 * @param mixed $input The raw input passed to `execute()`. 770 * @param WP_Ability $ability The ability instance. 771 */ 772 $pre_execute_sentinel = new WP_Filter_Sentinel(); 773 $pre = apply_filters( 'wp_pre_execute_ability', $pre_execute_sentinel, $this->name, $input, $this ); 774 if ( $pre !== $pre_execute_sentinel ) { 775 return $pre; 776 } 777 778 $input = $this->normalize_input( $input ); 779 if ( is_wp_error( $input ) ) { 780 return $input; 781 } 782 783 $is_valid = $this->validate_input( $input ); 784 if ( is_wp_error( $is_valid ) ) { 785 return $is_valid; 786 } 787 788 $has_permissions = $this->check_permissions( $input ); 789 if ( true !== $has_permissions ) { 790 if ( is_wp_error( $has_permissions ) ) { 791 // Don't leak the permission check error to someone without the correct perms. 792 _doing_it_wrong( 793 __METHOD__, 794 esc_html( $has_permissions->get_error_message() ), 795 '6.9.0' 796 ); 797 } 798 799 return new WP_Error( 800 'ability_invalid_permissions', 801 /* translators: %s ability name. */ 802 sprintf( __( 'Ability "%s" does not have necessary permission.' ), $this->name ) 803 ); 804 } 805 806 /** 807 * Fires before an ability gets executed, after input validation and permissions check. 808 * 809 * @since 6.9.0 810 * @since 7.1.0 Added the `$ability` parameter. 811 * 812 * @param string $ability_name The name of the ability. 813 * @param mixed $input The input data for the ability. 814 * @param WP_Ability $ability The ability instance. 815 */ 816 do_action( 'wp_before_execute_ability', $this->name, $input, $this ); 817 818 $result = $this->do_execute( $input ); 819 if ( is_wp_error( $result ) ) { 820 return $result; 821 } 822 823 $is_valid = $this->validate_output( $result ); 824 if ( is_wp_error( $is_valid ) ) { 825 return $is_valid; 826 } 827 828 /** 829 * Fires immediately after an ability finished executing. 830 * 831 * @since 6.9.0 832 * @since 7.1.0 Added the `$ability` parameter. 833 * 834 * @param string $ability_name The name of the ability. 835 * @param mixed $input The input data for the ability. 836 * @param mixed $result The result of the ability execution. 837 * @param WP_Ability $ability The ability instance. 838 */ 839 do_action( 'wp_after_execute_ability', $this->name, $input, $result, $this ); 840 841 return $result; 842 } 843 844 /** 845 * Wakeup magic method. 846 * 847 * @since 6.9.0 848 * @throws LogicException If the ability object is unserialized. 849 * This is a security hardening measure to prevent unserialization of the ability. 850 */ 851 public function __wakeup(): void { 852 throw new LogicException( __CLASS__ . ' should never be unserialized.' ); 853 } 854 855 /** 856 * Sleep magic method. 857 * 858 * @since 6.9.0 859 * @throws LogicException If the ability object is serialized. 860 * This is a security hardening measure to prevent serialization of the ability. 861 */ 862 public function __sleep(): array { 863 throw new LogicException( __CLASS__ . ' should never be serialized.' ); 864 } 865 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sun Jun 14 08:20:09 2026 | Cross-referenced by PHPXref |