: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* REST API: WP_REST_Controller class
* Core base controller for managing and interacting with REST API items.
#[AllowDynamicProperties]
abstract class WP_REST_Controller {
* The namespace of this controller's route.
* The base of this controller's route.
* Cached results of get_item_schema.
* Registers the routes for the objects of the controller.
* @see register_rest_route()
public function register_routes() {
'WP_REST_Controller::register_routes',
/* translators: %s: register_routes() */
sprintf( __( "Method '%s' must be overridden." ), __METHOD__ ),
* Checks if a given request has access to get items.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
public function get_items_permissions_check( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Retrieves a collection of items.
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function get_items( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Checks if a given request has access to get a specific item.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
public function get_item_permissions_check( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Retrieves one item from the collection.
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function get_item( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Checks if a given request has access to create items.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
public function create_item_permissions_check( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Creates one item from the collection.
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function create_item( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Checks if a given request has access to update a specific item.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
public function update_item_permissions_check( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Updates one item from the collection.
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function update_item( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Checks if a given request has access to delete a specific item.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
public function delete_item_permissions_check( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Deletes one item from the collection.
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function delete_item( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Prepares one item for create or update operation.
* @param WP_REST_Request $request Request object.
* @return object|WP_Error The prepared item, or WP_Error object on failure.
protected function prepare_item_for_database( $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Prepares the item for the REST response.
* @param mixed $item WordPress representation of the item.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
public function prepare_item_for_response( $item, $request ) {
/* translators: %s: Method name. */
sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
* Prepares a response for insertion into a collection.
* @param WP_REST_Response $response Response object.
* @return array|mixed Response data, ready for insertion into collection data.
public function prepare_response_for_collection( $response ) {
if ( ! ( $response instanceof WP_REST_Response ) ) {
$data = (array) $response->get_data();
$server = rest_get_server();
$links = $server::get_compact_response_links( $response );
if ( ! empty( $links ) ) {
$data['_links'] = $links;
* Filters a response based on the context defined in the schema.
* @param array $response_data Response data to filter.
* @param string $context Context defined in the schema.
* @return array Filtered response.
public function filter_response_by_context( $response_data, $context ) {
$schema = $this->get_item_schema();
return rest_filter_response_by_context( $response_data, $schema, $context );
* Retrieves the item's schema, conforming to JSON Schema.
* @return array Item schema data.
public function get_item_schema() {
return $this->add_additional_fields_schema( array() );
* Retrieves the item's schema for display / public consumption purposes.
* @return array Public item schema data.
public function get_public_item_schema() {
$schema = $this->get_item_schema();
if ( ! empty( $schema['properties'] ) ) {
foreach ( $schema['properties'] as &$property ) {
unset( $property['arg_options'] );
* Retrieves the query params for the collections.
* @return array Query parameters for the collection.
public function get_collection_params() {
'context' => $this->get_context_param(),
'description' => __( 'Current page of the collection.' ),
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
'description' => __( 'Maximum number of items to be returned in result set.' ),
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
'description' => __( 'Limit results to those matching a string.' ),
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
* Retrieves the magical context param.
* Ensures consistent descriptions between endpoints, and populates enum from schema.
* @param array $args Optional. Additional arguments for context parameter. Default empty array.
* @return array Context parameter details.
public function get_context_param( $args = array() ) {
'description' => __( 'Scope under which the request is made; determines fields present in response.' ),
'sanitize_callback' => 'sanitize_key',
'validate_callback' => 'rest_validate_request_arg',
$schema = $this->get_item_schema();
if ( empty( $schema['properties'] ) ) {
return array_merge( $param_details, $args );
foreach ( $schema['properties'] as $attributes ) {
if ( ! empty( $attributes['context'] ) ) {
$contexts = array_merge( $contexts, $attributes['context'] );
if ( ! empty( $contexts ) ) {
$param_details['enum'] = array_unique( $contexts );
rsort( $param_details['enum'] );
return array_merge( $param_details, $args );
* Adds the values from additional fields to a data object.
* @param array $response_data Prepared response array.
* @param WP_REST_Request $request Full details about the request.
* @return array Modified data object with additional fields.
protected function add_additional_fields_to_object( $response_data, $request ) {
$additional_fields = $this->get_additional_fields();
$requested_fields = $this->get_fields_for_response( $request );
foreach ( $additional_fields as $field_name => $field_options ) {
if ( ! $field_options['get_callback'] ) {
if ( ! rest_is_field_included( $field_name, $requested_fields ) ) {
$response_data[ $field_name ] = call_user_func(
$field_options['get_callback'],
* Updates the values of additional fields added to a data object.
* @param object $data_object Data model like WP_Term or WP_Post.
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True on success, WP_Error object if a field cannot be updated.
protected function update_additional_fields_for_object( $data_object, $request ) {
$additional_fields = $this->get_additional_fields();
foreach ( $additional_fields as $field_name => $field_options ) {
if ( ! $field_options['update_callback'] ) {
// Don't run the update callbacks if the data wasn't passed in the request.
if ( ! isset( $request[ $field_name ] ) ) {
$result = call_user_func(
$field_options['update_callback'],
if ( is_wp_error( $result ) ) {
* Adds the schema from additional fields to a schema array.
* The type of object is inferred from the passed schema.
* @param array $schema Schema array.
* @return array Modified Schema array.
protected function add_additional_fields_schema( $schema ) {
if ( empty( $schema['title'] ) ) {
// Can't use $this->get_object_type otherwise we cause an inf loop.