logger = $logger; $this->regex = $regex; } /** * Check if the current user can manage options. */ public function can_user_manage_options(): bool { return \user_can( \get_current_user_id(), 'manage_options' ); } /** * Register GET endpoint. * * @param string $endpoint The endpoint. * @param string $fun The function. * @param mixed[] $args The arguments. See https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/ * @param string $permission_callback The permission callback. */ protected function register_get( $endpoint, $fun, $args = array(), $permission_callback = 'can_user_manage_options' ): void { $this->register( \WP_REST_Server::READABLE, $endpoint, $fun, $args, $permission_callback ); } /** * Register POST endpoint. * * @param string $endpoint The endpoint. * @param string $fun The function. * @param mixed[] $args The arguments. See https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/ * @param string $permission_callback The permission callback. */ protected function register_post( $endpoint, $fun, $args = array(), $permission_callback = 'can_user_manage_options' ): void { $this->register( \WP_REST_Server::CREATABLE, $endpoint, $fun, $args, $permission_callback ); } /** * Register DELETE endpoint. * * @param string $endpoint The endpoint. * @param string $fun The function. * @param mixed[] $args The arguments. See https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/ * @param string $permission_callback The permission callback. */ protected function register_delete( $endpoint, $fun, $args = array(), $permission_callback = 'can_user_manage_options' ): void { $this->register( \WP_REST_Server::DELETABLE, $endpoint, $fun, $args, $permission_callback ); } /** * Register endpoint. * * @param string $methods The HTTP Verb. * @param string $endpoint The endpoint. * @param string $fun The function. * @param mixed[] $arguments The arguments. See https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/ * @param string $permission_callback The permission callback. */ private function register( $methods, $endpoint, $fun, $arguments, $permission_callback ): void { $args = array( 'methods' => $methods, 'callback' => array( $this, $fun ), 'permission_callback' => array( $this, $permission_callback ), ); if ( ! empty( $arguments ) && is_array( $arguments ) ) { $args['args'] = $arguments; } \register_rest_route( $this->namespace, "{$this->rest_base}/$endpoint", $args ); } /** * Validate if the parameter is not empty string. * * @param mixed $param The parameter. * @param WP_REST_Request $request The request. * @param string $key The key. */ public function validate_not_empty_string( $param, $request, $key ): bool { return is_string( $param ) && '' !== trim( $param ); } /** * Validate if the parameter is a boolean. * * @param mixed $param The parameter. * @param WP_REST_Request $request The request. * @param string $key The key. */ public function validate_is_bool( $param, $request, $key ): bool { return is_bool( $param ); } /** * Validate if the parameter is an integer. * * @param mixed $param The parameter. * @param WP_REST_Request $request The request. * @param string $key The key. */ public function validate_is_int( $param, $request, $key ): bool { return is_int( $param ); } /** * Validate id the parameter is an array of IP addresses. * * @param mixed $param The parameter. * @param WP_REST_Request $request The request. * @param string $key The key. */ public function validate_ip_list( $param, $request, $key ): bool { $ip_regex = $this->regex->getIpRegex(); if ( ! is_array( $param ) ) { return false; } foreach ( $param as $ip ) { if ( preg_match( $ip_regex, $ip ) !== 1 ) { return false; } } return true; } /** * Check dates (yyyy-mm-dd) and time durations (PnYnMnDTnHnMnS). ISO 8061 regex to validate the date format. * * @param mixed $param The parameter. * @param WP_REST_Request $request The request. * @param string $key The key. */ public function validate_time_duration( $param, $request, $key ): bool { $regex = $this->regex->getDateOrDurationRegex(); return is_string( $param ) && preg_match( $regex, $param ) === 1 && 'P' !== $param && ! str_ends_with( $param, 'T' ); } /** * Sanitize boolean. * * @param mixed $param The parameter. */ public function sanitize_bool( $param ): bool { return (bool) $param; } /** * Sanitize boolean. * * @param mixed $param The parameter. */ public function sanitize_int( $param ): int { return intval( $param ); } /** * Sanitize an array strings. * * @param mixed[] $param The parameter. * @return mixed[] */ public function sanitize_array_sanitize_text_field( $param ): array { foreach ( $param as &$value ) { $value = \sanitize_text_field( strval( $value ) ); } return $param; } /** * Get base argument structure. * * @param bool $required If the argument is required. * @param mixed $default_value The default value. Null will be ignored. * @return mixed[] */ protected function get_arg( $required = true, $default_value = null ): array { $arg = array( 'required' => $required ); if ( null !== $default_value ) { $arg['default'] = $default_value; } return $arg; } /** * Get argument structure for a boolean parameter. * * @param bool $required If the argument is required. * @param mixed $default_value The default value. Null will be ignored. * @return mixed[] */ protected function get_arg_bool( $required = true, $default_value = null ): array { return array_merge( $this->get_arg( $required, $default_value ), array( 'validate_callback' => array( $this, 'validate_is_bool' ), 'sanitize_callback' => array( $this, 'sanitize_bool' ), ) ); } /** * Get argument structure for a integer parameter. * * @param bool $required If the argument is required. * @param mixed $default_value The default value. Null will be ignored. * @return mixed[] */ protected function get_arg_int( $required = true, $default_value = null ): array { return array_merge( $this->get_arg( $required, $default_value ), array( 'validate_callback' => array( $this, 'validate_is_int' ), 'sanitize_callback' => array( $this, 'sanitize_int' ), ) ); } /** * Get argument structure for a boolean parameter. * * @param bool $required If the argument is required. * @param mixed $default_value The default value. Null will be ignored. * @param callable $validate The validate callback. Leave null to use the default. * @param callable $sanitize The sanitize callback. Leave null to use the default. * @return mixed[] */ protected function get_arg_string( $required = true, $default_value = null, $validate = null, $sanitize = null ): array { return array_merge( $this->get_arg( $required, $default_value ), array( 'validate_callback' => null === $validate ? array( $this, 'validate_not_empty_string' ) : $validate, 'sanitize_callback' => null === $sanitize ? 'sanitize_text_field' : $sanitize, ) ); } /** * Get argument structure for an IP list. * * @param bool $required If the argument is required. * @param mixed $default_value The default value. Null will be ignored. * @param callable $validate The validate callback. Leave null to use the default. * @param callable $sanitize The sanitize callback. Leave null to use the default. * @return mixed[] */ protected function get_arg_ip_list( $required = true, $default_value = null, $validate = null, $sanitize = null ): array { return array_merge( $this->get_arg( $required, $default_value ), array( 'validate_callback' => null === $validate ? array( $this, 'validate_ip_list' ) : $validate, 'sanitize_callback' => null === $sanitize ? array( $this, 'sanitize_array_sanitize_text_field' ) : $sanitize, ) ); } /** * Return a valid pattern to be used in a URL to match a parameter. * * @param string $param_name The URL parameter name. * @param string $type The type of the parameter. Default is 'string'. * Supported values are: * - 'string'. */ protected function url_param_pattern( $param_name, $type = 'string' ): string { switch ( $type ) { case 'string': return '(?P<' . $param_name . '>[\w]+)'; default: return ''; } } /** * Build a valid response for the REST API * * @return WP_REST_Response|WP_Error */ protected function build_response( Response $response ) { return $this->build_response_from_array( $response->toArray(), $response->isSuccessful() ); } /** * Build a valid response for the REST API from an array * * @return WP_REST_Response|WP_Error */ protected function build_response_from_array( array $response_array, bool $is_successful ) { if ( ! $is_successful ) { $code = strval( $response_array['statusCode'] ?? '500' ); $message = strval( $response_array['errorMessage'] ?? 'Unknown error' ); return new WP_Error( $code, $message ); } return \rest_ensure_response( $response_array ); } }