: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* REST API: WP_REST_Server class
* Core class used to implement the WordPress REST API server.
#[AllowDynamicProperties]
* Alias for GET transport method.
* Alias for POST transport method.
const CREATABLE = 'POST';
* Alias for POST, PUT, PATCH transport methods together.
const EDITABLE = 'POST, PUT, PATCH';
* Alias for DELETE transport method.
const DELETABLE = 'DELETE';
* Alias for GET, POST, PUT, PATCH & DELETE transport methods together.
const ALLMETHODS = 'GET, POST, PUT, PATCH, DELETE';
* Namespaces registered to the server.
protected $namespaces = array();
* Endpoints registered to the server.
protected $endpoints = array();
* Options defined for the routes.
protected $route_options = array();
* Caches embedded requests.
protected $embed_cache = array();
* Stores request objects that are currently being handled.
protected $dispatching_requests = array();
* Instantiates the REST server.
public function __construct() {
$this->endpoints = array(
'callback' => array( $this, 'get_index' ),
'callback' => array( $this, 'serve_batch_request_v1' ),
'enum' => array( 'require-all-validate', 'normal' ),
'maxItems' => $this->get_max_batch_size(),
'enum' => array( 'POST', 'PUT', 'PATCH', 'DELETE' ),
'additionalProperties' => true,
'additionalProperties' => array(
'type' => array( 'string', 'array' ),
* Checks the authentication headers if supplied.
* @return WP_Error|null|true WP_Error indicates unsuccessful login, null indicates successful
* or no authentication provided
public function check_authentication() {
* Filters REST API authentication errors.
* This is used to pass a WP_Error from an authentication method back to
* Authentication methods should check first if they're being used, as
* multiple authentication methods can be enabled on a site (cookies,
* HTTP basic auth, OAuth). If the authentication method hooked in is
* not actually being attempted, null should be returned to indicate
* another authentication method should check instead. Similarly,
* callbacks should ensure the value is `null` before checking for
* A WP_Error instance can be returned if an error occurs, and this should
* match the format used by API methods internally (that is, the `status`
* data should be used). A callback can return `true` to indicate that
* the authentication method was used, and it succeeded.
* @param WP_Error|null|true $errors WP_Error if authentication error, null if authentication
* method wasn't used, true if authentication succeeded.
return apply_filters( 'rest_authentication_errors', null );
* Converts an error to a response object.
* This iterates over all error codes and messages to change it into a flat
* array. This enables simpler client behavior, as it is represented as a
* list in JSON rather than an object/map.
* @since 5.7.0 Converted to a wrapper of {@see rest_convert_error_to_response()}.
* @param WP_Error $error WP_Error instance.
* @return WP_REST_Response List of associative arrays with code and message keys.
protected function error_to_response( $error ) {
return rest_convert_error_to_response( $error );
* Retrieves an appropriate error representation in JSON.
* Note: This should only be used in WP_REST_Server::serve_request(), as it
* cannot handle WP_Error internally. All callbacks and other internal methods
* should instead return a WP_Error with the data set to an array that includes
* a 'status' key, with the value being the HTTP status to send.
* @param string $code WP_Error-style code.
* @param string $message Human-readable message.
* @param int $status Optional. HTTP status code to send. Default null.
* @return string JSON representation of the error
protected function json_error( $code, $message, $status = null ) {
$this->set_status( $status );
$error = compact( 'code', 'message' );
return wp_json_encode( $error );
* Gets the encoding options passed to {@see wp_json_encode}.
* @param \WP_REST_Request $request The current request object.
* @return int The JSON encode options.
protected function get_json_encode_options( WP_REST_Request $request ) {
if ( $request->has_param( '_pretty' ) ) {
$options |= JSON_PRETTY_PRINT;
* Filters the JSON encoding options used to send the REST API response.
* @param int $options JSON encoding options {@see json_encode()}.
* @param WP_REST_Request $request Current request object.
return apply_filters( 'rest_json_encode_options', $options, $request );
* Handles serving a REST API request.
* Matches the current server URI to a route and runs the first matching
* callback then outputs a JSON representation of the returned value.
* @see WP_REST_Server::dispatch()
* @global WP_User $current_user The currently authenticated user.
* @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
* @return null|false Null if not served and a HEAD request, false otherwise.
public function serve_request( $path = null ) {
/* @var WP_User|null $current_user */
if ( $current_user instanceof WP_User && ! $current_user->exists() ) {
* If there is no current user authenticated via other means, clear
* the cached lack of user, so that an authenticate check can set it
* This is done because for authentications such as Application
* Passwords, we don't want it to be accepted unless the current HTTP
* request is a REST API request, which can't always be identified early
* Filters whether JSONP is enabled for the REST API.
* @param bool $jsonp_enabled Whether JSONP is enabled. Default true.
$jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
if ( isset( $_GET['_jsonp'] ) ) {
$jsonp_callback = $_GET['_jsonp'];
$content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json';
$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
$this->send_header( 'X-Robots-Tag', 'noindex' );
$api_root = get_rest_url();
if ( ! empty( $api_root ) ) {
$this->send_header( 'Link', '<' . sanitize_url( $api_root ) . '>; rel="https://api.w.org/"' );
* Mitigate possible JSONP Flash attacks.
* https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
$this->send_header( 'X-Content-Type-Options', 'nosniff' );
* Filters whether the REST API is enabled.
* @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to
* restrict access to the REST API.
* @param bool $rest_enabled Whether the REST API is enabled. Default true.
apply_filters_deprecated(
'rest_authentication_errors',
/* translators: %s: rest_authentication_errors */
__( 'The REST API can no longer be completely disabled, the %s filter can be used to restrict access to the API, instead.' ),
'rest_authentication_errors'
if ( ! $jsonp_enabled ) {
echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 );
if ( isset( $_SERVER['PATH_INFO'] ) ) {
$path = $_SERVER['PATH_INFO'];
$request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path );
$request->set_query_params( wp_unslash( $_GET ) );
$request->set_body_params( wp_unslash( $_POST ) );
$request->set_file_params( $_FILES );
$request->set_headers( $this->get_headers( wp_unslash( $_SERVER ) ) );
$request->set_body( self::get_raw_data() );
* HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check
* $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE
$method_overridden = false;
if ( isset( $_GET['_method'] ) ) {
$request->set_method( $_GET['_method'] );
} elseif ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) {
$request->set_method( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] );
$method_overridden = true;
$expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' );
* Filters the list of response headers that are exposed to REST API CORS requests.
* @since 6.3.0 The `$request` parameter was added.
* @param string[] $expose_headers The list of response headers to expose.
* @param WP_REST_Request $request The request in context.
$expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers, $request );
$this->send_header( 'Access-Control-Expose-Headers', implode( ', ', $expose_headers ) );
* Filters the list of request headers that are allowed for REST API CORS requests.
* The allowed headers are passed to the browser to specify which
* headers can be passed to the REST API. By default, we allow the
* Content-* headers needed to upload files to the media endpoints.
* As well as the Authorization and Nonce headers for allowing authentication.
* @since 6.3.0 The `$request` parameter was added.
* @param string[] $allow_headers The list of request headers to allow.
* @param WP_REST_Request $request The request in context.
$allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers, $request );
$this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) );
$result = $this->check_authentication();
if ( ! is_wp_error( $result ) ) {
$result = $this->dispatch( $request );
// Normalize to either WP_Error or WP_REST_Response...
$result = rest_ensure_response( $result );
// ...then convert WP_Error across.
if ( is_wp_error( $result ) ) {
$result = $this->error_to_response( $result );
* Filters the REST API response.
* Allows modification of the response before returning.
* @since 4.5.0 Applied to embedded responses.
* @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`.
* @param WP_REST_Server $server Server instance.
* @param WP_REST_Request $request Request used to generate the response.
$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );
// Wrap the response in an envelope if asked for.
if ( isset( $_GET['_envelope'] ) ) {
$embed = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false;
$result = $this->envelope_response( $result, $embed );
// Send extra data from response objects.
$headers = $result->get_headers();
$this->send_headers( $headers );
$code = $result->get_status();
$this->set_status( $code );
* Filters whether to send no-cache headers on a REST API request.
* @since 6.3.2 Moved the block to catch the filter added on rest_cookie_check_errors() from wp-includes/rest-api.php.
* @param bool $rest_send_nocache_headers Whether to send no-cache headers.
$send_no_cache_headers = apply_filters( 'rest_send_nocache_headers', is_user_logged_in() );
* Send no-cache headers if $send_no_cache_headers is true,
* OR if the HTTP_X_HTTP_METHOD_OVERRIDE is used but resulted a 4xx response code.
if ( $send_no_cache_headers || ( true === $method_overridden && str_starts_with( $code, '4' ) ) ) {
foreach ( wp_get_nocache_headers() as $header => $header_value ) {
if ( empty( $header_value ) ) {
$this->remove_header( $header );
$this->send_header( $header, $header_value );