/** * REST API: WP_REST_Server class * * @package WordPress * @subpackage REST_API * @since 4.4.0 */ /** * Core class used to implement the WordPress REST API server. * * @since 4.4.0 */ #[AllowDynamicProperties] class WP_REST_Server { /** * Alias for GET transport method. * * @since 4.4.0 * @var string */ const READABLE = 'GET'; /** * Alias for POST transport method. * * @since 4.4.0 * @var string */ const CREATABLE = 'POST'; /** * Alias for POST, PUT, PATCH transport methods together. * * @since 4.4.0 * @var string */ const EDITABLE = 'POST, PUT, PATCH'; /** * Alias for DELETE transport method. * * @since 4.4.0 * @var string */ const DELETABLE = 'DELETE'; /** * Alias for GET, POST, PUT, PATCH & DELETE transport methods together. * * @since 4.4.0 * @var string */ const ALLMETHODS = 'GET, POST, PUT, PATCH, DELETE'; /** * Namespaces registered to the server. * * @since 4.4.0 * @var array */ protected $namespaces = array(); /** * Endpoints registered to the server. * * @since 4.4.0 * @var array */ protected $endpoints = array(); /** * Options defined for the routes. * * @since 4.4.0 * @var array */ protected $route_options = array(); /** * Caches embedded requests. * * @since 5.4.0 * @var array */ protected $embed_cache = array(); /** * Stores request objects that are currently being handled. * * @since 6.5.0 * @var array */ protected $dispatching_requests = array(); /** * Instantiates the REST server. * * @since 4.4.0 */ public function __construct() { $this->endpoints = array( // Meta endpoints. '/' => array( 'callback' => array( $this, 'get_index' ), 'methods' => 'GET', 'args' => array( 'context' => array( 'default' => 'view', ), ), ), '/batch/v1' => array( 'callback' => array( $this, 'serve_batch_request_v1' ), 'methods' => 'POST', 'args' => array( 'validation' => array( 'type' => 'string', 'enum' => array( 'require-all-validate', 'normal' ), 'default' => 'normal', ), 'requests' => array( 'required' => true, 'type' => 'array', 'maxItems' => $this->get_max_batch_size(), 'items' => array( 'type' => 'object', 'properties' => array( 'method' => array( 'type' => 'string', 'enum' => array( 'POST', 'PUT', 'PATCH', 'DELETE' ), 'default' => 'POST', ), 'path' => array( 'type' => 'string', 'required' => true, ), 'body' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => true, ), 'headers' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => array( 'type' => array( 'string', 'array' ), 'items' => array( 'type' => 'string', ), ), ), ), ), ), ), ), ); } /** * Checks the authentication headers if supplied. * * @since 4.4.0 * * @return WP_Error|null|true WP_Error if authentication error occurred, null if authentication * method wasn't used, true if authentication succeeded. */ public function check_authentication() { /** * Filters REST API authentication errors. * * This is used to pass a WP_Error from an authentication method back to * the API. * * 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 * errors. * * 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. * * @since 4.4.0 * * @param WP_Error|null|true $errors WP_Error if authentication error occurred, 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 4.4.0 * @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. * * @since 4.4.0 * * @param string $code WP_Error-style code. * @param string $message Human-readable message. * @param int|null $status Optional. HTTP status code to send. Default null. * @return string JSON representation of the error. */ protected function json_error( $code, $message, $status = null ) { if ( $status ) { $this->set_status( $status ); } $error = compact( 'code', 'message' ); return wp_json_encode( $error ); } /** * Gets the encoding options passed to {@see wp_json_encode}. * * @since 6.1.0 * * @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 ) { $options = 0; if ( $request->has_param( '_pretty' ) ) { $options |= JSON_PRETTY_PRINT; } /** * Filters the JSON encoding options used to send the REST API response. * * @since 6.1.0 * * @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. * * @since 4.4.0 * * @see WP_REST_Server::dispatch() * * @global WP_User $current_user The currently authenticated user. * * @param string|null $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. * Default null. * @return null|false Null if not served and a HEAD request, false otherwise. */ public function serve_request( $path = null ) { // Refuse to start a fresh top-level REST cycle while another dispatch // is already in flight. Internal sub-requests must use dispatch(). if ( $this->is_dispatching() ) { return false; } /* @var WP_User|null $current_user */ global $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 * properly. * * 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 * enough in evaluation. */ $current_user = null; } /** * Filters whether JSONP is enabled for the REST API. * * @since 4.4.0 * * @param bool $jsonp_enabled Whether JSONP is enabled. Default true. */ $jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true ); $jsonp_callback = false; 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. * * @since 4.4.0 * @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_enabled', array( true ), '4.7.0', 'rest_authentication_errors', sprintf( /* 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_callback ) { if ( ! $jsonp_enabled ) { echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 ); return false; } if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) { echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 ); return false; } } if ( empty( $path ) ) { $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 * header. */ $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 5.5.0 * @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 ) ); $allow_headers = array( 'Authorization', 'X-WP-Nonce', 'Content-Disposition', 'Content-MD5', 'Content-Type', ); /** * 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 5.5.0 * @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.4.0 * @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 4.4.0 * @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 ); } else { $this->send_header( $header, $header_value ); } } } /** * Filters whether the REST API request has already been served. * * Allow sending the request manually - by returning true, the API result * will not be sent to the client. * * @since 4.4.0 * * @param bool $served Whether the request has already been served. Default false. * @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`. * @param WP_REST_Request $request Request used to generate the response. * @param WP_REST_Server $server Server instance. */ $served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this ); if ( ! $served ) { if ( 'HEAD' === $request->get_method() ) { return null; } // Embed links inside the request. $embed = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false; $result = $this->response_to_data( $result, $embed ); /** * Filters the REST API response. * * Allows modification of the response data after inserting * embedded data (if any) and before echoing the response data. * * @since 4.8.1 * * @param array $result Response data to send to the client. * @param WP_REST_Server $server Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters( 'rest_pre_echo_response', $result, $this, $request ); // The 204 response shouldn't have a body. if ( 204 === $code || null === $result ) { return null; } $result = wp_json_encode( $result, $this->get_json_encode_options( $request ) ); $json_error_message = $this->get_json_last_error(); if ( $json_error_message ) { $this->set_status( 500 ); $json_error_obj = new WP_Error( 'rest_encode_error', $json_error_message, array( 'status' => 500 ) ); $result = $this->error_to_response( $json_error_obj ); $result = wp_json_encode( $result->data, $this->get_json_encode_options( $request ) ); } if ( $jsonp_callback ) { // Prepend '/**/' to mitigate possible JSONP Flash attacks. // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ echo '/**/' . $jsonp_callback . '(' . $result . ')'; } else { echo $result; } } return null; } /** * Converts a response to data to send. * * @since 4.4.0 * @since 5.4.0 The `$embed` parameter can now contain a list of link relations to include. * * @param WP_REST_Response $response Response object. * @param bool|string[] $embed Whether to embed all links, a filtered list of link relations, or no links. * @return array { * Data with sub-requests embedded. * * @type array $_links Links. * @type array $_embedded Embedded objects. * } */ public function response_to_data( $response, $embed ) { $data = $response->get_data(); $links = self::get_compact_response_links( $response ); if ( ! empty( $links ) ) { // Convert links to part of the data. $data['_links'] = $links; } if ( $embed ) { $this->embed_cache = array(); // Determine if this is a numeric array. if ( wp_is_numeric_array( $data ) ) { foreach ( $data as $key => $item ) { $data[ $key ] = $this->embed_links( $item, $embed ); } } else { $data = $this->embed_links( $data, $embed ); } $this->embed_cache = array(); } return $data; } /** * Retrieves links from a response. * * Extracts the links from a response into a structured hash, suitable for * direct output. * * @since 4.4.0 * * @param WP_REST_Response $response Response to extract links from. * @return array Map of link relation to list of link hashes. */ public static function get_response_links( $response ) { $links = $response->get_links(); if ( empty( $links ) ) { return array(); } // Convert links to part of the data. $data = array(); foreach ( $links as $rel => $items ) { $data[ $rel ] = array(); foreach ( $items as $item ) { $attributes = $item['attributes']; $attributes['href'] = $item['href']; if ( 'self' !== $rel ) { $data[ $rel ][] = $attributes; continue; } $target_hints = self::get_target_hints_for_link( $attributes ); if ( $target_hints ) { $attributes['targetHints'] = $target_hints; } $data[ $rel ][] = $attributes; } } return $data; } /** * Gets the target hints for a REST API Link. * * @since 6.7.0 * * @param array $link The link to get target hints for. * @return array|null */ protected static function get_target_hints_for_link( $link ) { // Prefer targetHints that were specifically designated by the developer. if ( isset( $link['targetHints']['allow'] ) ) { return null; } $request = WP_REST_Request::from_url( $link['href'] ); if ( ! $request ) { return null; } $server = rest_get_server(); $match = $server->match_request_to_handler( $request ); if ( is_wp_error( $match ) ) { return null; } if ( is_wp_error( $request->has_valid_params() ) ) { return null; } if ( is_wp_error( $request->sanitize_params() ) ) { return null; } $target_hints = array(); $response = new WP_REST_Response(); $response->set_matched_route( $match[0] ); $response->set_matched_handler( $match[1] ); $headers = rest_send_allow_header( $response, $server, $request )->get_headers(); foreach ( $headers as $name => $value ) { $name = WP_REST_Request::canonicalize_header_name( $name ); $target_hints[ $name ] = array_map( 'trim', explode( ',', $value ) ); } return $target_hints; } /** * Retrieves the CURIEs (compact URIs) used for relations. * * Extracts the links from a response into a structured hash, suitable for * direct output. * * @since 4.5.0 * * @param WP_REST_Response $response Response to extract links from. * @return array Map of link relation to list of link hashes. */ public static function get_compact_response_links( $response ) { $links = self::get_response_links( $response ); if ( empty( $links ) ) { return array(); } $curies = $response->get_curies(); $used_curies = array(); foreach ( $links as $rel => $items ) { // Convert $rel URIs to their compact versions if they exist. foreach ( $curies as $curie ) { $href_prefix = substr( $curie['href'], 0, strpos( $curie['href'], '{rel}' ) ); if ( ! str_starts_with( $rel, $href_prefix ) ) { continue; } // Relation now changes from '$uri' to '$curie:$relation'. $rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) ); preg_match( '!' . $rel_regex . '!', $rel, $matches ); if ( $matches ) { $new_rel = $curie['name'] . ':' . $matches[1]; $used_curies[ $curie['name'] ] = $curie; $links[ $new_rel ] = $items; unset( $links[ $rel ] ); break; } } } // Push the curies onto the start of the links array. if ( $used_curies ) { $links['curies'] = array_values( $used_curies ); } return $links; } /** * Embeds the links from the data into the request. * * @since 4.4.0 * @since 5.4.0 The `$embed` parameter can now contain a list of link relations to include. * * @param array $data Data from the request. * @param bool|string[] $embed Whether to embed all links or a filtered list of link relations. * Default true. * @return array { * Data with sub-requests embedded. * * @type array $_links Links. * @type array $_embedded Embedded objects. * } */ protected function embed_links( $data, $embed = true ) { if ( empty( $data['_links'] ) ) { return $data; } $embedded = array(); foreach ( $data['_links'] as $rel => $links ) { /* * If a list of relations was specified, and the link relation * is not in the list of allowed relations, don't process the link. */ if ( is_array( $embed ) && ! in_array( $rel, $embed, true ) ) { continue; } $embeds = array(); foreach ( $links as $item ) { // Determine if the link is embeddable. if ( empty( $item['embeddable'] ) ) { // Ensure we keep the same order. $embeds[] = array(); continue; } if ( ! array_key_exists( $item['href'], $this->embed_cache ) ) { // Run through our internal routing and serve. $request = WP_REST_Request::from_url( $item['href'] ); if ( ! $request ) { $embeds[] = array(); continue; } // Embedded resources get passed context=embed. if ( empty( $request['context'] ) ) { $request['context'] = 'embed'; } if ( empty( $request['per_page'] ) ) { $matched = $this->match_request_to_handler( $request ); if ( ! is_wp_error( $matched ) && isset( $matched[1]['args']['per_page']['maximum'] ) ) { $request['per_page'] = (int) $matched[1]['args']['per_page']['maximum']; } } $response = $this->dispatch( $request ); /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $response = apply_filters( 'rest_post_dispatch', rest_ensure_response( $response ), $this, $request ); $this->embed_cache[ $item['href'] ] = $this->response_to_data( $response, false ); } $embeds[] = $this->embed_cache[ $item['href'] ]; } // Determine if any real links were found. $has_links = count( array_filter( $embeds ) ); if ( $has_links ) { $embedded[ $rel ] = $embeds; } } if ( ! empty( $embedded ) ) { $data['_embedded'] = $embedded; } return $data; } /** * Wraps the response in an envelope. * * The enveloping technique is used to work around browser/client * compatibility issues. Essentially, it converts the full HTTP response to * data instead. * * @since 4.4.0 * @since 6.0.0 The `$embed` parameter can now contain a list of link relations to include. * * @param WP_REST_Response $response Response object. * @param bool|string[] $embed Whether to embed all links, a filtered list of link relations, or no links. * @return WP_REST_Response New response with wrapped data */ public function envelope_response( $response, $embed ) { $envelope = array( 'body' => $this->response_to_data( $response, $embed ), 'status' => $response->get_status(), 'headers' => $response->get_headers(), ); /** * Filters the enveloped form of a REST API response. * * @since 4.4.0 * * @param array $envelope { * Envelope data. * * @type array $body Response data. * @type int $status The 3-digit HTTP status code. * @type array $headers Map of header name to header value. * } * @param WP_REST_Response $response Original response data. */ $envelope = apply_filters( 'rest_envelope_response', $envelope, $response ); // Ensure it's still a response and return. return rest_ensure_response( $envelope ); } /** * Registers a route to the server. * * @since 4.4.0 * * @param string $route_namespace Namespace. * @param string $route The REST route. * @param array $route_args Route arguments. * @param bool $override Optional. Whether the route should be overridden if it already exists. * Default false. */ public function register_route( $route_namespace, $route, $route_args, $override = false ) { if ( ! isset( $this->namespaces[ $route_namespace ] ) ) { $this->namespaces[ $route_namespace ] = array(); $this->register_route( $route_namespace, '/' . $route_namespace, array( array( 'methods' => self::READABLE, 'callback' => array( $this, 'get_namespace_index' ), 'args' => array( 'namespace' => array( 'default' => $route_namespace, ), 'context' => array( 'default' => 'view', ), ), ), ) ); } // Associative to avoid double-registration. $this->namespaces[ $route_namespace ][ $route ] = true; $route_args['namespace'] = $route_namespace; if ( $override || empty( $this->endpoints[ $route ] ) ) { $this->endpoints[ $route ] = $route_args; } else { $this->endpoints[ $route ] = array_merge( $this->endpoints[ $route ], $route_args ); } } /** * Retrieves the route map. * * The route map is an associative array with path regexes as the keys. The * value is an indexed array with the callback function/method as the first * item, and a bitmask of HTTP methods as the second item (see the class * constants). * * Each route can be mapped to more than one callback by using an array of * the indexed arrays. This allows mapping e.g. GET requests to one callback * and POST requests to another. * * Note that the path regexes (array keys) must have @ escaped, as this is * used as the delimiter with preg_match() * * @since 4.4.0 * @since 5.4.0 Added `$route_namespace` parameter. * * @param string $route_namespace Optionally, only return routes in the given namespace. * @return array `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ), ...)`. */ public function get_routes( $route_namespace = '' ) { $endpoints = $this->endpoints; if ( $route_namespace ) { $endpoints = wp_list_filter( $endpoints, array( 'namespace' => $route_namespace ) ); } /** * Filters the array of available REST API endpoints. * * @since 4.4.0 * * @param array $endpoints The available endpoints. An array of matching regex patterns, each mapped * to an array of callbacks for the endpoint. These take the format * `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ). */ $endpoints = apply_filters( 'rest_endpoints', $endpoints ); // Normalize the endpoints. $defaults = array( 'methods' => '', 'accept_json' => false, 'accept_raw' => false, 'show_in_index' => true, 'args' => array(), ); foreach ( $endpoints as $route => &$handlers ) { if ( isset( $handlers['callback'] ) ) { // Single endpoint, add one deeper. $handlers = array( $handlers ); } if ( ! isset( $this->route_options[ $route ] ) ) { $this->route_options[ $route ] = array(); } foreach ( $handlers as $key => &$handler ) { if ( ! is_numeric( $key ) ) { // Route option, move it to the options. $this->route_options[ $route ][ $key ] = $handler; unset( $handlers[ $key ] ); continue; } $handler = wp_parse_args( $handler, $defaults ); // Allow comma-separated HTTP methods. if ( is_string( $handler['methods'] ) ) { $methods = explode( ',', $handler['methods'] ); } elseif ( is_array( $handler['methods'] ) ) { $methods = $handler['methods']; } else { $methods = array(); } $handler['methods'] = array(); foreach ( $methods as $method ) { $method = strtoupper( trim( $method ) ); $handler['methods'][ $method ] = true; } } } return $endpoints; } /** * Retrieves namespaces registered on the server. * * @since 4.4.0 * * @return string[] List of registered namespaces. */ public function get_namespaces() { return array_keys( $this->namespaces ); } /** * Retrieves specified options for a route. * * @since 4.4.0 * * @param string $route Route pattern to fetch options for. * @return array|null Data as an associative array if found, or null if not found. */ public function get_route_options( $route ) { if ( ! isset( $this->route_options[ $route ] ) ) { return null; } return $this->route_options[ $route ]; } /** * Matches the request to a callback and call it. * * @since 4.4.0 * * @param WP_REST_Request $request Request to attempt dispatching. * @return WP_REST_Response Response returned by the callback. */ public function dispatch( $request ) { $this->dispatching_requests[] = $request; /** * Filters the pre-calculated result of a REST API dispatch request. * * Allow hijacking the request before dispatching by returning a non-empty. The returned value * will be used to serve the request instead. * * @since 4.4.0 * * @param mixed $result Response to replace the requested version with. Can be anything * a normal endpoint can return, or null to not hijack the request. * @param WP_REST_Server $server Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters( 'rest_pre_dispatch', null, $this, $request ); if ( ! empty( $result ) ) { // 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 ); } array_pop( $this->dispatching_requests ); return $result; } $error = null; $matched = $this->match_request_to_handler( $request ); if ( is_wp_error( $matched ) ) { $response = $this->error_to_response( $matched ); array_pop( $this->dispatching_requests ); return $response; } list( $route, $handler ) = $matched; if ( ! is_callable( $handler['callback'] ) ) { $error = new WP_Error( 'rest_invalid_handler', __( 'The handler for the route is invalid.' ), array( 'status' => 500 ) ); } if ( ! is_wp_error( $error ) ) { $check_required = $request->has_valid_params(); if ( is_wp_error( $check_required ) ) { $error = $check_required; } else { $check_sanitized = $request->sanitize_params(); if ( is_wp_error( $check_sanitized ) ) { $error = $check_sanitized; } } } $response = $this->respond_to_request( $request, $route, $handler, $error ); array_pop( $this->dispatching_requests ); return $response; } /** * Returns whether the REST server is currently dispatching / responding to a request. * * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. * * @since 6.5.0 * * @return bool Whether the REST server is currently handling a request. */ public function is_dispatching() { return (bool) $this->dispatching_requests; } /** * Matches a request object to its handler. * * @access private * @since 5.6.0 * * @param WP_REST_Request $request The request object. * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found. */ protected function match_request_to_handler( $request ) { $method = $request->get_method(); $path = $request->get_route(); $with_namespace = array(); foreach ( $this->get_namespaces() as $namespace ) { if ( str_starts_with( trailingslashit( ltrim( $path, '/' ) ), $namespace ) ) { $with_namespace[] = $this->get_routes( $namespace ); } } if ( $with_namespace ) { $routes = array_merge( ...$with_namespace ); } else { $routes = $this->get_routes(); } foreach ( $routes as $route => $handlers ) { $match = preg_match( '@^' . $route . '$@i', $path, $matches ); if ( ! $match ) { continue; } $args = array(); foreach ( $matches as $param => $value ) { if ( ! is_int( $param ) ) { $args[ $param ] = $value; } } foreach ( $handlers as $handler ) { $callback = $handler['callback']; // Fallback to GET method if no HEAD method is registered. $checked_method = $method; if ( 'HEAD' === $method && empty( $handler['methods']['HEAD'] ) ) { $checked_method = 'GET'; } if ( empty( $handler['methods'][ $checked_method ] ) ) { continue; } if ( ! is_callable( $callback ) ) { return array( $route, $handler ); } $request->set_url_params( $args ); $request->set_attributes( $handler ); $defaults = array(); foreach ( $handler['args'] as $arg => $options ) { if ( isset( $options['default'] ) ) { $defaults[ $arg ] = $options['default']; } } $request->set_default_params( $defaults ); return array( $route, $handler ); } } return new WP_Error( 'rest_no_route', __( 'No route was found matching the URL and request method.' ), array( 'status' => 404 ) ); } /** * Dispatches the request to the callback handler. * * @access private * @since 5.6.0 * * @param WP_REST_Request $request The request object. * @param string $route The matched route regex. * @param array $handler The matched route handler. * @param WP_Error|null $response The current error object if any. * @return WP_REST_Response */ protected function respond_to_request( $request, $route, $handler, $response ) { /** * Filters the response before executing any REST API callbacks. * * Allows plugins to perform additional validation after a * request is initialized and matched to a registered route, * but before it is executed. * * Note that this filter will not be called for requests that * fail to authenticate or match to a registered route. * * @since 4.7.0 * * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. * Usually a WP_REST_Response or WP_Error. * @param array $handler Route handler used for the request. * @param WP_REST_Request $request Request used to generate the response. */ $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); // Check permission specified on the route. if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) { $permission = call_user_func( $handler['permission_callback'], $request ); if ( is_wp_error( $permission ) ) { $response = $permission; } elseif ( false === $permission || null === $permission ) { $response = new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to do that.' ), array( 'status' => rest_authorization_required_code() ) ); } } if ( ! is_wp_error( $response ) ) { /** * Filters the REST API dispatch request result. * * Allow plugins to override dispatching the request. * * @since 4.4.0 * @since 4.5.0 Added `$route` and `$handler` parameters. * * @param mixed $dispatch_result Dispatch result, will be used if not empty. * @param WP_REST_Request $request Request used to generate the response. * @param string $route Route matched for the request. * @param array $handler Route handler used for the request. */ $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); // Allow plugins to halt the request via this filter. if ( null !== $dispatch_result ) { $response = $dispatch_result; } else { $response = call_user_func( $handler['callback'], $request ); } } /** * Filters the response immediately after executing any REST API * callbacks. * * Allows plugins to perform any needed cleanup, for example, * to undo changes made during the {@see 'rest_request_before_callbacks'} * filter. * * Note that this filter will not be called for requests that * fail to authenticate or match to a registered route. * * Note that an endpoint's `permission_callback` can still be * called after this filter - see `rest_send_allow_header()`. * * @since 4.7.0 * * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. * Usually a WP_REST_Response or WP_Error. * @param array $handler Route handler used for the request. * @param WP_REST_Request $request Request used to generate the response. */ $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); if ( is_wp_error( $response ) ) { $response = $this->error_to_response( $response ); } else { $response = rest_ensure_response( $response ); } $response->set_matched_route( $route ); $response->set_matched_handler( $handler ); return $response; } /** * Returns if an error occurred during most recent JSON encode/decode. * * Strings to be translated will be in format like * "Encoding error: Maximum stack depth exceeded". * * @since 4.4.0 * * @return false|string Boolean false or string error message. */ protected function get_json_last_error() { if ( JSON_ERROR_NONE === json_last_error() ) { return false; } return json_last_error_msg(); } /** * Retrieves the site index. * * This endpoint describes the capabilities of the site. * * @since 4.4.0 * * @param WP_REST_Request $request Request data. * @return WP_REST_Response The API root index data. */ public function get_index( $request ) { // General site data. $available = array( 'name' => get_option( 'blogname' ), 'description' => get_option( 'blogdescription' ), 'url' => get_option( 'siteurl' ), 'home' => home_url(), 'gmt_offset' => get_option( 'gmt_offset' ), 'timezone_string' => get_option( 'timezone_string' ), 'page_for_posts' => (int) get_option( 'page_for_posts' ), 'page_on_front' => (int) get_option( 'page_on_front' ), 'show_on_front' => get_option( 'show_on_front' ), 'namespaces' => array_keys( $this->namespaces ), 'authentication' => array(), 'routes' => $this->get_data_for_routes( $this->get_routes(), $request['context'] ), ); // Add media processing settings for users who can upload files. if ( wp_is_client_side_media_processing_enabled() && current_user_can( 'upload_files' ) ) { // Image sizes keyed by name for client-side media processing. $available['image_sizes'] = array(); foreach ( wp_get_registered_image_subsizes() as $name => $size ) { $available['image_sizes'][ $name ] = $size; } /** This filter is documented in wp-admin/includes/image.php */ $available['image_size_threshold'] = (int) apply_filters( 'big_image_size_threshold', 2560, array( 0, 0 ), '', 0 ); /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ $available['image_strip_meta'] = (bool) apply_filters( 'image_strip_meta', true ); /* * On the server, this filter receives the decoded image's actual bit depth. * The client path never decodes the image on the server, so the filter is * applied with 16 (the maximum depth the client encoder can produce) as * both the value and the current depth. The client caps its output bit * depth at the filtered value, so a plugin lowering it (e.g. to 8) takes * effect on client-generated images too. */ /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ $available['image_max_bit_depth'] = (int) apply_filters( 'image_max_bit_depth', 16, 16 ); } $response = new WP_REST_Response( $available ); $fields = $request['_fields'] ?? ''; $fields = wp_parse_list( $fields ); if ( empty( $fields ) ) { $fields[] = '_links'; } if ( $request->has_param( '_embed' ) ) { $fields[] = '_embedded'; } if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $response->add_link( 'help', 'https://developer.wordpress.org/rest-api/' ); $this->add_active_theme_link_to_index( $response ); $this->add_site_logo_to_index( $response ); $this->add_site_icon_to_index( $response ); } else { if ( rest_is_field_included( 'site_logo', $fields ) ) { $this->add_site_logo_to_index( $response ); } if ( rest_is_field_included( 'site_icon', $fields ) || rest_is_field_included( 'site_icon_url', $fields ) ) { $this->add_site_icon_to_index( $response ); } } /** * Filters the REST API root index data. * * This contains the data describing the API. This includes information * about supported authentication schemes, supported namespaces, routes * available on the API, and a small amount of data about the site. * * @since 4.4.0 * @since 6.0.0 Added `$request` parameter. * * @param WP_REST_Response $response Response data. * @param WP_REST_Request $request Request data. */ return apply_filters( 'rest_index', $response, $request ); } /** * Adds a link to the active theme for users who have proper permissions. * * @since 5.7.0 * * @param WP_REST_Response $response REST API response. */ protected function add_active_theme_link_to_index( WP_REST_Response $response ) { $should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ); if ( ! $should_add && current_user_can( 'edit_posts' ) ) { $should_add = true; } if ( ! $should_add ) { foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { if ( current_user_can( $post_type->cap->edit_posts ) ) { $should_add = true; break; } } } if ( $should_add ) { $theme = wp_get_theme(); $response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) ); } } /** * Exposes the site logo through the WordPress REST API. * * This is used for fetching this information when user has no rights * to update settings. * * @since 5.8.0 * * @param WP_REST_Response $response REST API response. */ protected function add_site_logo_to_index( WP_REST_Response $response ) { $site_logo_id = get_theme_mod( 'custom_logo', 0 ); $this->add_image_to_index( $response, $site_logo_id, 'site_logo' ); } /** * Exposes the site icon through the WordPress REST API. * * This is used for fetching this information when user has no rights * to update settings. * * @since 5.9.0 * * @param WP_REST_Response $response REST API response. */ protected function add_site_icon_to_index( WP_REST_Response $response ) { $site_icon_id = get_option( 'site_icon', 0 ); $this->add_image_to_index( $response, $site_icon_id, 'site_icon' ); $response->data['site_icon_url'] = get_site_icon_url(); } /** * Exposes an image through the WordPress REST API. * This is used for fetching this information when user has no rights * to update settings. * * @since 5.9.0 * * @param WP_REST_Response $response REST API response. * @param int $image_id Image attachment ID. * @param string $type Type of Image. */ protected function add_image_to_index( WP_REST_Response $response, $image_id, $type ) { $response->data[ $type ] = (int) $image_id; if ( $image_id ) { $response->add_link( 'https://api.w.org/featuredmedia', rest_url( rest_get_route_for_post( $image_id ) ), array( 'embeddable' => true, 'type' => $type, ) ); } } /** * Retrieves the index for a namespace. * * @since 4.4.0 * * @param WP_REST_Request $request REST request instance. * @return WP_REST_Response|WP_Error WP_REST_Response instance if the index was found, * WP_Error if the namespace isn't set. */ public function get_namespace_index( $request ) { $namespace = $request['namespace']; if ( ! isset( $this->namespaces[ $namespace ] ) ) { return new WP_Error( 'rest_invalid_namespace', __( 'The specified namespace could not be found.' ), array( 'status' => 404 ) ); } $routes = $this->namespaces[ $namespace ]; $endpoints = array_intersect_key( $this->get_routes(), $routes ); $data = array( 'namespace' => $namespace, 'routes' => $this->get_data_for_routes( $endpoints, $request['context'] ), ); $response = rest_ensure_response( $data ); // Link to the root index. $response->add_link( 'up', rest_url( '/' ) ); /** * Filters the REST API namespace index data. * * This typically is just the route data for the namespace, but you can * add any data you'd like here. * * @since 4.4.0 * * @param WP_REST_Response $response Response data. * @param WP_REST_Request $request Request data. The namespace is passed as the 'namespace' parameter. */ return apply_filters( 'rest_namespace_index', $response, $request ); } /** * Retrieves the publicly-visible data for routes. * * @since 4.4.0 * * @param array $routes Routes to get data for. * @param string $context Optional. Context for data. Accepts 'view' or 'help'. Default 'view'. * @return array[] Route data to expose in indexes, keyed by route. */ public function get_data_for_routes( $routes, $context = 'view' ) { $available = array(); // Find the available routes. foreach ( $routes as $route => $callbacks ) { $data = $this->get_data_for_route( $route, $callbacks, $context ); if ( empty( $data ) ) { continue; } /** * Filters the publicly-visible data for a single REST API route. * * @since 4.4.0 * * @param array $data Publicly-visible data for the route. */ $available[ $route ] = apply_filters( 'rest_endpoints_description', $data ); } /** * Filters the publicly-visible data for REST API routes. * * This data is exposed on indexes and can be used by clients or * developers to investigate the site and find out how to use it. It * acts as a form of self-documentation. * * @since 4.4.0 * * @param array[] $available Route data to expose in indexes, keyed by route. * @param array $routes Internal route data as an associative array. */ return apply_filters( 'rest_route_data', $available, $routes ); } /** * Retrieves publicly-visible data for the route. * * @since 4.4.0 * * @param string $route Route to get data for. * @param array $callbacks Callbacks to convert to data. * @param string $context Optional. Context for the data. Accepts 'view' or 'help'. Default 'view'. * @return array|null Data for the route, or null if no publicly-visible data. */ public function get_data_for_route( $route, $callbacks, $context = 'view' ) { $data = array( 'namespace' => '', 'methods' => array(), 'endpoints' => array(), ); $allow_batch = false; if ( isset( $this->route_options[ $route ] ) ) { $options = $this->route_options[ $route ]; if ( isset( $options['namespace'] ) ) { $data['namespace'] = $options['namespace']; } $allow_batch = $options['allow_batch'] ?? false; if ( isset( $options['schema'] ) && 'help' === $context ) { $data['schema'] = call_user_func( $options['schema'] ); } } $allowed_schema_keywords = array_flip( wp_get_json_schema_allowed_keywords( 'rest-api' ) ); $route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route ); foreach ( $callbacks as $callback ) { // Skip to the next route if any callback is hidden. if ( empty( $callback['show_in_index'] ) ) { continue; } $data['methods'] = array_merge( $data['methods'], array_keys( $callback['methods'] ) ); $endpoint_data = array( 'methods' => array_keys( $callback['methods'] ), ); $callback_batch = $callback['allow_batch'] ?? $allow_batch; if ( $callback_batch ) { $endpoint_data['allow_batch'] = $callback_batch; } if ( isset( $callback['args'] ) ) { $endpoint_data['args'] = array(); foreach ( $callback['args'] as $key => $opts ) { if ( is_string( $opts ) ) { $opts = array( $opts => 0 ); } elseif ( ! is_array( $opts ) ) { $opts = array(); } $arg_data = array_intersect_key( $opts, $allowed_schema_keywords ); $arg_data['required'] = ! empty( $opts['required'] ); $endpoint_data['args'][ $key ] = $arg_data; } } $data['endpoints'][] = $endpoint_data; // For non-variable routes, generate links. if ( ! str_contains( $route, '{' ) ) { $data['_links'] = array( 'self' => array( array( 'href' => rest_url( $route ), ), ), ); } } if ( empty( $data['methods'] ) ) { // No methods supported, hide the route. return null; } return $data; } /** * Gets the maximum number of requests that can be included in a batch. * * @since 5.6.0 * * @return int The maximum requests. */ protected function get_max_batch_size() { /** * Filters the maximum number of REST API requests that can be included in a batch. * * @since 5.6.0 * * @param int $max_size The maximum size. */ return apply_filters( 'rest_get_max_batch_size', 25 ); } /** * Serves the batch/v1 request. * * @since 5.6.0 * * @param WP_REST_Request $batch_request The batch request object. * @return WP_REST_Response The generated response object. */ public function serve_batch_request_v1( WP_REST_Request $batch_request ) { $requests = array(); foreach ( $batch_request['requests'] as $args ) { $parsed_url = wp_parse_url( $args['path'] ); if ( false === $parsed_url ) { $requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.' ), array( 'status' => 400 ) ); continue; } $single_request = new WP_REST_Request( $args['method'] ?? 'POST', $parsed_url['path'] ); if ( ! empty( $parsed_url['query'] ) ) { $query_args = array(); wp_parse_str( $parsed_url['query'], $query_args ); $single_request->set_query_params( $query_args ); } if ( ! empty( $args['body'] ) ) { $single_request->set_body_params( $args['body'] ); } if ( ! empty( $args['headers'] ) ) { $single_request->set_headers( $args['headers'] ); } $requests[] = $single_request; } $matches = array(); $validation = array(); $has_error = false; foreach ( $requests as $single_request ) { if ( is_wp_error( $single_request ) ) { $has_error = true; $matches[] = $single_request; $validation[] = $single_request; continue; } $match = $this->match_request_to_handler( $single_request ); $matches[] = $match; $error = null; if ( is_wp_error( $match ) ) { $error = $match; } if ( ! $error ) { list( $route, $handler ) = $match; if ( isset( $handler['allow_batch'] ) ) { $allow_batch = $handler['allow_batch']; } else { $route_options = $this->get_route_options( $route ); $allow_batch = $route_options['allow_batch'] ?? false; } if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) { $error = new WP_Error( 'rest_batch_not_allowed', __( 'The requested route does not support batch requests.' ), array( 'status' => 400 ) ); } } if ( ! $error ) { $check_required = $single_request->has_valid_params(); if ( is_wp_error( $check_required ) ) { $error = $check_required; } } if ( ! $error ) { $check_sanitized = $single_request->sanitize_params(); if ( is_wp_error( $check_sanitized ) ) { $error = $check_sanitized; } } if ( $error ) { $has_error = true; $validation[] = $error; } else { $validation[] = true; } } $responses = array(); if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) { foreach ( $validation as $valid ) { if ( is_wp_error( $valid ) ) { $responses[] = $this->envelope_response( $this->error_to_response( $valid ), false )->get_data(); } else { $responses[] = null; } } return new WP_REST_Response( array( 'failed' => 'validation', 'responses' => $responses, ), WP_Http::MULTI_STATUS ); } foreach ( $requests as $i => $single_request ) { if ( is_wp_error( $single_request ) ) { $result = $this->error_to_response( $single_request ); $responses[] = $this->envelope_response( $result, false )->get_data(); continue; } $clean_request = clone $single_request; $clean_request->set_url_params( array() ); $clean_request->set_attributes( array() ); $clean_request->set_default_params( array() ); /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $result = apply_filters( 'rest_pre_dispatch', null, $this, $clean_request ); if ( empty( $result ) ) { $match = $matches[ $i ]; $error = null; if ( is_wp_error( $validation[ $i ] ) ) { $error = $validation[ $i ]; } if ( is_wp_error( $match ) ) { $result = $this->error_to_response( $match ); } else { list( $route, $handler ) = $match; if ( ! $error && ! is_callable( $handler['callback'] ) ) { $error = new WP_Error( 'rest_invalid_handler', __( 'The handler for the route is invalid' ), array( 'status' => 500 ) ); } $result = $this->respond_to_request( $single_request, $route, $handler, $error ); } } /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $single_request ); $responses[] = $this->envelope_response( $result, false )->get_data(); } return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS ); } /** * Sends an HTTP status code. * * @since 4.4.0 * * @param int $code HTTP status. */ protected function set_status( $code ) { status_header( $code ); } /** * Sends an HTTP header. * * @since 4.4.0 * * @param string $key Header key. * @param string $value Header value. */ public function send_header( $key, $value ) { /* * Sanitize as per RFC2616 (Section 4.2): * * Any LWS that occurs between field-content MAY be replaced with a * single SP before interpreting the field value or forwarding the * message downstream. */ $value = preg_replace( '/\s+/', ' ', $value ); header( sprintf( '%s: %s', $key, $value ) ); } /** * Sends multiple HTTP headers. * * @since 4.4.0 * * @param array $headers Map of header name to header value. */ public function send_headers( $headers ) { foreach ( $headers as $key => $value ) { $this->send_header( $key, $value ); } } /** * Removes an HTTP header from the current response. * * @since 4.8.0 * * @param string $key Header key. */ public function remove_header( $key ) { header_remove( $key ); } /** * Retrieves the raw request entity (body). * * @since 4.4.0 * * @global string $HTTP_RAW_POST_DATA Raw post data. * * @return string Raw request data. */ public static function get_raw_data() { // phpcs:disable PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved global $HTTP_RAW_POST_DATA; // $HTTP_RAW_POST_DATA was deprecated in PHP 5.6 and removed in PHP 7.0. if ( ! isset( $HTTP_RAW_POST_DATA ) ) { $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' ); } return $HTTP_RAW_POST_DATA; // phpcs:enable } /** * Extracts headers from a PHP-style $_SERVER array. * * @since 4.4.0 * * @param array $server Associative array similar to `$_SERVER`. * @return array Headers extracted from the input. */ public function get_headers( $server ) { $headers = array(); // CONTENT_* headers are not prefixed with HTTP_. $additional = array( 'CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true, ); foreach ( $server as $key => $value ) { if ( str_starts_with( $key, 'HTTP_' ) ) { $headers[ substr( $key, 5 ) ] = $value; } elseif ( 'REDIRECT_HTTP_AUTHORIZATION' === $key && empty( $server['HTTP_AUTHORIZATION'] ) ) { /* * In some server configurations, the authorization header is passed in this alternate location. * Since it would not be passed in both places we do not check for both headers and resolve. */ $headers['AUTHORIZATION'] = $value; } elseif ( isset( $additional[ $key ] ) ) { $headers[ $key ] = $value; } } return $headers; } } Trust | ZIRAN Comunicación https://www.ziran.es Comunicación Estratégica Thu, 20 Aug 2026 15:32:52 +0000 es hourly 1 https://wordpress.org/?v=7.1 https://www.ziran.es/wp-content/uploads/2020/07/favicon.svg Trust | ZIRAN Comunicación https://www.ziran.es 32 32 Trust anuncia descuentos de hasta el 30% en dispositivos de oficina respetuosos con el cuerpo y el medioambiente https://www.ziran.es/nota_prensa/trust-anuncia-descuentos-de-hasta-el-30-en-dispositivos-de-oficina-respetuosos-con-el-cuerpo-y-el-medioambiente/ Thu, 20 Aug 2026 15:32:52 +0000 https://www.ziran.es/?post_type=nota_prensa&p=68634 La compañía ha bajado el precio de varios dispositivos de oficina con diseño ergonómico certificado que ayudan a mantener las articulaciones sanas durante las sesiones de trabajo. Trust, la compañía especializada en dispositivos electrónicos para el ocio y el trabajo, ha anunciado que varios de sus productos estarán disponibles a precio reducido por tiempo limitado. […]

La entrada Trust anuncia descuentos de hasta el 30% en dispositivos de oficina respetuosos con el cuerpo y el medioambiente apareció primero en ZIRAN Comunicación.]]>

La compañía ha bajado el precio de varios dispositivos de oficina con diseño ergonómico certificado que ayudan a mantener las articulaciones sanas durante las sesiones de trabajo.

Trust, la compañía especializada en dispositivos electrónicos para el ocio y el trabajo, ha anunciado que varios de sus productos estarán disponibles a precio reducido por tiempo limitado. Esta selección de dispositivos engloba algunas de las mejores herramientas para la oficina de la compañía y se presenta como una opción a tener en cuenta para aquellos que necesiten renovar algún componente de su equipo.

Entre los dispositivos ofertados, destacan aquellos con diseño ergonómico, certificados por Ergocert, lo que asegura un uso respetuoso con las articulaciones y el cuerpo en general:

 

RATÓN BAYO+
(53% materiales reciclados)
Ratón ergonómico inalámbrico con ángulo vertical óptimo que reduce la tensión en la muñeca. Certificado de ergonomía Ergocert.
Disponible en blanco y en negro.

 

 

TECLADO ERGONÓMICO INALÁMBRICO KEYRA
(85% materiales reciclados)
Teclado inalámbrico multidispositivo ergonómico con diseño dividido para trabajar con comodidad y productividad. Certificado de ergonomía Ergocert.
RATÓN FERRO HYPERSCROLL
(85% materiales reciclados)
Ratón inalámbrico con conexión multidispositivo y rueda de hiperdesplazamiento para un desplazamiento superrápido ideal para Excel.
Disponible en blanco y en negro.
AURICULARES USB con ENC AYDAMAX
(85% materiales reciclados)
Cómodos auriculares circumaurales USB con micrófono con cancelación de ruido y diadema acolchada.
PACK TECLADO Y RATÓN ODY II
Pack de teclado y ratón inalámbricos y silenciosos para trabajar cómodamente durante horas.
Una gran variedad de los productos de Trust ha sido concebida pensando en la preservación del medio ambiente. Para ello, la compañía ha decidido emplear materiales reciclados de calidad para que los usuarios puedan disfrutar de los mejores dispositivos reduciendo al máximo el impacto medioambiental. Como resultado, la compañía ha sido galardonada en múltiples ocasiones con la medalla Ecovadis Gold, que la sitúa como una de las empresas más responsables del sector.
Para saber más acerca de Trust y su catálogo de dispositivos respetuosos con el cuerpo y el medioambiente, los usuarios interesados pueden visitar la página oficial de la compañía.

Acerca de Trust International

Trust International B.V. se fundó en 1983 y es la marca integral de accesorios para el estilo de vida digital. Somos una empresa global con la misión de simplificar la vida cotidiana con soluciones inteligentes y cada vez más sostenibles. Nuestro amplio surtido abarca productos de calidad a un precio asequible para satisfacer todas las necesidades de la casa, la oficina y los desplazamientos. Con un enfoque en la producción de productos más ecológicos a través de nuestra etiqueta Clevergreen, nos dedicamos a proteger el medio ambiente sin dejar de ofrecer a los consumidores productos que conocen, aman y en los que confían. Ya sea para su portátil, videoconsola, tablet, ordenador de sobremesa, smartphone o televisor, descubra todas nuestras líneas, incluidas Trust Home & Office, Trust Smart Home y Trust Gaming. Gracias a los equipos de ventas locales, los productos de Trust están disponibles en 50 países: desde tiendas locales hasta grandes tiendas de electrónica, grandes almacenes, hipermercados y e-commerce.


 

La entrada Trust anuncia descuentos de hasta el 30% en dispositivos de oficina respetuosos con el cuerpo y el medioambiente apareció primero en ZIRAN Comunicación.]]>
GXTrust presenta sus nuevas bases de refrigeración para portátiles https://www.ziran.es/nota_prensa/gxtrust-presenta-sus-nuevas-bases-de-refrigeracion-para-portatiles/ Tue, 18 Aug 2026 09:12:15 +0000 https://www.ziran.es/?post_type=nota_prensa&p=68357 Con algunas localidades españolas alcanzado sus temperaturas máximas históricas, GXTrust ha lanzado su nueva colección de bases refrigeradoras para portátiles, un complemento indispensable para los gamers que quieran preservar sus máquinas mientras disfrutan de sus juegos favoritos durante los meses más calurosos del año. Para comenzar, la compañía ha presentado la GXT 1126 Aura, una […]

La entrada GXTrust presenta sus nuevas bases de refrigeración para portátiles apareció primero en ZIRAN Comunicación.]]>

Con algunas localidades españolas alcanzado sus temperaturas máximas históricas, GXTrust ha lanzado su nueva colección de bases refrigeradoras para portátiles, un complemento indispensable para los gamers que quieran preservar sus máquinas mientras disfrutan de sus juegos favoritos durante los meses más calurosos del año.

Para comenzar, la compañía ha presentado la GXT 1126 Aura, una base de refrigeración con iluminación multicolor para mantener el portátil refrigerado durante las sesiones de gaming más intensas. Con ventiladores ajustables de 200 mm, es capaz de evitar el sobrecalentamiento en portátiles de hasta 17,3 pulgadas. Ya está disponible por PVPR 59,99€.

Aquellos que busquen un diseño más sobrio y premium, podrán optar por el modelo GXT 1125 Quno, con iluminación LED y 5 ventiladores. Además de suponer un soporte ideal para portátiles gaming, dispone también de un soporte para mantener el teléfono móvil visible mientras se juega. Ya puede adquirirse a un PVPR de 59,99€.

Y, para los que prefieran un diseño más agresivo, está disponible la GXT 278 Yozu, una base de refrigeración para ordenadores portátiles de 17,3 pulgadas. Cuenta con 4 ventiladores iluminados con velocidad ajustable y una carcasa robusta. Además, los tres ajustes en altura de la base Yozu permiten inclinar el portátil con el ángulo perfecto para disfrutar de la mejor ergonomía y perspectiva para ganar la partida. Ya está a la venta a un PVPR de 49,99€.

Con más de 40 años de experiencia en la fabricación de accesorios digitales, GXTrust, con sede en los Países Bajos, es la marca integral de referencia para los jugadores de PC y consola que buscan equipamiento de calidad. Desde ratones de alta precisión y teclados mecánicos resistentes, hasta sillas ultracómodas y auriculares con sonido envolvente, el catálogo de Trust Gaming proporciona el equipo completo para que los gamers puedan alcanzar todo su potencial.

La entrada GXTrust presenta sus nuevas bases de refrigeración para portátiles apareció primero en ZIRAN Comunicación.]]>
Vigila tu propiedad con las cámaras IP de seguridad de Trust https://www.ziran.es/nota_prensa/vigila-tu-propiedad-con-las-camaras-ip-de-seguridad-de-trust/ Tue, 21 Jul 2026 13:25:10 +0000 https://www.ziran.es/?post_type=nota_prensa&p=67092 Diseñadas para asegurar un uso flexible y fiable tanto en interiores como en exteriores, las cámaras IP de seguridad de Trust se pueden controlar y gestionar al instante a través de la aplicación gratuita Trust WIFI con una interfaz cómoda y sencilla. Con la llegada del verano y con las vacaciones a la vuelta de […]

La entrada Vigila tu propiedad con las cámaras IP de seguridad de Trust apareció primero en ZIRAN Comunicación.]]>

Diseñadas para asegurar un uso flexible y fiable tanto en interiores como en exteriores, las cámaras IP de seguridad de Trust se pueden controlar y gestionar al instante a través de la aplicación gratuita Trust WIFI con una interfaz cómoda y sencilla.

Con la llegada del verano y con las vacaciones a la vuelta de la esquina, Trust ha destacado su familia de cámaras de seguridad IP, con varios modelos optimizados para supervisar tanto el interior como el exterior del hogar. Gracias a la sencillez de su instalación, estas cámaras pueden situarse en cualquier lugar cercano a un enchufe; basta con conectarlas a la red eléctrica y seguir los pasos de configuración en la app, que permite controlar otros productos de la compañía, como las luces inteligentes, para asegurar nuestra casa ante multitud de imprevistos.

Varios modelos, incluido el optimizado para exteriores, disponen de un cuerpo robotizado con 360º de rotación. Esta función puede controlarse manualmente desde la app, así como de manera automática con la tecnología de seguimiento incorporada. Todas las cámaras de Trust son capaces de distinguir a las personas, descartando alertas innecesarias, como las causadas por mascotas. Además, incluyen una sirena (y una luz de alarma en el caso del modelo para el exterior) para ahuyentar a visitantes no deseados y así mantener un entorno seguro.

Trust también ha lanzado un modelo ultracompacto, ideal para aquellos lugares en los que una cámara grande pudiese resultar demasiado evidente. Este modelo no goza del mecanismo de rotación, pero ofrece la misma calidad de imagen de 3MP en una solución ideal para monitorizar espacios más reducidos.

Todas las cámaras de seguridad IP de Trust son capaces de enviar notificaciones al teléfono móvil, informando al usuario de cualquier detección de movimiento, en cualquier momento y en cualquier lugar. La app asociada permite visualizar vídeo en directo, así como repasar grabaciones de sucesos detectados anteriormente. Con estas cámaras de seguridad inteligentes, Trust ha querido ofrecer una solución sencilla y confiable para supervisar de forma óptima la seguridad de la casa y el jardín.

Los cuatro modelos de cámaras de seguridad IP de Trust ya están disponibles en España y pueden adquirirse en las principales tiendas físicas y en línea de nuestro país:

IPCAM-2900
Cámara de vigilancia de 3MP con alarma sonora para seguridad en interiores.
PVPR: 24,99€
IPCAM-2700
Cámara de seguridad de 360° para interiores de 3MP con alarma sonora para una seguridad óptima.

PVPR: 39,99€

Especificaciones y puntos de venta

IPCAM-2800
Cámara de seguridad de 360° para interiores de 2MP con alarma sonora para una seguridad óptima.

PVPR: 29,99€

Especificaciones y puntos de venta

IPCAM-3900
Cámara exterior de 360° con 3MP, alarma sonora y luminosa para la seguridad de jardines y entradas de vehículos.

PVPR: 49,99€

Especificaciones y puntos de venta

Trust es una compañía neerlandesa con un amplio catálogo de productos para el hogar y la oficina. En los últimos años, se ha propuesto fabricar sus nuevos dispositivos usando materiales reciclados con la intención de tener el menor impacto posible en el medioambiente. Dispone de auriculares, ratones, teclados, altavoces y de muchos otros productos que se componen, en la medida de lo posible, de plásticos reutilizados. Su filosofía ha sido galardonada con la Medalla de Oro Ecovadis 2024, como resultado de la solidez medioambiental, social y de gobernanza de la empresa.
Para saber más acerca de Trust y de su extensa oferta de productos para el ocio y el trabajo, los usuarios interesados pueden visitar la página oficial de la compañía.

Acerca de Trust International

Trust International B.V. se fundó en 1983 y es la marca integral de accesorios para el estilo de vida digital. Somos una empresa global con la misión de simplificar la vida cotidiana con soluciones inteligentes y cada vez más sostenibles. Nuestro amplio surtido abarca productos de calidad a un precio asequible para satisfacer todas las necesidades de la casa, la oficina y los desplazamientos. Con un enfoque en la producción de productos más ecológicos a través de nuestra etiqueta Clevergreen, nos dedicamos a proteger el medio ambiente sin dejar de ofrecer a los consumidores productos que conocen, aman y en los que confían. Ya sea para su portátil, videoconsola, tablet, ordenador de sobremesa, smartphone o televisor, descubra todas nuestras líneas, incluidas Trust Home & Office, Trust Smart Home y Trust Gaming. Gracias a los equipos de ventas locales, los productos de Trust están disponibles en 50 países: desde tiendas locales hasta grandes tiendas de electrónica, grandes almacenes, hipermercados y e-commerce.


 

La entrada Vigila tu propiedad con las cámaras IP de seguridad de Trust apareció primero en ZIRAN Comunicación.]]>
Refresca tu equipo este verano con lo mejor de GXTrust https://www.ziran.es/nota_prensa/refresca-tu-equipo-este-verano-con-lo-mejor-de-gxtrust/ Wed, 08 Jul 2026 17:55:23 +0000 https://www.ziran.es/?post_type=nota_prensa&p=66374 Trust, el reconocido fabricante de dispositivos para el ocio y el trabajo, ha recopilado los mejores accesorios gaming para disfrutar de las vacaciones de verano. Desde sillas de tela transpirables hasta mandos que se acoplan al móvil, la compañía pone a disposición de los usuarios los gadgets ideales para hacernos olvidar, en la medida de […]

La entrada Refresca tu equipo este verano con lo mejor de GXTrust apareció primero en ZIRAN Comunicación.]]>

Trust, el reconocido fabricante de dispositivos para el ocio y el trabajo, ha recopilado los mejores accesorios gaming para disfrutar de las vacaciones de verano. Desde sillas de tela transpirables hasta mandos que se acoplan al móvil, la compañía pone a disposición de los usuarios los gadgets ideales para hacernos olvidar, en la medida de lo posible, el calor estival.

Para aquellos que estén cansados de quedarse pegados en el sofá durante los meses más calurosos, GXTrust destaca la silla GXT 723 Ruya, que incluye tapicería con tejido transpirable para mantener el frescor. Con su altura ajustable, asiento de espuma moldeada y respaldo de espuma de alta densidad, reposabrazos 3D y un mecanismo que bloquea la inclinación, se puede personalizar para lograr el máximo nivel de comodidad y control. Ya puede adquirirse por 199,99€.

Los que disfruten de sus videojuegos favoritos en smartphones Android, iOS o retransmitiendo sus partidas de PlayStation o Xbox en el móvil, podrán aprovechar la comodidad y precisión del GXT 735 Mylox, un mando inalámbrico que se acopla al teléfono convirtiéndolo en una consola portátil. Este aliado incondicional para el verano ya está disponible a un PVPR de 49,99€.

Para los fans de PlayStation que quieran disfrutar del mejor sonido al resguardo del calor y sin molestos cables, los auriculares GXT 499 Forta inalámbricos con licencia oficial para PS5 se presentan como una gran solución de sonido 3D. Presentan el logo oficial de la consola, se conectan mediante un receptor USB de 2,4 GHz de latencia ultrabaja y ofrecen un sonido rico y potente que da vida a los juegos. Tienen un micrófono extraíble y pueden comprarse en blanco y en negro por 79,99€.

Y aquellos que quieran renovar la apariencia y efectividad de sus ordenadores, este verano podrán hacerse con el ratón GXT 122 Felox+ dual, que incorpora una batería recargable y tiene un sensor PixArt PAW3311, una tasa de sondeo de 1000 Hz y un sensor óptico de 12 000 ppp con respuestas ultrarrápidas y precisión quirúrgica por 29,99€, Y el teclado mecánico inalámbrico GXT 873 Acira, con interruptores rojos mecánicos Outemu, conexión triple y 50 millones de pulsaciones por 69,99€.

Con más de 40 años de experiencia en la fabricación de accesorios digitales, GXTrust, con sede en los Países Bajos, es la marca integral de referencia para los jugadores de PC y consola que buscan equipamiento de calidad. Desde ratones de alta precisión y teclados mecánicos resistentes, hasta sillas ultracómodas y auriculares con sonido envolvente, el catálogo de Trust Gaming proporciona el equipo completo para que los gamers puedan alcanzar todo su potencial.

Imágenes de los productos
Silla transpirable Ruya: https://www.trust.com/es/media?q=25532
Mando para móviles Mylox: https://www.trust.com/es/media?q=25485
Auriculares oficiales PS5 Forta: https://www.trust.com/es/media?q=25978
Ratón gaming Felox Dual sin cables: https://www.trust.com/es/media?q=25748
Teclado mecánico sin cables Acira: https://www.trust.com/es/media?q=26052

Sobre GXTrust
Con más de 40 años de experiencia en la fabricación de accesorios digitales, GXTrust, con sede en los Países Bajos, es la marca integral de referencia para los jugadores de PC y consola que buscan equipamiento de calidad. Con productos asequibles, duraderos y fáciles de usar diseñados en Europa, nuestra amplia gama cubre todo lo necesario para mejorar las habilidades y ofrecer una experiencia de juego verdaderamente inmersiva. Desde ratones de alta precisión y teclados mecánicos resistentes, hasta sillas ultracómodas y auriculares con sonido envolvente, el catálogo de Trust Gaming proporciona el equipo completo para que los gamers puedan alcanzar todo su potencial. Con el apoyo de equipos locales de ventas y marketing, los productos Trust Gaming están disponibles en más de 50 países, con más de 20.000 referencias repartidas entre tiendas locales, grandes superficies de electrónica, grandes almacenes, hipermercados y plataformas online.

La entrada Refresca tu equipo este verano con lo mejor de GXTrust apareció primero en ZIRAN Comunicación.]]>
Al buen tiempo, buena carga https://www.ziran.es/nota_prensa/al-buen-tiempo-buena-carga/ Thu, 11 Jun 2026 09:42:27 +0000 https://www.ziran.es/?post_type=nota_prensa&p=65384 Trust presenta nuevas baterías portátiles ideales para el verano. Trust, la reconocida marca de dispositivos electrónicos para el ocio y el trabajo, ha presentado su nueva gama de powerbanks de cara a la llegada del verano. Las baterías portátiles se han convertido en un accesorio imprescindible para el día a día, ya que ayudan a […]

La entrada Al buen tiempo, buena carga apareció primero en ZIRAN Comunicación.]]>

Trust presenta nuevas baterías portátiles ideales para el verano.

Trust, la reconocida marca de dispositivos electrónicos para el ocio y el trabajo, ha presentado su nueva gama de powerbanks de cara a la llegada del verano.

Las baterías portátiles se han convertido en un accesorio imprescindible para el día a día, ya que ayudan a los usuarios a mantenerse conectados en el trabajo, durante los viajes o en cualquier lugar. Desde modelos compactos para recargas rápidas hasta soluciones de gran capacidad para un uso prolongado y la carga de varios dispositivos, contar con el cargador portátil adecuado marca la diferencia.

El primer modelo destacado por la compañía es el Pacto 5.000 mAh Pocket, una powerbank ultracompacta de 5000 mAh que mide solo 77 x 38,6 x 25 mm, por lo que cabe fácilmente en el bolsillo o bolso. Destaca por su diseño reducido y apto para aviones, lo que permite llevarla a cualquier parte. Incorpora un puerto USB-C y un enchufe plegable que también sirven para cargar el cargador portátil, además de un cable integrado, que permiten cargar hasta tres dispositivos a la vez. Con carga rápida de 20 W y un indicador LED de porcentaje, Pacto ya se puede adquirir a un PVPR de 19,99€.

Aquellos que busquen una batería portátil de mayor capacidad, pueden encontrar una  buena solución en los modelos Avala de 10.000 y 20.000 mAh con carga rápida. Permite cargar hasta tres dispositivos a la vez, su diseño es apto para aviones e incorpora un puerto USB-C PD de 20 W y dos puertos USB-A adicionales. Avala destaca por su brillante pantalla LED, que muestra exactamente cuánta batería te queda (0-100 %), mientras que una luz verde indica que se está realizando una carga rápida. Además, este powerbank incluye un práctico cable USB-C de 30 cm para que siempre estés listo para recargar. La batería portátil Avala ya está disponible a un PVPR de 29,99€ en su versión de 10.000 mAh, y por 39,99€ en su versión de 20.000 mAh.

Y para los que necesiten una gran capacidad de carga en un diseño ultradelgado, ya están disponible las powerbank Fiera de 10.000 y 20.000 mAh de carga rápida. Es capaz de cargar dos dispositivos a la vez con el puerto USB-C adicional de 20 W incorporado. La pantalla LED muestra la energía restante (0-100 %) de un vistazo, mientras que su diseño ultradelgado (de tamaño similar al de un teléfono normal) cabe fácilmente en cualquier bolsillo o bolso. Además, su diseño apto para aviones permite llevarla siempre a mano. Las powerbank Fiera ya se pueden comprar a un PVPR de 29,99€ en el modelo de 10.000 mAh y por 39,99€ en su versión de 20.000 mAh.

Trust es una compañía neerlandesa con un amplio catálogo de productos para el trabajo y el ocio. En los últimos años, se ha propuesto fabricar sus nuevos dispositivos usando materiales reciclados con la intención de tener el menor impacto posible en el medioambiente. Dispone de auriculares, ratones, teclados, altavoces y de muchos otros productos que se componen, en la medida de lo posible, de plásticos reutilizados. Su filosofía ha sido galardonada con la Medalla de Oro Ecovadis 2025, como resultado de la solidez medioambiental, social y de gobernanza de la empresa. Además, es la primera marca de accesorios para PC en obtener la certificación ErgoCert, un organismo acreditado internacionalmente que se especializa en la validación científica de la ergonomía en una amplia gama de sectores.

Para saber más acerca de Trust y de su extensa oferta de productos para el ocio y el trabajo, los usuarios interesados pueden visitar la página oficial de la compañía.

La entrada Al buen tiempo, buena carga apareció primero en ZIRAN Comunicación.]]>
Un teclado pequeño con un gran potencial: el nuevo teclado mecánico gaming inalámbrico Acira 60% Mini Tri-Mode https://www.ziran.es/nota_prensa/un-teclado-pequeno-con-un-gran-potencial-el-nuevo-teclado-mecanico-gaming-inalambrico-acira-60-mini-tri-mode/ Tue, 02 Jun 2026 16:34:10 +0000 https://www.ziran.es/?post_type=nota_prensa&p=65176 Con un diseño compacto, interruptores mecánicos táctiles y conexión inalámbrica con tres modos, el nuevo teclado inalámbrico gaming GXTrust Acira Tri-Mode demuestra que lo pequeño también puede ser extraordinario. Incluye todas las fantásticas prestaciones de su predecesor, el teclado inalámbrico Acira y, además, esta versión de tres modos ofrece a los gamers la ventaja adicional […]

La entrada Un teclado pequeño con un gran potencial: el nuevo teclado mecánico gaming inalámbrico Acira 60% Mini Tri-Mode apareció primero en ZIRAN Comunicación.]]>

Con un diseño compacto, interruptores mecánicos táctiles y conexión inalámbrica con tres modos, el nuevo teclado inalámbrico gaming GXTrust Acira Tri-Mode demuestra que lo pequeño también puede ser extraordinario. Incluye todas las fantásticas prestaciones de su predecesor, el teclado inalámbrico Acira y, además, esta versión de tres modos ofrece a los gamers la ventaja adicional de poder elegir el tipo de conexión y jugar exactamente como prefieran.

Gracias a su formato del 60 %, el teclado Acira Tri-Mode permite aprovechar más espacio del escritorio y ofrece más margen para el movimiento del ratón, algo esencial en el gaming competitivo, donde cada milímetro cuenta. A pesar de su tamaño reducido, los gamers no tendrán que renunciar a la funcionalidad: sus teclas de triple función ofrecen una experiencia de teclado de tamaño completo, junto con teclas multifunción Fn* que facilitan el acceso directo a controles multimedia como el de reproducir, pausar o ajustar el volumen.

Las opciones de conectividad del teclado Acira Tri-Mode son increíblemente versátiles, con la posibilidad de elegir entre un receptor USB-A, dos conexiones Bluetooth** y un cable de carga USB-C-A incluido, para que los usuarios decidan cómo conectarse en cada momento. Tanto si se juega en PC, se cambia a un portátil para trabajar o el usuario tiene que conectarse a otros dispositivos, el teclado Acira Tri-Mode se adapta fácilmente a diferentes set-ups.

Diseñado para ofrecer pulsaciones rápidas y precisas, el teclado Acira Tri-Mode incorpora interruptores lineales OUTEMU Red, probados para soportar hasta 50 millones de pulsaciones y garantizar un rendimiento fiable y duradero. Su batería recargable integrada proporciona hasta 50 horas de juego con una sola carga***, incluso con la iluminación RGB activada. Y cuando llega el momento de recargarlo, el cable USB-C-A de 1,8 m incluido permite seguir jugando mientras se carga.

Es más, este teclado mecánico gaming permite personalizar la experiencia al máximo. Su retroiluminación RGB completa cuenta con una selección de 16,8 millones de combinaciones de colores, para que los gamers puedan configurar el set-up que encaje perfectamente con su estilo. También incluye un software que ofrece la posibilidad de programar teclas y personalizar los efectos de iluminación según el estado de ánimo o el juego del momento.

Compacto, potente e infinitamente personalizable, el teclado Acira Tri-Mode es perfecto para los jugadores que buscan un rendimiento de alto nivel sin ocupar todo el escritorio.

Disponible actualmente, el teclado mecánico gaming inalámbrico Acira 60% Mini Tri-Mode está a la venta por un PVP de 69,99 €.

*Es posible que la funcionalidad sea limitada en consola
**La conexión Bluetooth no está disponible ni en PlayStation® ni en Xbox®
***La vida útil de la batería depende del uso promedio y puede variar según la situación

 


Sobre GXTrust
Con más de 40 años de experiencia en la fabricación de accesorios digitales, GXTrust, con sede en los Países Bajos, es la marca integral de referencia para los jugadores de PC y consola que buscan equipamiento de calidad. Con productos asequibles, duraderos y fáciles de usar diseñados en Europa, nuestra amplia gama cubre todo lo necesario para mejorar las habilidades y ofrecer una experiencia de juego verdaderamente inmersiva. Desde ratones de alta precisión y teclados mecánicos resistentes, hasta sillas ultracómodas y auriculares con sonido envolvente, el catálogo de Trust Gaming proporciona el equipo completo para que los gamers puedan alcanzar todo su potencial. Con el apoyo de equipos locales de ventas y marketing, los productos Trust Gaming están disponibles en más de 50 países, con más de 20.000 referencias repartidas entre tiendas locales, grandes superficies de electrónica, grandes almacenes, hipermercados y plataformas online.
La entrada Un teclado pequeño con un gran potencial: el nuevo teclado mecánico gaming inalámbrico Acira 60% Mini Tri-Mode apareció primero en ZIRAN Comunicación.]]>
Nuevo ratón ergonómico con hyperscroll Trust Vyran https://www.ziran.es/nota_prensa/nuevo-raton-ergonomico-con-hyperscroll-trust-vyran/ Thu, 07 May 2026 13:54:01 +0000 https://www.ziran.es/?post_type=nota_prensa&p=64222 Trust ha anunciado el lanzamiento del nuevo Vyran, un ratón ergonómico con conexión inalámbrica múltiple y rueda hyperscroll. Su diseño presenta un ángulo óptimo de 57° y un cómodo soporte para el pulgar, ideal para trabajar durante horas sin sufrir dolor en la muñeca y el brazo. Además de asegurar una postura respetuosa con las […]

La entrada Nuevo ratón ergonómico con hyperscroll Trust Vyran apareció primero en ZIRAN Comunicación.]]>

Trust ha anunciado el lanzamiento del nuevo Vyran, un ratón ergonómico con conexión inalámbrica múltiple y rueda hyperscroll. Su diseño presenta un ángulo óptimo de 57° y un cómodo soporte para el pulgar, ideal para trabajar durante horas sin sufrir dolor en la muñeca y el brazo.

Además de asegurar una postura respetuosa con las articulaciones, el ratón Vyran representa una solución perfecta para aquellos que trabajen con interminables hojas de cálculo gracias a su rueda hyperscroll con función de inclinación. Independientemente de la extensión del documento, los usuarios podrán moverse a lo largo y ancho sin ningún esfuerzo.

El Trust Vyran también es un ratón versátil gracias a su capacidad de conectarse y alternar entre hasta tres dispositivos mediante el receptor USB-A inalámbrico de 2,4 G (o el adaptador USB-C incluido) y dos conexiones Bluetooth. Su diseño enfocado a la productividad no choca con el respeto al medioambiente: se compone de un 85% de plástico reciclado e incorpora una batería recargable que alcanza los 6 meses de uso.

Para finalizar, el ratón Vyran ha sido concebido para mejorar el trabajo del usuario y de las personas que se encuentren a su alrededor gracias a los botones silenciosos izquierdo y derecho. También incluye dos botones laterales permiten una navegación rápida hacia delante y hacia atrás, y la velocidad del cursor es ajustable (1000-4800 ppp) para asegurar un movimiento preciso y respetuoso con el entorno.

El ratón ergonómico con conexión inalámbrica múltiple y rueda hyperscroll Trust Vyran ya está disponible a un PVP de 59,99€.

Trust es una compañía neerlandesa con un amplio catálogo de productos para el trabajo y el ocio. En los últimos años, se ha propuesto fabricar sus nuevos dispositivos usando materiales reciclados con la intención de tener el menor impacto posible en el medioambiente. Dispone de auriculares, ratones, teclados, altavoces y de muchos otros productos que se componen, en la medida de lo posible, de plásticos reutilizados. Su filosofía ha sido galardonada con la Medalla de Oro Ecovadis 2025, como resultado de la solidez medioambiental, social y de gobernanza de la empresa.

Además, es la primera marca de accesorios para PC en obtener la certificación ErgoCert, un organismo acreditado internacionalmente que se especializa en la validación científica de la ergonomía en una amplia gama de sectores.

Para saber más acerca de Trust y de su extensa oferta de productos para el ocio y el trabajo, los usuarios interesados pueden visitar la página oficial de la compañía.

La entrada Nuevo ratón ergonómico con hyperscroll Trust Vyran apareció primero en ZIRAN Comunicación.]]>
Trust celebra el día de la Tierra con productos diseñados con materiales reciclados https://www.ziran.es/nota_prensa/trust-celebra-el-dia-de-la-tierra-con-productos-disenados-con-materiales-reciclados/ Thu, 23 Apr 2026 17:00:17 +0000 https://www.ziran.es/?post_type=nota_prensa&p=63957 La compañía neerlandesa ha vuelto a recibir la medalla de Oro Ecovadis en 2026, un galardón que premia su compromiso con el medioambiente. Hoy, 22 de abril, es el Día Internacional de la Madre Tierra, un momento de celebración y de concienciación  que sirve para transmitir un mensaje fundamental: debemos cuidar nuestro planeta y luchar […]

La entrada Trust celebra el día de la Tierra con productos diseñados con materiales reciclados apareció primero en ZIRAN Comunicación.]]>

La compañía neerlandesa ha vuelto a recibir la medalla de Oro Ecovadis en 2026, un galardón que premia su compromiso con el medioambiente.

Hoy, 22 de abril, es el Día Internacional de la Madre Tierra, un momento de celebración y de concienciación  que sirve para transmitir un mensaje fundamental: debemos cuidar nuestro planeta y luchar por un futuro próspero, tanto para nosotros como para las generaciones venideras.

En una fecha tan señalada, Trust ha querido destacar las políticas que está llevando a cabo para asegurar que su actividad sea sostenible y respetuosa con el medio ambiente, así como algunos productos que sirven como ejemplo de cómo se pueden crear y comercializar dispositivos tecnológicos reduciendo su impacto en la naturaleza.

Esta selección de dispositivos destaca por el uso de materiales reciclados de calidad, que ofrecen a los usuarios el mejor rendimiento así como la oportunidad de aportar al cuidado de nuestro planeta eligiendo de manera responsable. Existen ratones, teclados, auriculares e incluso sillas diseñados con esta filosofía. A continuación, se destacan algunos ejemplos:

  • El ratón ergonómico inalámbrico Bayo II, con una composición del 53% de plástico reciclado, asegura también una posición saludable para las articulaciones. Dispone de la certificación ergonómica Ergocert, convirtiéndose en una gran opción a un PVP de 39,99€.
  • El teclado inalámbrico multidispositivo Vaiya, que proporciona una conexión fácil y cómoda entre dispositivos, con un 55% de materiales reciclados. Ya está disponible por 39,99€.
  • El ratón con rueda de hiperdeslizamiento Ferro, inalámbrico y con conexión multidispositivo, se compone de un 85% de materiales reciclados. Ya está a la venta a un PVP de 29,99€.
  • Los auriculares Ayda Max USB con tecnología ENC de cancelación de ruido integrada en el micrófono, aseguran una comunicación cómoda y nítida con un diseño ecológico compuesto por un 85% de plástico reciclado. Están disponibles por 39,99€.
  • El teclado inalámbrico multidispositivo Keyra, con certificado ergonómico Ergocert, se presenta como una opción respetuosa con nuestro cuerpo y con el medio ambiente, ya que se compone de un 85% de materiales reciclados. Puede obtenerse a un PVP de 69,99€.

Además de los dispositivos para el trabajo, Trust también dispone de un amplio catálogo de productos gaming. Los amantes de los videojuegos también lo pueden ser del planeta gracias a los siguientes artículos:

  • El mando inalámbrico gaming GXT 542 Muta, con triple conexión y soporte multiplataforma, proporciona un control total en los videojuegos. Fabricado con un 75% de materiales reciclados, ya puede adquirirse a un PVP de 39,99€.
  • La silla Ruya, cómoda, ajustable y con tapicería de tela, ha sido diseñada con un marco de madera con Certificado FSC. Ya está disponible a un PVP de 199,99€.

Trust es una compañía neerlandesa con un amplio catálogo de productos para el trabajo y el ocio. En los últimos años, se ha propuesto fabricar sus nuevos dispositivos usando materiales reciclados con la intención de tener el menor impacto posible en el medioambiente. Dispone de auriculares, ratones, teclados, altavoces y de muchos otros productos que se componen, en la medida de lo posible, de plásticos reutilizados. Su filosofía ha sido galardonada con la Medalla de Oro Ecovadis 2026, como resultado de la solidez medioambiental, social y de gobernanza de la empresa. Además, es la primera marca de accesorios para PC en obtener la certificación ErgoCert, un organismo acreditado internacionalmente que se especializa en la validación científica de la ergonomía en una amplia gama de sectores.

Para saber más acerca de Trust y de su extensa oferta de productos para el ocio y el trabajo, los usuarios interesados pueden visitar la página oficial de la compañía.

 

La entrada Trust celebra el día de la Tierra con productos diseñados con materiales reciclados apareció primero en ZIRAN Comunicación.]]>
Oficialmente sin límites: disfruta de una experiencia de juego fluida y sin cables con los nuevos auriculares inalámbricos de gaming Forta https://www.ziran.es/nota_prensa/oficialmente-sin-limites-disfruta-de-una-experiencia-de-juego-fluida-y-sin-cables-con-los-nuevos-auriculares-inalambricos-de-gaming-forta/ Tue, 14 Apr 2026 15:36:01 +0000 https://www.ziran.es/?post_type=nota_prensa&p=63721 Following the success of the Forta gaming headset, Trust announces the launch of the highly anticipated Forta Wireless, an officially licensed product for PlayStation®5 consoles. Featuring all the premium characteristics gamers have come to expect from the Forta—immersive sound, seamless console connectivity, and an ultra-comfortable design—this new addition offers one ultimate advantage: complete wireless freedom. […]

La entrada Oficialmente sin límites: disfruta de una experiencia de juego fluida y sin cables con los nuevos auriculares inalámbricos de gaming Forta apareció primero en ZIRAN Comunicación.]]>

Following the success of the Forta gaming headset, Trust announces the launch of the highly anticipated Forta Wireless, an officially licensed product for PlayStation®5 consoles. Featuring all the premium characteristics gamers have come to expect from the Forta—immersive sound, seamless console connectivity, and an ultra-comfortable design—this new addition offers one ultimate advantage: complete wireless freedom.

Designed specifically for PlayStation® fans, the officially licensed Forta Wireless Headset offers a lossless, ultra-low-latency wireless connection via a 2.4G USB receiver. This allows gamers to move freely, celebrate victories, and enjoy the game without any cables getting in their way. Furthermore, the absence of delays ensures precise actions and reactions, guaranteeing gamers stay connected during every crucial moment.

With powerful 50mm drivers and 3D audio in compatible PS5® games, the Forta Wireless Headset delivers rich, detailed sound that brings every game to life, whether you’re in intense multiplayer battles or exploring vast single-player worlds. With truly immersive audio, gamers will hear every approaching footstep, explosion, and whisper, enjoying the most exciting gaming experience yet.

Designed for intense gaming sessions, the Forta wireless headset features rotating earcups and an adjustable headband, combined with thick, extra-soft circumaural ear cushions that keep users comfortable for hours. The detachable cardioid microphone captures clear voices for team communication and can be removed when not needed. Integrated volume control and a convenient microphone mute button allow easy access to settings without interrupting gameplay.

Finally, the built-in rechargeable battery offers up to 55 hours of uninterrupted playtime* on a single charge, so gamers can enjoy multiple sessions without constantly worrying about running out of battery. When it’s time to recharge, the included 1m USB-C to USB-C cable lets gamers keep playing while the headset charges, so the action never has to stop.

With a Midnight Black, White edition and an exclusive Amazon Black & White edition that complements the design of the PS5® console, the Forta wireless gaming headset combines performance, comfort and official PS5® compatibility with the ability to play with total freedom.

Currently available, the Forta wireless gaming headset is on sale for an RRP of €79.99.


About GXTrust

With over 40 years of experience manufacturing digital accessories, Netherlands-based GXTrust is the go-to brand for PC and console gamers seeking quality equipment. Offering affordable, durable, and user-friendly products designed in Europe, our extensive range covers everything needed to enhance skills and deliver a truly immersive gaming experience. From high-precision mice and robust mechanical keyboards to ultra-comfortable chairs and surround sound headsets, the Trust Gaming catalog provides the complete gear gamers need to reach their full potential. Supported by local sales and marketing teams, Trust Gaming products are available in over 50 countries, with more than 20,000 items distributed across local stores, electronics retailers, department stores, hypermarkets, and online platforms.

La entrada Oficialmente sin límites: disfruta de una experiencia de juego fluida y sin cables con los nuevos auriculares inalámbricos de gaming Forta apareció primero en ZIRAN Comunicación.]]>
Trust mantiene su liderazgo en sostenibilidad al ser premiada con la medalla de oro EcoVadis por cuarto año consecutivo https://www.ziran.es/nota_prensa/trust-mantiene-su-liderazgo-en-sostenibilidad-al-ser-premiada-con-la-medalla-de-oro-ecovadis-por-cuarto-ano-consecutivo/ Wed, 08 Apr 2026 08:56:17 +0000 https://www.ziran.es/?post_type=nota_prensa&p=63641 La sostenibilidad sigue siendo una prioridad incuestionable tanto para las empresas como para los consumidores y, al recibir la medalla de oro EcoVadis por cuarto año consecutivo, Trust demuestra que esto es algo más que una moda pasajera. Este último logro mantiene a Trust en el grupo del 5 % de las mejores empresas evaluadas […]

La entrada Trust mantiene su liderazgo en sostenibilidad al ser premiada con la medalla de oro EcoVadis por cuarto año consecutivo apareció primero en ZIRAN Comunicación.]]>

La sostenibilidad sigue siendo una prioridad incuestionable tanto para las empresas como para los consumidores y, al recibir la medalla de oro EcoVadis por cuarto año consecutivo, Trust demuestra que esto es algo más que una moda pasajera. Este último logro mantiene a Trust en el grupo del 5 % de las mejores empresas evaluadas por EcoVadis en los últimos 12 meses, lo que refuerza aún más la posición de la compañía como líder en prácticas empresariales sostenibles.

EcoVadis es uno de los proveedores de calificaciones de sostenibilidad empresarial más reputados del mundo. Se encarga de evaluar la eficacia con la que las empresas integran la sostenibilidad en sus operaciones y sistemas de gestión. Con unos criterios cada vez más exigentes, la obtención de la medalla de oro refleja una vez más el progreso continuo de Trust a la hora de convertir la sostenibilidad en un pilar fundamental de todas sus actividades, desde el diseño y el abastecimiento de materiales hasta las operaciones y la gobernanza.
Este año, Trust ha recibido una mención especial por su sólido desempeño en cuatro ámbitos clave: medio ambiente, prácticas laborales y derechos humanos, ética y compras sostenibles. La compañía continúa demostrando un avance claro y una responsabilidad firme en estos ámbitos; por ejemplo, mediante el respaldo a iniciativas globales como la Science Based Targets (SBTi), el Pacto Mundial de las Naciones Unidas (UNGC) y la Business Social Compliance Initiative (BSCI).
El progreso de Trust no se limita únicamente a las políticas, ya que EcoVadis también ha destacado el enfoque de la empresa en la implementación de medidas concretas y documentadas, así como en el seguimiento del desempeño a través de indicadores clave definidos. Por ejemplo, esto incluye el aumento del uso de materiales más sostenibles, así como la reducción del consumo de materiales mediante la optimización de procesos. En conjunto, estos esfuerzos validan el arduo trabajo realizado para garantizar que las operaciones de Trust no solo sirvan a sus clientes, sino también a sus empleados y al planeta en general.
Asimismo, la calificación de EcoVadis respalda a Trust en su respuesta a la creciente demanda de los clientes de productos sostenibles y de una cadena de suministro transparente. A medida que las expectativas siguen aumentando tanto entre los consumidores como entre los socios comerciales, este logro subraya la posición de Trust como un colaborador fiable y responsable.
«Para nosotros es un verdadero orgullo obtener el reconocimiento de EcoVadis por cuarta vez consecutiva», afirma Dorothee de Backer, directora de Producto y Marketing de Trust. «Es una muestra del sólido progreso que hemos alcanzado, especialmente en el ámbito medioambiental, con nuestro enfoque continuo a favor de lograr que nuestros productos sean más duraderos y sostenibles. Este logro no habría sido posible sin una estrecha colaboración y sin nuestros socios en la cadena de suministro, y nos inspira a seguir haciendo lo correcto: invertir en sostenibilidad y seguir adelante en este apasionante viaje».
La compañía mantiene su enfoque en impulsar nuevas mejoras; las prioridades para el próximo año incluyen la reducción del consumo energético, la ampliación de los esfuerzos para reducir la huella de carbono en los grupos de productos de gran volumen y la actualización de las políticas a fin de que estén en consonancia con las normativas y los estándares industriales que van cambiando. Además, Trust también seguirá trabajando para armonizar sus informes de sostenibilidad con un marco de presentación de informes de reconocido prestigio.
Conseguir la medalla de oro de EcoVadis durante cuatro años consecutivos es un gran hito que demuestra el importante progreso derivado de que Trust dé prioridad al planeta y a las personas, año tras año.
Para obtener información adicional sobre las iniciativas de sostenibilidad que lleva a cabo Trust, visite nuestro sitio web.
La entrada Trust mantiene su liderazgo en sostenibilidad al ser premiada con la medalla de oro EcoVadis por cuarto año consecutivo apareció primero en ZIRAN Comunicación.]]>