wp/wp-includes/rest-api.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    21  *
    21  *
    22  * @since 4.4.0
    22  * @since 4.4.0
    23  * @since 5.1.0 Added a `_doing_it_wrong()` notice when not called on or after the `rest_api_init` hook.
    23  * @since 5.1.0 Added a `_doing_it_wrong()` notice when not called on or after the `rest_api_init` hook.
    24  * @since 5.5.0 Added a `_doing_it_wrong()` notice when the required `permission_callback` argument is not set.
    24  * @since 5.5.0 Added a `_doing_it_wrong()` notice when the required `permission_callback` argument is not set.
    25  *
    25  *
    26  * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
    26  * @param string $route_namespace The first URL segment after core prefix. Should be unique to your package/plugin.
    27  * @param string $route     The base URL for route you are adding.
    27  * @param string $route           The base URL for route you are adding.
    28  * @param array  $args      Optional. Either an array of options for the endpoint, or an array of arrays for
    28  * @param array  $args            Optional. Either an array of options for the endpoint, or an array of arrays for
    29  *                          multiple methods. Default empty array.
    29  *                                multiple methods. Default empty array.
    30  * @param bool   $override  Optional. If the route already exists, should we override it? True overrides,
    30  * @param bool   $override        Optional. If the route already exists, should we override it? True overrides,
    31  *                          false merges (with newer overriding if duplicate keys exist). Default false.
    31  *                                false merges (with newer overriding if duplicate keys exist). Default false.
    32  * @return bool True on success, false on error.
    32  * @return bool True on success, false on error.
    33  */
    33  */
    34 function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
    34 function register_rest_route( $route_namespace, $route, $args = array(), $override = false ) {
    35 	if ( empty( $namespace ) ) {
    35 	if ( empty( $route_namespace ) ) {
    36 		/*
    36 		/*
    37 		 * Non-namespaced routes are not allowed, with the exception of the main
    37 		 * Non-namespaced routes are not allowed, with the exception of the main
    38 		 * and namespace indexes. If you really need to register a
    38 		 * and namespace indexes. If you really need to register a
    39 		 * non-namespaced route, call `WP_REST_Server::register_route` directly.
    39 		 * non-namespaced route, call `WP_REST_Server::register_route` directly.
    40 		 */
    40 		 */
    43 	} elseif ( empty( $route ) ) {
    43 	} elseif ( empty( $route ) ) {
    44 		_doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' );
    44 		_doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' );
    45 		return false;
    45 		return false;
    46 	}
    46 	}
    47 
    47 
    48 	$clean_namespace = trim( $namespace, '/' );
    48 	$clean_namespace = trim( $route_namespace, '/' );
    49 
    49 
    50 	if ( $clean_namespace !== $namespace ) {
    50 	if ( $clean_namespace !== $route_namespace ) {
    51 		_doing_it_wrong( __FUNCTION__, __( 'Namespace must not start or end with a slash.' ), '5.4.2' );
    51 		_doing_it_wrong( __FUNCTION__, __( 'Namespace must not start or end with a slash.' ), '5.4.2' );
    52 	}
    52 	}
    53 
    53 
    54 	if ( ! did_action( 'rest_api_init' ) ) {
    54 	if ( ! did_action( 'rest_api_init' ) ) {
    55 		_doing_it_wrong(
    55 		_doing_it_wrong(
   101 					'<code>__return_true</code>'
   101 					'<code>__return_true</code>'
   102 				),
   102 				),
   103 				'5.5.0'
   103 				'5.5.0'
   104 			);
   104 			);
   105 		}
   105 		}
       
   106 
       
   107 		foreach ( $arg_group['args'] as $arg ) {
       
   108 			if ( ! is_array( $arg ) ) {
       
   109 				_doing_it_wrong(
       
   110 					__FUNCTION__,
       
   111 					sprintf(
       
   112 						/* translators: 1: $args, 2: The REST API route being registered. */
       
   113 						__( 'REST API %1$s should be an array of arrays. Non-array value detected for %2$s.' ),
       
   114 						'<code>$args</code>',
       
   115 						'<code>' . $clean_namespace . '/' . trim( $route, '/' ) . '</code>'
       
   116 					),
       
   117 					'6.1.0'
       
   118 				);
       
   119 				break; // Leave the foreach loop once a non-array argument was found.
       
   120 			}
       
   121 		}
   106 	}
   122 	}
   107 
   123 
   108 	$full_route = '/' . $clean_namespace . '/' . trim( $route, '/' );
   124 	$full_route = '/' . $clean_namespace . '/' . trim( $route, '/' );
   109 	rest_get_server()->register_route( $clean_namespace, $full_route, $args, $override );
   125 	rest_get_server()->register_route( $clean_namespace, $full_route, $args, $override );
   110 	return true;
   126 	return true;
   191  * to make testing and disabling these filters easier.
   207  * to make testing and disabling these filters easier.
   192  *
   208  *
   193  * @since 4.4.0
   209  * @since 4.4.0
   194  */
   210  */
   195 function rest_api_default_filters() {
   211 function rest_api_default_filters() {
   196 	if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
   212 	if ( wp_is_serving_rest_request() ) {
   197 		// Deprecated reporting.
   213 		// Deprecated reporting.
   198 		add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
   214 		add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
   199 		add_filter( 'deprecated_function_trigger_error', '__return_false' );
   215 		add_filter( 'deprecated_function_trigger_error', '__return_false' );
   200 		add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
   216 		add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
   201 		add_filter( 'deprecated_argument_trigger_error', '__return_false' );
   217 		add_filter( 'deprecated_argument_trigger_error', '__return_false' );
   223 
   239 
   224 		if ( ! $controller ) {
   240 		if ( ! $controller ) {
   225 			continue;
   241 			continue;
   226 		}
   242 		}
   227 
   243 
   228 		$controller->register_routes();
   244 		if ( ! $post_type->late_route_registration ) {
   229 
   245 			$controller->register_routes();
   230 		if ( post_type_supports( $post_type->name, 'revisions' ) ) {
   246 		}
   231 			$revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
   247 
       
   248 		$revisions_controller = $post_type->get_revisions_rest_controller();
       
   249 		if ( $revisions_controller ) {
   232 			$revisions_controller->register_routes();
   250 			$revisions_controller->register_routes();
   233 		}
   251 		}
   234 
   252 
   235 		if ( 'attachment' !== $post_type->name ) {
   253 		$autosaves_controller = $post_type->get_autosave_rest_controller();
   236 			$autosaves_controller = new WP_REST_Autosaves_Controller( $post_type->name );
   254 		if ( $autosaves_controller ) {
   237 			$autosaves_controller->register_routes();
   255 			$autosaves_controller->register_routes();
   238 		}
   256 		}
       
   257 
       
   258 		if ( $post_type->late_route_registration ) {
       
   259 			$controller->register_routes();
       
   260 		}
   239 	}
   261 	}
   240 
   262 
   241 	// Post types.
   263 	// Post types.
   242 	$controller = new WP_REST_Post_Types_Controller;
   264 	$controller = new WP_REST_Post_Types_Controller();
   243 	$controller->register_routes();
   265 	$controller->register_routes();
   244 
   266 
   245 	// Post statuses.
   267 	// Post statuses.
   246 	$controller = new WP_REST_Post_Statuses_Controller;
   268 	$controller = new WP_REST_Post_Statuses_Controller();
   247 	$controller->register_routes();
   269 	$controller->register_routes();
   248 
   270 
   249 	// Taxonomies.
   271 	// Taxonomies.
   250 	$controller = new WP_REST_Taxonomies_Controller;
   272 	$controller = new WP_REST_Taxonomies_Controller();
   251 	$controller->register_routes();
   273 	$controller->register_routes();
   252 
   274 
   253 	// Terms.
   275 	// Terms.
   254 	foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) {
   276 	foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) {
   255 		$controller = $taxonomy->get_rest_controller();
   277 		$controller = $taxonomy->get_rest_controller();
   260 
   282 
   261 		$controller->register_routes();
   283 		$controller->register_routes();
   262 	}
   284 	}
   263 
   285 
   264 	// Users.
   286 	// Users.
   265 	$controller = new WP_REST_Users_Controller;
   287 	$controller = new WP_REST_Users_Controller();
   266 	$controller->register_routes();
   288 	$controller->register_routes();
   267 
   289 
   268 	// Application Passwords
   290 	// Application Passwords
   269 	$controller = new WP_REST_Application_Passwords_Controller();
   291 	$controller = new WP_REST_Application_Passwords_Controller();
   270 	$controller->register_routes();
   292 	$controller->register_routes();
   271 
   293 
   272 	// Comments.
   294 	// Comments.
   273 	$controller = new WP_REST_Comments_Controller;
   295 	$controller = new WP_REST_Comments_Controller();
   274 	$controller->register_routes();
   296 	$controller->register_routes();
   275 
   297 
   276 	$search_handlers = array(
   298 	$search_handlers = array(
   277 		new WP_REST_Post_Search_Handler(),
   299 		new WP_REST_Post_Search_Handler(),
   278 		new WP_REST_Term_Search_Handler(),
   300 		new WP_REST_Term_Search_Handler(),
   299 
   321 
   300 	// Block Types.
   322 	// Block Types.
   301 	$controller = new WP_REST_Block_Types_Controller();
   323 	$controller = new WP_REST_Block_Types_Controller();
   302 	$controller->register_routes();
   324 	$controller->register_routes();
   303 
   325 
   304 	// Global Styles.
   326 	// Settings.
   305 	$controller = new WP_REST_Global_Styles_Controller;
   327 	$controller = new WP_REST_Settings_Controller();
   306 	$controller->register_routes();
   328 	$controller->register_routes();
   307 
   329 
   308 	// Settings.
       
   309 	$controller = new WP_REST_Settings_Controller;
       
   310 	$controller->register_routes();
       
   311 
       
   312 	// Themes.
   330 	// Themes.
   313 	$controller = new WP_REST_Themes_Controller;
   331 	$controller = new WP_REST_Themes_Controller();
   314 	$controller->register_routes();
   332 	$controller->register_routes();
   315 
   333 
   316 	// Plugins.
   334 	// Plugins.
   317 	$controller = new WP_REST_Plugins_Controller();
   335 	$controller = new WP_REST_Plugins_Controller();
   318 	$controller->register_routes();
   336 	$controller->register_routes();
   359 	$controller->register_routes();
   377 	$controller->register_routes();
   360 
   378 
   361 	// Site Editor Export.
   379 	// Site Editor Export.
   362 	$controller = new WP_REST_Edit_Site_Export_Controller();
   380 	$controller = new WP_REST_Edit_Site_Export_Controller();
   363 	$controller->register_routes();
   381 	$controller->register_routes();
       
   382 
       
   383 	// Navigation Fallback.
       
   384 	$controller = new WP_REST_Navigation_Fallback_Controller();
       
   385 	$controller->register_routes();
       
   386 
       
   387 	// Font Collections.
       
   388 	$font_collections_controller = new WP_REST_Font_Collections_Controller();
       
   389 	$font_collections_controller->register_routes();
   364 }
   390 }
   365 
   391 
   366 /**
   392 /**
   367  * Loads the REST API.
   393  * Loads the REST API.
   368  *
   394  *
   447 		}
   473 		}
   448 
   474 
   449 		$url .= $path;
   475 		$url .= $path;
   450 	} else {
   476 	} else {
   451 		$url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
   477 		$url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
   452 		// nginx only allows HTTP/1.0 methods when redirecting from / to /index.php.
   478 		/*
   453 		// To work around this, we manually add index.php to the URL, avoiding the redirect.
   479 		 * nginx only allows HTTP/1.0 methods when redirecting from / to /index.php.
   454 		if ( 'index.php' !== substr( $url, 9 ) ) {
   480 		 * To work around this, we manually add index.php to the URL, avoiding the redirect.
       
   481 		 */
       
   482 		if ( ! str_ends_with( $url, 'index.php' ) ) {
   455 			$url .= 'index.php';
   483 			$url .= 'index.php';
   456 		}
   484 		}
   457 
   485 
   458 		$url = add_query_arg( 'rest_route', $path, $url );
   486 		$url = add_query_arg( 'rest_route', $path, $url );
   459 	}
   487 	}
   544 		 * @since 4.4.0
   572 		 * @since 4.4.0
   545 		 *
   573 		 *
   546 		 * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
   574 		 * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
   547 		 */
   575 		 */
   548 		$wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
   576 		$wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
   549 		$wp_rest_server       = new $wp_rest_server_class;
   577 		$wp_rest_server       = new $wp_rest_server_class();
   550 
   578 
   551 		/**
   579 		/**
   552 		 * Fires when preparing to serve a REST API request.
   580 		 * Fires when preparing to serve a REST API request.
   553 		 *
   581 		 *
   554 		 * Endpoint objects should be created and register their hooks on this action rather
   582 		 * Endpoint objects should be created and register their hooks on this action rather
   606 
   634 
   607 	if ( $response instanceof WP_REST_Response ) {
   635 	if ( $response instanceof WP_REST_Response ) {
   608 		return $response;
   636 		return $response;
   609 	}
   637 	}
   610 
   638 
   611 	// While WP_HTTP_Response is the base class of WP_REST_Response, it doesn't provide
   639 	/*
   612 	// all the required methods used in WP_REST_Server::dispatch().
   640 	 * While WP_HTTP_Response is the base class of WP_REST_Response, it doesn't provide
       
   641 	 * all the required methods used in WP_REST_Server::dispatch().
       
   642 	 */
   613 	if ( $response instanceof WP_HTTP_Response ) {
   643 	if ( $response instanceof WP_HTTP_Response ) {
   614 		return new WP_REST_Response(
   644 		return new WP_REST_Response(
   615 			$response->get_data(),
   645 			$response->get_data(),
   616 			$response->get_status(),
   646 			$response->get_status(),
   617 			$response->get_headers()
   647 			$response->get_headers()
   624 /**
   654 /**
   625  * Handles _deprecated_function() errors.
   655  * Handles _deprecated_function() errors.
   626  *
   656  *
   627  * @since 4.4.0
   657  * @since 4.4.0
   628  *
   658  *
   629  * @param string $function    The function that was called.
   659  * @param string $function_name The function that was called.
   630  * @param string $replacement The function that should have been called.
   660  * @param string $replacement   The function that should have been called.
   631  * @param string $version     Version.
   661  * @param string $version       Version.
   632  */
   662  */
   633 function rest_handle_deprecated_function( $function, $replacement, $version ) {
   663 function rest_handle_deprecated_function( $function_name, $replacement, $version ) {
   634 	if ( ! WP_DEBUG || headers_sent() ) {
   664 	if ( ! WP_DEBUG || headers_sent() ) {
   635 		return;
   665 		return;
   636 	}
   666 	}
   637 	if ( ! empty( $replacement ) ) {
   667 	if ( ! empty( $replacement ) ) {
   638 		/* translators: 1: Function name, 2: WordPress version number, 3: New function name. */
   668 		/* translators: 1: Function name, 2: WordPress version number, 3: New function name. */
   639 		$string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
   669 		$string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function_name, $version, $replacement );
   640 	} else {
   670 	} else {
   641 		/* translators: 1: Function name, 2: WordPress version number. */
   671 		/* translators: 1: Function name, 2: WordPress version number. */
   642 		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
   672 		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function_name, $version );
   643 	}
   673 	}
   644 
   674 
   645 	header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
   675 	header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
   646 }
   676 }
   647 
   677 
   648 /**
   678 /**
   649  * Handles _deprecated_argument() errors.
   679  * Handles _deprecated_argument() errors.
   650  *
   680  *
   651  * @since 4.4.0
   681  * @since 4.4.0
   652  *
   682  *
   653  * @param string $function    The function that was called.
   683  * @param string $function_name The function that was called.
   654  * @param string $message     A message regarding the change.
   684  * @param string $message       A message regarding the change.
   655  * @param string $version     Version.
   685  * @param string $version       Version.
   656  */
   686  */
   657 function rest_handle_deprecated_argument( $function, $message, $version ) {
   687 function rest_handle_deprecated_argument( $function_name, $message, $version ) {
   658 	if ( ! WP_DEBUG || headers_sent() ) {
   688 	if ( ! WP_DEBUG || headers_sent() ) {
   659 		return;
   689 		return;
   660 	}
   690 	}
   661 	if ( $message ) {
   691 	if ( $message ) {
   662 		/* translators: 1: Function name, 2: WordPress version number, 3: Error message. */
   692 		/* translators: 1: Function name, 2: WordPress version number, 3: Error message. */
   663 		$string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $message );
   693 		$string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function_name, $version, $message );
   664 	} else {
   694 	} else {
   665 		/* translators: 1: Function name, 2: WordPress version number. */
   695 		/* translators: 1: Function name, 2: WordPress version number. */
   666 		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
   696 		$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function_name, $version );
   667 	}
   697 	}
   668 
   698 
   669 	header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
   699 	header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
   670 }
   700 }
   671 
   701 
   672 /**
   702 /**
   673  * Handles _doing_it_wrong errors.
   703  * Handles _doing_it_wrong errors.
   674  *
   704  *
   675  * @since 5.5.0
   705  * @since 5.5.0
   676  *
   706  *
   677  * @param string      $function The function that was called.
   707  * @param string      $function_name The function that was called.
   678  * @param string      $message  A message explaining what has been done incorrectly.
   708  * @param string      $message       A message explaining what has been done incorrectly.
   679  * @param string|null $version  The version of WordPress where the message was added.
   709  * @param string|null $version       The version of WordPress where the message was added.
   680  */
   710  */
   681 function rest_handle_doing_it_wrong( $function, $message, $version ) {
   711 function rest_handle_doing_it_wrong( $function_name, $message, $version ) {
   682 	if ( ! WP_DEBUG || headers_sent() ) {
   712 	if ( ! WP_DEBUG || headers_sent() ) {
   683 		return;
   713 		return;
   684 	}
   714 	}
   685 
   715 
   686 	if ( $version ) {
   716 	if ( $version ) {
   687 		/* translators: Developer debugging message. 1: PHP function name, 2: WordPress version number, 3: Explanatory message. */
   717 		/* translators: Developer debugging message. 1: PHP function name, 2: WordPress version number, 3: Explanatory message. */
   688 		$string = __( '%1$s (since %2$s; %3$s)' );
   718 		$string = __( '%1$s (since %2$s; %3$s)' );
   689 		$string = sprintf( $string, $function, $version, $message );
   719 		$string = sprintf( $string, $function_name, $version, $message );
   690 	} else {
   720 	} else {
   691 		/* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message. */
   721 		/* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message. */
   692 		$string = __( '%1$s (%2$s)' );
   722 		$string = __( '%1$s (%2$s)' );
   693 		$string = sprintf( $string, $function, $message );
   723 		$string = sprintf( $string, $function_name, $message );
   694 	}
   724 	}
   695 
   725 
   696 	header( sprintf( 'X-WP-DoingItWrong: %s', $string ) );
   726 	header( sprintf( 'X-WP-DoingItWrong: %s', $string ) );
   697 }
   727 }
   698 
   728 
   708 	$origin = get_http_origin();
   738 	$origin = get_http_origin();
   709 
   739 
   710 	if ( $origin ) {
   740 	if ( $origin ) {
   711 		// Requests from file:// and data: URLs send "Origin: null".
   741 		// Requests from file:// and data: URLs send "Origin: null".
   712 		if ( 'null' !== $origin ) {
   742 		if ( 'null' !== $origin ) {
   713 			$origin = esc_url_raw( $origin );
   743 			$origin = sanitize_url( $origin );
   714 		}
   744 		}
   715 		header( 'Access-Control-Allow-Origin: ' . $origin );
   745 		header( 'Access-Control-Allow-Origin: ' . $origin );
   716 		header( 'Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE' );
   746 		header( 'Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE' );
   717 		header( 'Access-Control-Allow-Credentials: true' );
   747 		header( 'Access-Control-Allow-Credentials: true' );
   718 		header( 'Vary: Origin', false );
   748 		header( 'Vary: Origin', false );
   840 	}
   870 	}
   841 	return $array1;
   871 	return $array1;
   842 }
   872 }
   843 
   873 
   844 /**
   874 /**
   845  * Filters the REST API response to include only a white-listed set of response object fields.
   875  * Filters the REST API response to include only an allow-listed set of response object fields.
   846  *
   876  *
   847  * @since 4.8.0
   877  * @since 4.8.0
   848  *
   878  *
   849  * @param WP_REST_Response $response Current response being served.
   879  * @param WP_REST_Response $response Current response being served.
   850  * @param WP_REST_Server   $server   ResponseHandler instance (usually WP_REST_Server).
   880  * @param WP_REST_Server   $server   ResponseHandler instance (usually WP_REST_Server).
   919 	if ( in_array( $field, $fields, true ) ) {
   949 	if ( in_array( $field, $fields, true ) ) {
   920 		return true;
   950 		return true;
   921 	}
   951 	}
   922 
   952 
   923 	foreach ( $fields as $accepted_field ) {
   953 	foreach ( $fields as $accepted_field ) {
   924 		// Check to see if $field is the parent of any item in $fields.
   954 		/*
   925 		// A field "parent" should be accepted if "parent.child" is accepted.
   955 		 * Check to see if $field is the parent of any item in $fields.
   926 		if ( strpos( $accepted_field, "$field." ) === 0 ) {
   956 		 * A field "parent" should be accepted if "parent.child" is accepted.
       
   957 		 */
       
   958 		if ( str_starts_with( $accepted_field, "$field." ) ) {
   927 			return true;
   959 			return true;
   928 		}
   960 		}
   929 		// Conversely, if "parent" is accepted, all "parent.child" fields
   961 		/*
   930 		// should also be accepted.
   962 		 * Conversely, if "parent" is accepted, all "parent.child" fields
   931 		if ( strpos( $field, "$accepted_field." ) === 0 ) {
   963 		 * should also be accepted.
       
   964 		 */
       
   965 		if ( str_starts_with( $field, "$accepted_field." ) ) {
   932 			return true;
   966 			return true;
   933 		}
   967 		}
   934 	}
   968 	}
   935 
   969 
   936 	return false;
   970 	return false;
   971 	printf( '<link rel="https://api.w.org/" href="%s" />', esc_url( $api_root ) );
  1005 	printf( '<link rel="https://api.w.org/" href="%s" />', esc_url( $api_root ) );
   972 
  1006 
   973 	$resource = rest_get_queried_resource_route();
  1007 	$resource = rest_get_queried_resource_route();
   974 
  1008 
   975 	if ( $resource ) {
  1009 	if ( $resource ) {
   976 		printf( '<link rel="alternate" type="application/json" href="%s" />', esc_url( rest_url( $resource ) ) );
  1010 		printf(
       
  1011 			'<link rel="alternate" title="%1$s" type="application/json" href="%2$s" />',
       
  1012 			_x( 'JSON', 'REST API resource link name' ),
       
  1013 			esc_url( rest_url( $resource ) )
       
  1014 		);
   977 	}
  1015 	}
   978 }
  1016 }
   979 
  1017 
   980 /**
  1018 /**
   981  * Sends a Link header for the REST API.
  1019  * Sends a Link header for the REST API.
   991 
  1029 
   992 	if ( empty( $api_root ) ) {
  1030 	if ( empty( $api_root ) ) {
   993 		return;
  1031 		return;
   994 	}
  1032 	}
   995 
  1033 
   996 	header( sprintf( 'Link: <%s>; rel="https://api.w.org/"', esc_url_raw( $api_root ) ), false );
  1034 	header( sprintf( 'Link: <%s>; rel="https://api.w.org/"', sanitize_url( $api_root ) ), false );
   997 
  1035 
   998 	$resource = rest_get_queried_resource_route();
  1036 	$resource = rest_get_queried_resource_route();
   999 
  1037 
  1000 	if ( $resource ) {
  1038 	if ( $resource ) {
  1001 		header( sprintf( 'Link: <%s>; rel="alternate"; type="application/json"', esc_url_raw( rest_url( $resource ) ) ), false );
  1039 		header(
       
  1040 			sprintf(
       
  1041 				'Link: <%1$s>; rel="alternate"; title="%2$s"; type="application/json"',
       
  1042 				sanitize_url( rest_url( $resource ) ),
       
  1043 				_x( 'JSON', 'REST API resource link name' )
       
  1044 			),
       
  1045 			false
       
  1046 		);
  1002 	}
  1047 	}
  1003 }
  1048 }
  1004 
  1049 
  1005 /**
  1050 /**
  1006  * Checks for errors when using cookie-based authentication.
  1051  * Checks for errors when using cookie-based authentication.
  1050 
  1095 
  1051 	// Check the nonce.
  1096 	// Check the nonce.
  1052 	$result = wp_verify_nonce( $nonce, 'wp_rest' );
  1097 	$result = wp_verify_nonce( $nonce, 'wp_rest' );
  1053 
  1098 
  1054 	if ( ! $result ) {
  1099 	if ( ! $result ) {
       
  1100 		add_filter( 'rest_send_nocache_headers', '__return_true', 20 );
  1055 		return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie check failed' ), array( 'status' => 403 ) );
  1101 		return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie check failed' ), array( 'status' => 403 ) );
  1056 	}
  1102 	}
  1057 
  1103 
  1058 	// Send a refreshed nonce in header.
  1104 	// Send a refreshed nonce in header.
  1059 	rest_get_server()->send_header( 'X-WP-Nonce', wp_create_nonce( 'wp_rest' ) );
  1105 	rest_get_server()->send_header( 'X-WP-Nonce', wp_create_nonce( 'wp_rest' ) );
  1181 
  1227 
  1182 	return $response;
  1228 	return $response;
  1183 }
  1229 }
  1184 
  1230 
  1185 /**
  1231 /**
  1186  * Retrieves the avatar urls in various sizes.
  1232  * Retrieves the avatar URLs in various sizes.
  1187  *
  1233  *
  1188  * @since 4.7.0
  1234  * @since 4.7.0
  1189  *
  1235  *
  1190  * @see get_avatar_url()
  1236  * @see get_avatar_url()
  1191  *
  1237  *
  1192  * @param mixed $id_or_email The Gravatar to retrieve a URL for. Accepts a user_id, gravatar md5 hash,
  1238  * @param mixed $id_or_email The avatar to retrieve a URL for. Accepts a user ID, Gravatar MD5 hash,
  1193  *                           user email, WP_User object, WP_Post object, or WP_Comment object.
  1239  *                           user email, WP_User object, WP_Post object, or WP_Comment object.
  1194  * @return array Avatar URLs keyed by size. Each value can be a URL string or boolean false.
  1240  * @return (string|false)[] Avatar URLs keyed by size. Each value can be a URL string or boolean false.
  1195  */
  1241  */
  1196 function rest_get_avatar_urls( $id_or_email ) {
  1242 function rest_get_avatar_urls( $id_or_email ) {
  1197 	$avatar_sizes = rest_get_avatar_sizes();
  1243 	$avatar_sizes = rest_get_avatar_sizes();
  1198 
  1244 
  1199 	$urls = array();
  1245 	$urls = array();
  1232  * @since 4.4.0
  1278  * @since 4.4.0
  1233  *
  1279  *
  1234  * @param string $date      RFC3339 timestamp.
  1280  * @param string $date      RFC3339 timestamp.
  1235  * @param bool   $force_utc Optional. Whether to force UTC timezone instead of using
  1281  * @param bool   $force_utc Optional. Whether to force UTC timezone instead of using
  1236  *                          the timestamp's timezone. Default false.
  1282  *                          the timestamp's timezone. Default false.
  1237  * @return int Unix timestamp.
  1283  * @return int|false Unix timestamp on success, false on failure.
  1238  */
  1284  */
  1239 function rest_parse_date( $date, $force_utc = false ) {
  1285 function rest_parse_date( $date, $force_utc = false ) {
  1240 	if ( $force_utc ) {
  1286 	if ( $force_utc ) {
  1241 		$date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
  1287 		$date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
  1242 	}
  1288 	}
  1254  * Parses a 3 or 6 digit hex color (with #).
  1300  * Parses a 3 or 6 digit hex color (with #).
  1255  *
  1301  *
  1256  * @since 5.4.0
  1302  * @since 5.4.0
  1257  *
  1303  *
  1258  * @param string $color 3 or 6 digit hex color (with #).
  1304  * @param string $color 3 or 6 digit hex color (with #).
  1259  * @return string|false
  1305  * @return string|false Color value on success, false on failure.
  1260  */
  1306  */
  1261 function rest_parse_hex_color( $color ) {
  1307 function rest_parse_hex_color( $color ) {
  1262 	$regex = '|^#([A-Fa-f0-9]{3}){1,2}$|';
  1308 	$regex = '|^#([A-Fa-f0-9]{3}){1,2}$|';
  1263 	if ( ! preg_match( $regex, $color, $matches ) ) {
  1309 	if ( ! preg_match( $regex, $color, $matches ) ) {
  1264 		return false;
  1310 		return false;
  1274  *
  1320  *
  1275  * @see rest_parse_date()
  1321  * @see rest_parse_date()
  1276  *
  1322  *
  1277  * @param string $date   RFC3339 timestamp.
  1323  * @param string $date   RFC3339 timestamp.
  1278  * @param bool   $is_utc Whether the provided date should be interpreted as UTC. Default false.
  1324  * @param bool   $is_utc Whether the provided date should be interpreted as UTC. Default false.
  1279  * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
  1325  * @return array|null {
  1280  *                    null on failure.
  1326  *     Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
       
  1327  *     null on failure.
       
  1328  *
       
  1329  *     @type string $0 Local datetime string.
       
  1330  *     @type string $1 UTC datetime string.
       
  1331  * }
  1281  */
  1332  */
  1282 function rest_get_date_with_gmt( $date, $is_utc = false ) {
  1333 function rest_get_date_with_gmt( $date, $is_utc = false ) {
  1283 	/*
  1334 	/*
  1284 	 * Whether or not the original date actually has a timezone string
  1335 	 * Whether or not the original date actually has a timezone string
  1285 	 * changes the way we need to do timezone conversion.
  1336 	 * changes the way we need to do timezone conversion.
  1396  * @return string|false The valid IP address, otherwise false.
  1447  * @return string|false The valid IP address, otherwise false.
  1397  */
  1448  */
  1398 function rest_is_ip_address( $ip ) {
  1449 function rest_is_ip_address( $ip ) {
  1399 	$ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
  1450 	$ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
  1400 
  1451 
  1401 	if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) {
  1452 	if ( ! preg_match( $ipv4_pattern, $ip ) && ! WpOrg\Requests\Ipv6::check_ipv6( $ip ) ) {
  1402 		return false;
  1453 		return false;
  1403 	}
  1454 	}
  1404 
  1455 
  1405 	return $ip;
  1456 	return $ip;
  1406 }
  1457 }
  1531 
  1582 
  1532 	return is_array( $maybe_object );
  1583 	return is_array( $maybe_object );
  1533 }
  1584 }
  1534 
  1585 
  1535 /**
  1586 /**
  1536  * Converts an object-like value to an object.
  1587  * Converts an object-like value to an array.
  1537  *
  1588  *
  1538  * @since 5.5.0
  1589  * @since 5.5.0
  1539  *
  1590  *
  1540  * @param mixed $maybe_object The value being evaluated.
  1591  * @param mixed $maybe_object The value being evaluated.
  1541  * @return array Returns the object extracted from the value.
  1592  * @return array Returns the object extracted from the value as an associative array.
  1542  */
  1593  */
  1543 function rest_sanitize_object( $maybe_object ) {
  1594 function rest_sanitize_object( $maybe_object ) {
  1544 	if ( '' === $maybe_object ) {
  1595 	if ( '' === $maybe_object ) {
  1545 		return array();
  1596 		return array();
  1546 	}
  1597 	}
  1563 /**
  1614 /**
  1564  * Gets the best type for a value.
  1615  * Gets the best type for a value.
  1565  *
  1616  *
  1566  * @since 5.5.0
  1617  * @since 5.5.0
  1567  *
  1618  *
  1568  * @param mixed $value The value to check.
  1619  * @param mixed    $value The value to check.
  1569  * @param array $types The list of possible types.
  1620  * @param string[] $types The list of possible types.
  1570  * @return string The best matching type, an empty string if no types match.
  1621  * @return string The best matching type, an empty string if no types match.
  1571  */
  1622  */
  1572 function rest_get_best_type_for_value( $value, $types ) {
  1623 function rest_get_best_type_for_value( $value, $types ) {
  1573 	static $checks = array(
  1624 	static $checks = array(
  1574 		'array'   => 'rest_is_array',
  1625 		'array'   => 'rest_is_array',
  1578 		'boolean' => 'rest_is_boolean',
  1629 		'boolean' => 'rest_is_boolean',
  1579 		'string'  => 'is_string',
  1630 		'string'  => 'is_string',
  1580 		'null'    => 'is_null',
  1631 		'null'    => 'is_null',
  1581 	);
  1632 	);
  1582 
  1633 
  1583 	// Both arrays and objects allow empty strings to be converted to their types.
  1634 	/*
  1584 	// But the best answer for this type is a string.
  1635 	 * Both arrays and objects allow empty strings to be converted to their types.
       
  1636 	 * But the best answer for this type is a string.
       
  1637 	 */
  1585 	if ( '' === $value && in_array( 'string', $types, true ) ) {
  1638 	if ( '' === $value && in_array( 'string', $types, true ) ) {
  1586 		return 'string';
  1639 		return 'string';
  1587 	}
  1640 	}
  1588 
  1641 
  1589 	foreach ( $types as $type ) {
  1642 	foreach ( $types as $type ) {
  1638 /**
  1691 /**
  1639  * Checks if an array is made up of unique items.
  1692  * Checks if an array is made up of unique items.
  1640  *
  1693  *
  1641  * @since 5.5.0
  1694  * @since 5.5.0
  1642  *
  1695  *
  1643  * @param array $array The array to check.
  1696  * @param array $input_array The array to check.
  1644  * @return bool True if the array contains unique items, false otherwise.
  1697  * @return bool True if the array contains unique items, false otherwise.
  1645  */
  1698  */
  1646 function rest_validate_array_contains_unique_items( $array ) {
  1699 function rest_validate_array_contains_unique_items( $input_array ) {
  1647 	$seen = array();
  1700 	$seen = array();
  1648 
  1701 
  1649 	foreach ( $array as $item ) {
  1702 	foreach ( $input_array as $item ) {
  1650 		$stabilized = rest_stabilize_value( $item );
  1703 		$stabilized = rest_stabilize_value( $item );
  1651 		$key        = serialize( $stabilized );
  1704 		$key        = serialize( $stabilized );
  1652 
  1705 
  1653 		if ( ! isset( $seen[ $key ] ) ) {
  1706 		if ( ! isset( $seen[ $key ] ) ) {
  1654 			$seen[ $key ] = true;
  1707 			$seen[ $key ] = true;
  2160 		if ( is_wp_error( $enum_contains_value ) ) {
  2213 		if ( is_wp_error( $enum_contains_value ) ) {
  2161 			return $enum_contains_value;
  2214 			return $enum_contains_value;
  2162 		}
  2215 		}
  2163 	}
  2216 	}
  2164 
  2217 
  2165 	// The "format" keyword should only be applied to strings. However, for backward compatibility,
  2218 	/*
  2166 	// we allow the "format" keyword if the type keyword was not specified, or was set to an invalid value.
  2219 	 * The "format" keyword should only be applied to strings. However, for backward compatibility,
       
  2220 	 * we allow the "format" keyword if the type keyword was not specified, or was set to an invalid value.
       
  2221 	 */
  2167 	if ( isset( $args['format'] )
  2222 	if ( isset( $args['format'] )
  2168 		&& ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) )
  2223 		&& ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) )
  2169 	) {
  2224 	) {
  2170 		switch ( $args['format'] ) {
  2225 		switch ( $args['format'] ) {
  2171 			case 'hex-color':
  2226 			case 'hex-color':
  2793 			case 'email':
  2848 			case 'email':
  2794 				// sanitize_email() validates, which would be unexpected.
  2849 				// sanitize_email() validates, which would be unexpected.
  2795 				return sanitize_text_field( $value );
  2850 				return sanitize_text_field( $value );
  2796 
  2851 
  2797 			case 'uri':
  2852 			case 'uri':
  2798 				return esc_url_raw( $value );
  2853 				return sanitize_url( $value );
  2799 
  2854 
  2800 			case 'ip':
  2855 			case 'ip':
  2801 				return sanitize_text_field( $value );
  2856 				return sanitize_text_field( $value );
  2802 
  2857 
  2803 			case 'uuid':
  2858 			case 'uuid':
  2827  * @param array  $memo Reduce accumulator.
  2882  * @param array  $memo Reduce accumulator.
  2828  * @param string $path REST API path to preload.
  2883  * @param string $path REST API path to preload.
  2829  * @return array Modified reduce accumulator.
  2884  * @return array Modified reduce accumulator.
  2830  */
  2885  */
  2831 function rest_preload_api_request( $memo, $path ) {
  2886 function rest_preload_api_request( $memo, $path ) {
  2832 	// array_reduce() doesn't support passing an array in PHP 5.2,
  2887 	/*
  2833 	// so we need to make sure we start with one.
  2888 	 * array_reduce() doesn't support passing an array in PHP 5.2,
       
  2889 	 * so we need to make sure we start with one.
       
  2890 	 */
  2834 	if ( ! is_array( $memo ) ) {
  2891 	if ( ! is_array( $memo ) ) {
  2835 		$memo = array();
  2892 		$memo = array();
  2836 	}
  2893 	}
  2837 
  2894 
  2838 	if ( empty( $path ) ) {
  2895 	if ( empty( $path ) ) {
  2868 	$response = rest_do_request( $request );
  2925 	$response = rest_do_request( $request );
  2869 	if ( 200 === $response->status ) {
  2926 	if ( 200 === $response->status ) {
  2870 		$server = rest_get_server();
  2927 		$server = rest_get_server();
  2871 		/** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
  2928 		/** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
  2872 		$response = apply_filters( 'rest_post_dispatch', rest_ensure_response( $response ), $server, $request );
  2929 		$response = apply_filters( 'rest_post_dispatch', rest_ensure_response( $response ), $server, $request );
  2873 		$embed  = $request->has_param( '_embed' ) ? rest_parse_embed_param( $request['_embed'] ) : false;
  2930 		$embed    = $request->has_param( '_embed' ) ? rest_parse_embed_param( $request['_embed'] ) : false;
  2874 		$data   = (array) $server->response_to_data( $response, $embed );
  2931 		$data     = (array) $server->response_to_data( $response, $embed );
  2875 
  2932 
  2876 		if ( 'OPTIONS' === $method ) {
  2933 		if ( 'OPTIONS' === $method ) {
  2877 			$memo[ $method ][ $path ] = array(
  2934 			$memo[ $method ][ $path ] = array(
  2878 				'body'    => $data,
  2935 				'body'    => $data,
  2879 				'headers' => $response->headers,
  2936 				'headers' => $response->headers,
  2916  *
  2973  *
  2917  * @since 5.5.0
  2974  * @since 5.5.0
  2918  * @since 5.6.0 Support the "patternProperties" keyword for objects.
  2975  * @since 5.6.0 Support the "patternProperties" keyword for objects.
  2919  *              Support the "anyOf" and "oneOf" keywords.
  2976  *              Support the "anyOf" and "oneOf" keywords.
  2920  *
  2977  *
  2921  * @param array|object $data    The response data to modify.
  2978  * @param array|object $response_data The response data to modify.
  2922  * @param array        $schema  The schema for the endpoint used to filter the response.
  2979  * @param array        $schema        The schema for the endpoint used to filter the response.
  2923  * @param string       $context The requested context.
  2980  * @param string       $context       The requested context.
  2924  * @return array|object The filtered response data.
  2981  * @return array|object The filtered response data.
  2925  */
  2982  */
  2926 function rest_filter_response_by_context( $data, $schema, $context ) {
  2983 function rest_filter_response_by_context( $response_data, $schema, $context ) {
  2927 	if ( isset( $schema['anyOf'] ) ) {
  2984 	if ( isset( $schema['anyOf'] ) ) {
  2928 		$matching_schema = rest_find_any_matching_schema( $data, $schema, '' );
  2985 		$matching_schema = rest_find_any_matching_schema( $response_data, $schema, '' );
  2929 		if ( ! is_wp_error( $matching_schema ) ) {
  2986 		if ( ! is_wp_error( $matching_schema ) ) {
  2930 			if ( ! isset( $schema['type'] ) ) {
  2987 			if ( ! isset( $schema['type'] ) ) {
  2931 				$schema['type'] = $matching_schema['type'];
  2988 				$schema['type'] = $matching_schema['type'];
  2932 			}
  2989 			}
  2933 
  2990 
  2934 			$data = rest_filter_response_by_context( $data, $matching_schema, $context );
  2991 			$response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
  2935 		}
  2992 		}
  2936 	}
  2993 	}
  2937 
  2994 
  2938 	if ( isset( $schema['oneOf'] ) ) {
  2995 	if ( isset( $schema['oneOf'] ) ) {
  2939 		$matching_schema = rest_find_one_matching_schema( $data, $schema, '', true );
  2996 		$matching_schema = rest_find_one_matching_schema( $response_data, $schema, '', true );
  2940 		if ( ! is_wp_error( $matching_schema ) ) {
  2997 		if ( ! is_wp_error( $matching_schema ) ) {
  2941 			if ( ! isset( $schema['type'] ) ) {
  2998 			if ( ! isset( $schema['type'] ) ) {
  2942 				$schema['type'] = $matching_schema['type'];
  2999 				$schema['type'] = $matching_schema['type'];
  2943 			}
  3000 			}
  2944 
  3001 
  2945 			$data = rest_filter_response_by_context( $data, $matching_schema, $context );
  3002 			$response_data = rest_filter_response_by_context( $response_data, $matching_schema, $context );
  2946 		}
  3003 		}
  2947 	}
  3004 	}
  2948 
  3005 
  2949 	if ( ! is_array( $data ) && ! is_object( $data ) ) {
  3006 	if ( ! is_array( $response_data ) && ! is_object( $response_data ) ) {
  2950 		return $data;
  3007 		return $response_data;
  2951 	}
  3008 	}
  2952 
  3009 
  2953 	if ( isset( $schema['type'] ) ) {
  3010 	if ( isset( $schema['type'] ) ) {
  2954 		$type = $schema['type'];
  3011 		$type = $schema['type'];
  2955 	} elseif ( isset( $schema['properties'] ) ) {
  3012 	} elseif ( isset( $schema['properties'] ) ) {
  2956 		$type = 'object'; // Back compat if a developer accidentally omitted the type.
  3013 		$type = 'object'; // Back compat if a developer accidentally omitted the type.
  2957 	} else {
  3014 	} else {
  2958 		return $data;
  3015 		return $response_data;
  2959 	}
  3016 	}
  2960 
  3017 
  2961 	$is_array_type  = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) );
  3018 	$is_array_type  = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) );
  2962 	$is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) );
  3019 	$is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) );
  2963 
  3020 
  2964 	if ( $is_array_type && $is_object_type ) {
  3021 	if ( $is_array_type && $is_object_type ) {
  2965 		if ( rest_is_array( $data ) ) {
  3022 		if ( rest_is_array( $response_data ) ) {
  2966 			$is_object_type = false;
  3023 			$is_object_type = false;
  2967 		} else {
  3024 		} else {
  2968 			$is_array_type = false;
  3025 			$is_array_type = false;
  2969 		}
  3026 		}
  2970 	}
  3027 	}
  2971 
  3028 
  2972 	$has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] );
  3029 	$has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] );
  2973 
  3030 
  2974 	foreach ( $data as $key => $value ) {
  3031 	foreach ( $response_data as $key => $value ) {
  2975 		$check = array();
  3032 		$check = array();
  2976 
  3033 
  2977 		if ( $is_array_type ) {
  3034 		if ( $is_array_type ) {
  2978 			$check = isset( $schema['items'] ) ? $schema['items'] : array();
  3035 			$check = isset( $schema['items'] ) ? $schema['items'] : array();
  2979 		} elseif ( $is_object_type ) {
  3036 		} elseif ( $is_object_type ) {
  2994 		}
  3051 		}
  2995 
  3052 
  2996 		if ( ! in_array( $context, $check['context'], true ) ) {
  3053 		if ( ! in_array( $context, $check['context'], true ) ) {
  2997 			if ( $is_array_type ) {
  3054 			if ( $is_array_type ) {
  2998 				// All array items share schema, so there's no need to check each one.
  3055 				// All array items share schema, so there's no need to check each one.
  2999 				$data = array();
  3056 				$response_data = array();
  3000 				break;
  3057 				break;
  3001 			}
  3058 			}
  3002 
  3059 
  3003 			if ( is_object( $data ) ) {
  3060 			if ( is_object( $response_data ) ) {
  3004 				unset( $data->$key );
  3061 				unset( $response_data->$key );
  3005 			} else {
  3062 			} else {
  3006 				unset( $data[ $key ] );
  3063 				unset( $response_data[ $key ] );
  3007 			}
  3064 			}
  3008 		} elseif ( is_array( $value ) || is_object( $value ) ) {
  3065 		} elseif ( is_array( $value ) || is_object( $value ) ) {
  3009 			$new_value = rest_filter_response_by_context( $value, $check, $context );
  3066 			$new_value = rest_filter_response_by_context( $value, $check, $context );
  3010 
  3067 
  3011 			if ( is_object( $data ) ) {
  3068 			if ( is_object( $response_data ) ) {
  3012 				$data->$key = $new_value;
  3069 				$response_data->$key = $new_value;
  3013 			} else {
  3070 			} else {
  3014 				$data[ $key ] = $new_value;
  3071 				$response_data[ $key ] = $new_value;
  3015 			}
  3072 			}
  3016 		}
  3073 		}
  3017 	}
  3074 	}
  3018 
  3075 
  3019 	return $data;
  3076 	return $response_data;
  3020 }
  3077 }
  3021 
  3078 
  3022 /**
  3079 /**
  3023  * Sets the "additionalProperties" to false by default for all object definitions in the schema.
  3080  * Sets the "additionalProperties" to false by default for all object definitions in the schema.
  3024  *
  3081  *
  3289 
  3346 
  3290 /**
  3347 /**
  3291  * Converts an error to a response object.
  3348  * Converts an error to a response object.
  3292  *
  3349  *
  3293  * This iterates over all error codes and messages to change it into a flat
  3350  * This iterates over all error codes and messages to change it into a flat
  3294  * array. This enables simpler client behaviour, as it is represented as a
  3351  * array. This enables simpler client behavior, as it is represented as a
  3295  * list in JSON rather than an object/map.
  3352  * list in JSON rather than an object/map.
  3296  *
  3353  *
  3297  * @since 5.7.0
  3354  * @since 5.7.0
  3298  *
  3355  *
  3299  * @param WP_Error $error WP_Error instance.
  3356  * @param WP_Error $error WP_Error instance.
  3337 		$data['additional_errors'] = $errors;
  3394 		$data['additional_errors'] = $errors;
  3338 	}
  3395 	}
  3339 
  3396 
  3340 	return new WP_REST_Response( $data, $status );
  3397 	return new WP_REST_Response( $data, $status );
  3341 }
  3398 }
       
  3399 
       
  3400 /**
       
  3401  * Checks whether a REST API endpoint request is currently being handled.
       
  3402  *
       
  3403  * This may be a standalone REST API request, or an internal request dispatched from within a regular page load.
       
  3404  *
       
  3405  * @since 6.5.0
       
  3406  *
       
  3407  * @global WP_REST_Server $wp_rest_server REST server instance.
       
  3408  *
       
  3409  * @return bool True if a REST endpoint request is currently being handled, false otherwise.
       
  3410  */
       
  3411 function wp_is_rest_endpoint() {
       
  3412 	/* @var WP_REST_Server $wp_rest_server */
       
  3413 	global $wp_rest_server;
       
  3414 
       
  3415 	// Check whether this is a standalone REST request.
       
  3416 	$is_rest_endpoint = wp_is_serving_rest_request();
       
  3417 	if ( ! $is_rest_endpoint ) {
       
  3418 		// Otherwise, check whether an internal REST request is currently being handled.
       
  3419 		$is_rest_endpoint = isset( $wp_rest_server )
       
  3420 			&& $wp_rest_server->is_dispatching();
       
  3421 	}
       
  3422 
       
  3423 	/**
       
  3424 	 * Filters whether a REST endpoint request is currently being handled.
       
  3425 	 *
       
  3426 	 * This may be a standalone REST API request, or an internal request dispatched from within a regular page load.
       
  3427 	 *
       
  3428 	 * @since 6.5.0
       
  3429 	 *
       
  3430 	 * @param bool $is_request_endpoint Whether a REST endpoint request is currently being handled.
       
  3431 	 */
       
  3432 	return (bool) apply_filters( 'wp_is_rest_endpoint', $is_rest_endpoint );
       
  3433 }