| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API list controller for Abilities API. 4 * 5 * @package WordPress 6 * @subpackage Abilities_API 7 * @since 6.9.0 8 */ 9 10 declare( strict_types = 1 ); 11 12 /** 13 * Core controller used to access abilities via the REST API. 14 * 15 * @since 6.9.0 16 * 17 * @see WP_REST_Controller 18 */ 19 class WP_REST_Abilities_V1_List_Controller extends WP_REST_Controller { 20 21 /** 22 * REST API namespace. 23 * 24 * @since 6.9.0 25 * @var string 26 */ 27 protected $namespace = 'wp-abilities/v1'; 28 29 /** 30 * REST API base route. 31 * 32 * @since 6.9.0 33 * @var string 34 */ 35 protected $rest_base = 'abilities'; 36 37 /** 38 * Registers the routes for abilities. 39 * 40 * @since 6.9.0 41 * 42 * @see register_rest_route() 43 */ 44 public function register_routes(): void { 45 register_rest_route( 46 $this->namespace, 47 '/' . $this->rest_base, 48 array( 49 array( 50 'methods' => WP_REST_Server::READABLE, 51 'callback' => array( $this, 'get_items' ), 52 'permission_callback' => array( $this, 'get_items_permissions_check' ), 53 'args' => $this->get_collection_params(), 54 ), 55 'schema' => array( $this, 'get_public_item_schema' ), 56 ) 57 ); 58 59 register_rest_route( 60 $this->namespace, 61 '/' . $this->rest_base . '/(?P<name>[a-zA-Z0-9\-\/]+)', 62 array( 63 'args' => array( 64 'name' => array( 65 'description' => __( 'Unique identifier for the ability.' ), 66 'type' => 'string', 67 'pattern' => '^[a-zA-Z0-9\-\/]+$', 68 ), 69 ), 70 array( 71 'methods' => WP_REST_Server::READABLE, 72 'callback' => array( $this, 'get_item' ), 73 'permission_callback' => array( $this, 'get_item_permissions_check' ), 74 ), 75 'schema' => array( $this, 'get_public_item_schema' ), 76 ) 77 ); 78 } 79 80 /** 81 * Retrieves all abilities. 82 * 83 * @since 6.9.0 84 * 85 * @param WP_REST_Request $request Full details about the request. 86 * @return WP_REST_Response Response object on success. 87 */ 88 public function get_items( $request ) { 89 $abilities = array_filter( 90 wp_get_abilities(), 91 static function ( $ability ) { 92 return $ability->get_meta_item( 'show_in_rest' ); 93 } 94 ); 95 96 // Filter by ability category if specified. 97 $category = $request['category']; 98 if ( ! empty( $category ) ) { 99 $abilities = array_filter( 100 $abilities, 101 static function ( $ability ) use ( $category ) { 102 return $ability->get_category() === $category; 103 } 104 ); 105 // Reset array keys after filtering. 106 $abilities = array_values( $abilities ); 107 } 108 109 $page = $request['page']; 110 $per_page = $request['per_page']; 111 $offset = ( $page - 1 ) * $per_page; 112 113 $total_abilities = count( $abilities ); 114 $max_pages = (int) ceil( $total_abilities / $per_page ); 115 116 if ( $request->get_method() === 'HEAD' ) { 117 $response = new WP_REST_Response( array() ); 118 } else { 119 $abilities = array_slice( $abilities, $offset, $per_page ); 120 121 $data = array(); 122 foreach ( $abilities as $ability ) { 123 $item = $this->prepare_item_for_response( $ability, $request ); 124 $data[] = $this->prepare_response_for_collection( $item ); 125 } 126 127 $response = rest_ensure_response( $data ); 128 } 129 130 $response->header( 'X-WP-Total', (string) $total_abilities ); 131 $response->header( 'X-WP-TotalPages', (string) $max_pages ); 132 133 $query_params = $request->get_query_params(); 134 $base = add_query_arg( urlencode_deep( $query_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); 135 136 if ( $page > 1 ) { 137 $prev_page = $page - 1; 138 $prev_link = add_query_arg( 'page', $prev_page, $base ); 139 $response->link_header( 'prev', $prev_link ); 140 } 141 142 if ( $page < $max_pages ) { 143 $next_page = $page + 1; 144 $next_link = add_query_arg( 'page', $next_page, $base ); 145 $response->link_header( 'next', $next_link ); 146 } 147 148 return $response; 149 } 150 151 /** 152 * Retrieves a specific ability. 153 * 154 * @since 6.9.0 155 * 156 * @param WP_REST_Request $request Full details about the request. 157 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 158 */ 159 public function get_item( $request ) { 160 $ability = wp_get_ability( $request['name'] ); 161 if ( ! $ability || ! $ability->get_meta_item( 'show_in_rest' ) ) { 162 return new WP_Error( 163 'rest_ability_not_found', 164 __( 'Ability not found.' ), 165 array( 'status' => 404 ) 166 ); 167 } 168 169 $data = $this->prepare_item_for_response( $ability, $request ); 170 return rest_ensure_response( $data ); 171 } 172 173 /** 174 * Checks if a given request has access to read ability items. 175 * 176 * @since 6.9.0 177 * 178 * @param WP_REST_Request $request Full details about the request. 179 * @return bool True if the request has read access. 180 */ 181 public function get_items_permissions_check( $request ) { 182 return current_user_can( 'read' ); 183 } 184 185 /** 186 * Checks if a given request has access to read an ability item. 187 * 188 * @since 6.9.0 189 * 190 * @param WP_REST_Request $request Full details about the request. 191 * @return bool True if the request has read access. 192 */ 193 public function get_item_permissions_check( $request ) { 194 return current_user_can( 'read' ); 195 } 196 197 /** 198 * Normalizes schema empty object defaults. 199 * 200 * Converts empty array defaults to objects when the schema type is 'object' 201 * to ensure proper JSON serialization as {} instead of []. 202 * 203 * @since 6.9.0 204 * 205 * @param array<string, mixed> $schema The schema array. 206 * @return array<string, mixed> The normalized schema. 207 */ 208 private function normalize_schema_empty_object_defaults( array $schema ): array { 209 if ( isset( $schema['type'] ) && 'object' === $schema['type'] && isset( $schema['default'] ) ) { 210 $default = $schema['default']; 211 if ( is_array( $default ) && empty( $default ) ) { 212 $schema['default'] = (object) $default; 213 } 214 } 215 return $schema; 216 } 217 218 /** 219 * Prepares an ability for response. 220 * 221 * @since 6.9.0 222 * 223 * @param WP_Ability $ability The ability object. 224 * @param WP_REST_Request $request Request object. 225 * @return WP_REST_Response Response object. 226 */ 227 public function prepare_item_for_response( $ability, $request ) { 228 $data = array( 229 'name' => $ability->get_name(), 230 'label' => $ability->get_label(), 231 'description' => $ability->get_description(), 232 'category' => $ability->get_category(), 233 'input_schema' => $this->normalize_schema_empty_object_defaults( $ability->get_input_schema() ), 234 'output_schema' => $this->normalize_schema_empty_object_defaults( $ability->get_output_schema() ), 235 'meta' => $ability->get_meta(), 236 ); 237 238 $context = $request['context'] ?? 'view'; 239 $data = $this->add_additional_fields_to_object( $data, $request ); 240 $data = $this->filter_response_by_context( $data, $context ); 241 242 $response = rest_ensure_response( $data ); 243 244 $fields = $this->get_fields_for_response( $request ); 245 if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { 246 $links = array( 247 'self' => array( 248 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $ability->get_name() ) ), 249 ), 250 'collection' => array( 251 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), 252 ), 253 ); 254 255 $links['wp:action-run'] = array( 256 'href' => rest_url( sprintf( '%s/%s/%s/run', $this->namespace, $this->rest_base, $ability->get_name() ) ), 257 ); 258 259 $response->add_links( $links ); 260 } 261 262 return $response; 263 } 264 265 /** 266 * Retrieves the ability's schema, conforming to JSON Schema. 267 * 268 * @since 6.9.0 269 * 270 * @return array<string, mixed> Item schema data. 271 */ 272 public function get_item_schema(): array { 273 $schema = array( 274 '$schema' => 'http://json-schema.org/draft-04/schema#', 275 'title' => 'ability', 276 'type' => 'object', 277 'properties' => array( 278 'name' => array( 279 'description' => __( 'Unique identifier for the ability.' ), 280 'type' => 'string', 281 'context' => array( 'view', 'edit', 'embed' ), 282 'readonly' => true, 283 ), 284 'label' => array( 285 'description' => __( 'Display label for the ability.' ), 286 'type' => 'string', 287 'context' => array( 'view', 'edit', 'embed' ), 288 'readonly' => true, 289 ), 290 'description' => array( 291 'description' => __( 'Description of the ability.' ), 292 'type' => 'string', 293 'context' => array( 'view', 'edit' ), 294 'readonly' => true, 295 ), 296 'category' => array( 297 'description' => __( 'Ability category this ability belongs to.' ), 298 'type' => 'string', 299 'context' => array( 'view', 'edit', 'embed' ), 300 'readonly' => true, 301 ), 302 'input_schema' => array( 303 'description' => __( 'JSON Schema for the ability input.' ), 304 'type' => 'object', 305 'context' => array( 'view', 'edit' ), 306 'readonly' => true, 307 ), 308 'output_schema' => array( 309 'description' => __( 'JSON Schema for the ability output.' ), 310 'type' => 'object', 311 'context' => array( 'view', 'edit' ), 312 'readonly' => true, 313 ), 314 'meta' => array( 315 'description' => __( 'Meta information about the ability.' ), 316 'type' => 'object', 317 'properties' => array( 318 'annotations' => array( 319 'description' => __( 'Annotations for the ability.' ), 320 'type' => array( 'boolean', 'null' ), 321 'default' => null, 322 ), 323 ), 324 'context' => array( 'view', 'edit' ), 325 'readonly' => true, 326 ), 327 ), 328 ); 329 330 return $this->add_additional_fields_schema( $schema ); 331 } 332 333 /** 334 * Retrieves the query params for collections. 335 * 336 * @since 6.9.0 337 * 338 * @return array<string, mixed> Collection parameters. 339 */ 340 public function get_collection_params(): array { 341 return array( 342 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 343 'page' => array( 344 'description' => __( 'Current page of the collection.' ), 345 'type' => 'integer', 346 'default' => 1, 347 'minimum' => 1, 348 ), 349 'per_page' => array( 350 'description' => __( 'Maximum number of items to be returned in result set.' ), 351 'type' => 'integer', 352 'default' => 50, 353 'minimum' => 1, 354 'maximum' => 100, 355 ), 356 'category' => array( 357 'description' => __( 'Limit results to abilities in specific ability category.' ), 358 'type' => 'string', 359 'sanitize_callback' => 'sanitize_key', 360 'validate_callback' => 'rest_validate_request_arg', 361 ), 362 ); 363 } 364 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Apr 15 08:20:10 2026 | Cross-referenced by PHPXref |