[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/abilities-api/ -> class-wp-abilities-registry.php (source)

   1  <?php
   2  /**
   3   * Abilities API
   4   *
   5   * Defines WP_Abilities_Registry class.
   6   *
   7   * @package WordPress
   8   * @subpackage Abilities API
   9   * @since 6.9.0
  10   */
  11  
  12  declare( strict_types = 1 );
  13  
  14  /**
  15   * Manages the registration and lookup of abilities.
  16   *
  17   * @since 6.9.0
  18   * @access private
  19   */
  20  final class WP_Abilities_Registry {
  21      /**
  22       * The singleton instance of the registry.
  23       *
  24       * @since 6.9.0
  25       * @var self|null
  26       */
  27      private static $instance = null;
  28  
  29      /**
  30       * Holds the registered abilities.
  31       *
  32       * @since 6.9.0
  33       * @var WP_Ability[]
  34       */
  35      private $registered_abilities = array();
  36  
  37      /**
  38       * Registers a new ability.
  39       *
  40       * Do not use this method directly. Instead, use the `wp_register_ability()` function.
  41       *
  42       * @since 6.9.0
  43       *
  44       * @see wp_register_ability()
  45       *
  46       * @param string               $name The name of the ability. The name must be a string containing a namespace
  47       *                                   prefix, i.e. `my-plugin/my-ability`. It can only contain lowercase
  48       *                                   alphanumeric characters, dashes and the forward slash.
  49       * @param array<string, mixed> $args {
  50       *     An associative array of arguments for the ability.
  51       *
  52       *     @type string               $label                 The human-readable label for the ability.
  53       *     @type string               $description           A detailed description of what the ability does.
  54       *     @type string               $category              The ability category slug this ability belongs to.
  55       *     @type callable             $execute_callback      A callback function to execute when the ability is invoked.
  56       *                                                       Receives optional mixed input and returns mixed result or WP_Error.
  57       *     @type callable             $permission_callback   A callback function to check permissions before execution.
  58       *                                                       Receives optional mixed input and returns bool or WP_Error.
  59       *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
  60       *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
  61       *     @type array<string, mixed> $meta                  {
  62       *         Optional. Additional metadata for the ability.
  63       *
  64       *         @type array<string, bool|null> $annotations  {
  65       *             Optional. Semantic annotations describing the ability's behavioral characteristics.
  66       *             These annotations are hints for tooling and documentation.
  67       *
  68       *             @type bool|null $readonly    Optional. If true, the ability does not modify its environment.
  69       *             @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment.
  70       *                                          If false, the ability performs only additive updates.
  71       *             @type bool|null $idempotent  Optional. If true, calling the ability repeatedly with the same arguments
  72       *                                          will have no additional effect on its environment.
  73       *         }
  74       *         @type bool                     $show_in_rest Optional. Whether to expose this ability in the REST API. Default false.
  75       *     }
  76       *     @type string               $ability_class         Optional. Custom class to instantiate instead of WP_Ability.
  77       * }
  78       * @return WP_Ability|null The registered ability instance on success, null on failure.
  79       */
  80  	public function register( string $name, array $args ): ?WP_Ability {
  81          if ( ! preg_match( '/^[a-z0-9-]+\/[a-z0-9-]+$/', $name ) ) {
  82              _doing_it_wrong(
  83                  __METHOD__,
  84                  __(
  85                      'Ability name must be a string containing a namespace prefix, i.e. "my-plugin/my-ability". It can only contain lowercase alphanumeric characters, dashes and the forward slash.'
  86                  ),
  87                  '6.9.0'
  88              );
  89              return null;
  90          }
  91  
  92          if ( $this->is_registered( $name ) ) {
  93              _doing_it_wrong(
  94                  __METHOD__,
  95                  /* translators: %s: Ability name. */
  96                  sprintf( __( 'Ability "%s" is already registered.' ), esc_html( $name ) ),
  97                  '6.9.0'
  98              );
  99              return null;
 100          }
 101  
 102          /**
 103           * Filters the ability arguments before they are validated and used to instantiate the ability.
 104           *
 105           * @since 6.9.0
 106           *
 107           * @param array<string, mixed> $args {
 108           *     An associative array of arguments for the ability.
 109           *
 110           *     @type string               $label                 The human-readable label for the ability.
 111           *     @type string               $description           A detailed description of what the ability does.
 112           *     @type string               $category              The ability category slug this ability belongs to.
 113           *     @type callable             $execute_callback      A callback function to execute when the ability is invoked.
 114           *                                                       Receives optional mixed input and returns mixed result or WP_Error.
 115           *     @type callable             $permission_callback   A callback function to check permissions before execution.
 116           *                                                       Receives optional mixed input and returns bool or WP_Error.
 117           *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
 118           *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 119           *     @type array<string, mixed> $meta                  {
 120           *         Optional. Additional metadata for the ability.
 121           *
 122           *         @type array<string, bool|string> $annotations  Optional. Annotation metadata for the ability.
 123           *         @type bool                       $show_in_rest Optional. Whether to expose this ability in the REST API. Default false.
 124           *     }
 125           *     @type string               $ability_class         Optional. Custom class to instantiate instead of WP_Ability.
 126           * }
 127           * @param string               $name The name of the ability, with its namespace.
 128           */
 129          $args = apply_filters( 'wp_register_ability_args', $args, $name );
 130  
 131          // Validate ability category exists if provided (will be validated as required in WP_Ability).
 132          if ( isset( $args['category'] ) ) {
 133              if ( ! wp_has_ability_category( $args['category'] ) ) {
 134                  _doing_it_wrong(
 135                      __METHOD__,
 136                      sprintf(
 137                          /* translators: %1$s: ability category slug, %2$s: ability name */
 138                          __( 'Ability category "%1$s" is not registered. Please register the ability category before assigning it to ability "%2$s".' ),
 139                          esc_html( $args['category'] ),
 140                          esc_html( $name )
 141                      ),
 142                      '6.9.0'
 143                  );
 144                  return null;
 145              }
 146          }
 147  
 148          // The class is only used to instantiate the ability, and is not a property of the ability itself.
 149          if ( isset( $args['ability_class'] ) && ! is_a( $args['ability_class'], WP_Ability::class, true ) ) {
 150              _doing_it_wrong(
 151                  __METHOD__,
 152                  __( 'The ability args should provide a valid `ability_class` that extends WP_Ability.' ),
 153                  '6.9.0'
 154              );
 155              return null;
 156          }
 157  
 158          /** @var class-string<WP_Ability> */
 159          $ability_class = $args['ability_class'] ?? WP_Ability::class;
 160          unset( $args['ability_class'] );
 161  
 162          try {
 163              // WP_Ability::prepare_properties() will throw an exception if the properties are invalid.
 164              $ability = new $ability_class( $name, $args );
 165          } catch ( InvalidArgumentException $e ) {
 166              _doing_it_wrong(
 167                  __METHOD__,
 168                  $e->getMessage(),
 169                  '6.9.0'
 170              );
 171              return null;
 172          }
 173  
 174          $this->registered_abilities[ $name ] = $ability;
 175          return $ability;
 176      }
 177  
 178      /**
 179       * Unregisters an ability.
 180       *
 181       * Do not use this method directly. Instead, use the `wp_unregister_ability()` function.
 182       *
 183       * @since 6.9.0
 184       *
 185       * @see wp_unregister_ability()
 186       *
 187       * @param string $name The name of the registered ability, with its namespace.
 188       * @return WP_Ability|null The unregistered ability instance on success, null on failure.
 189       */
 190  	public function unregister( string $name ): ?WP_Ability {
 191          if ( ! $this->is_registered( $name ) ) {
 192              _doing_it_wrong(
 193                  __METHOD__,
 194                  /* translators: %s: Ability name. */
 195                  sprintf( __( 'Ability "%s" not found.' ), esc_html( $name ) ),
 196                  '6.9.0'
 197              );
 198              return null;
 199          }
 200  
 201          $unregistered_ability = $this->registered_abilities[ $name ];
 202          unset( $this->registered_abilities[ $name ] );
 203  
 204          return $unregistered_ability;
 205      }
 206  
 207      /**
 208       * Retrieves the list of all registered abilities.
 209       *
 210       * Do not use this method directly. Instead, use the `wp_get_abilities()` function.
 211       *
 212       * @since 6.9.0
 213       *
 214       * @see wp_get_abilities()
 215       *
 216       * @return WP_Ability[] The array of registered abilities.
 217       */
 218  	public function get_all_registered(): array {
 219          return $this->registered_abilities;
 220      }
 221  
 222      /**
 223       * Checks if an ability is registered.
 224       *
 225       * Do not use this method directly. Instead, use the `wp_has_ability()` function.
 226       *
 227       * @since 6.9.0
 228       *
 229       * @see wp_has_ability()
 230       *
 231       * @param string $name The name of the registered ability, with its namespace.
 232       * @return bool True if the ability is registered, false otherwise.
 233       */
 234  	public function is_registered( string $name ): bool {
 235          return isset( $this->registered_abilities[ $name ] );
 236      }
 237  
 238      /**
 239       * Retrieves a registered ability.
 240       *
 241       * Do not use this method directly. Instead, use the `wp_get_ability()` function.
 242       *
 243       * @since 6.9.0
 244       *
 245       * @see wp_get_ability()
 246       *
 247       * @param string $name The name of the registered ability, with its namespace.
 248       * @return WP_Ability|null The registered ability instance, or null if it is not registered.
 249       */
 250  	public function get_registered( string $name ): ?WP_Ability {
 251          if ( ! $this->is_registered( $name ) ) {
 252              _doing_it_wrong(
 253                  __METHOD__,
 254                  /* translators: %s: Ability name. */
 255                  sprintf( __( 'Ability "%s" not found.' ), esc_html( $name ) ),
 256                  '6.9.0'
 257              );
 258              return null;
 259          }
 260          return $this->registered_abilities[ $name ];
 261      }
 262  
 263      /**
 264       * Utility method to retrieve the main instance of the registry class.
 265       *
 266       * The instance will be created if it does not exist yet.
 267       *
 268       * @since 6.9.0
 269       *
 270       * @return WP_Abilities_Registry|null The main registry instance, or null when `init` action has not fired.
 271       */
 272  	public static function get_instance(): ?self {
 273          if ( ! did_action( 'init' ) ) {
 274              _doing_it_wrong(
 275                  __METHOD__,
 276                  sprintf(
 277                      // translators: %s: init action.
 278                      __( 'Ability API should not be initialized before the %s action has fired.' ),
 279                      '<code>init</code>'
 280                  ),
 281                  '6.9.0'
 282              );
 283              return null;
 284          }
 285  
 286          if ( null === self::$instance ) {
 287              self::$instance = new self();
 288  
 289              // Ensure ability category registry is initialized first to allow categories to be registered
 290              // before abilities that depend on them.
 291              WP_Ability_Categories_Registry::get_instance();
 292  
 293              /**
 294               * Fires when preparing abilities registry.
 295               *
 296               * Abilities should be created and register their hooks on this action rather
 297               * than another action to ensure they're only loaded when needed.
 298               *
 299               * @since 6.9.0
 300               *
 301               * @param WP_Abilities_Registry $instance Abilities registry object.
 302               */
 303              do_action( 'wp_abilities_api_init', self::$instance );
 304          }
 305  
 306          return self::$instance;
 307      }
 308  
 309      /**
 310       * Wakeup magic method.
 311       *
 312       * @since 6.9.0
 313       * @throws LogicException If the registry object is unserialized.
 314       *                        This is a security hardening measure to prevent unserialization of the registry.
 315       */
 316  	public function __wakeup(): void {
 317          throw new LogicException( __CLASS__ . ' should never be unserialized.' );
 318      }
 319  
 320      /**
 321       * Sleep magic method.
 322       *
 323       * @since 6.9.0
 324       * @throws LogicException If the registry object is serialized.
 325       *                        This is a security hardening measure to prevent serialization of the registry.
 326       */
 327  	public function __sleep(): array {
 328          throw new LogicException( __CLASS__ . ' should never be serialized.' );
 329      }
 330  }


Generated : Mon May 25 08:20:05 2026 Cross-referenced by PHPXref