1<?php
2/**
3 * REST API: WP_REST_Response class
4 *
5 * @package WordPress
6 * @subpackage REST_API
7 * @since 4.4.0
8 */
9
10/**
11 * Core class used to implement a REST response object.
12 *
13 * @since 4.4.0
14 *
15 * @see WP_HTTP_Response
16 */
17class WP_REST_Response extends WP_HTTP_Response {
18
19 /**
20 * Links related to the response.
21 *
22 * @since 4.4.0
23 * @var array
24 */
25 protected $links = array();
26
27 /**
28 * The route that was to create the response.
29 *
30 * @since 4.4.0
31 * @var string
32 */
33 protected $matched_route = '';
34
35 /**
36 * The handler that was used to create the response.
37 *
38 * @since 4.4.0
39 * @var null|array
40 */
41 protected $matched_handler = null;
42
43 /**
44 * Adds a link to the response.
45 *
46 * {@internal The $rel parameter is first, as this looks nicer when sending multiple.}
47 *
48 * @since 4.4.0
49 *
50 * @link https://tools.ietf.org/html/rfc5988
51 * @link https://www.iana.org/assignments/link-relations/link-relations.xml
52 *
53 * @param string $rel Link relation. Either an IANA registered type,
54 * or an absolute URL.
55 * @param string $href Target URI for the link.
56 * @param array $attributes Optional. Link parameters to send along with the URL. Default empty array.
57 */
58 public function add_link( $rel, $href, $attributes = array() ) {
59 if ( empty( $this->links[ $rel ] ) ) {
60 $this->links[ $rel ] = array();
61 }
62
63 if ( isset( $attributes['href'] ) ) {
64 // Remove the href attribute, as it's used for the main URL.
65 unset( $attributes['href'] );
66 }
67
68 $this->links[ $rel ][] = array(
69 'href' => $href,
70 'attributes' => $attributes,
71 );
72 }
73
74 /**
75 * Removes a link from the response.
76 *
77 * @since 4.4.0
78 *
79 * @param string $rel Link relation. Either an IANA registered type, or an absolute URL.
80 * @param string|null $href Optional. Only remove links for the relation matching the given href.
81 * Default null.
82 */
83 public function remove_link( $rel, $href = null ) {
84 if ( ! isset( $this->links[ $rel ] ) ) {
85 return;
86 }
87
88 if ( $href ) {
89 $this->links[ $rel ] = wp_list_filter( $this->links[ $rel ], array( 'href' => $href ), 'NOT' );
90 } else {
91 $this->links[ $rel ] = array();
92 }
93
94 if ( ! $this->links[ $rel ] ) {
95 unset( $this->links[ $rel ] );
96 }
97 }
98
99 /**
100 * Adds multiple links to the response.
101 *
102 * Link data should be an associative array with link relation as the key.
103 * The value can either be an associative array of link attributes
104 * (including `href` with the URL for the response), or a list of these
105 * associative arrays.
106 *
107 * @since 4.4.0
108 *
109 * @param array $links Map of link relation to list of links.
110 */
111 public function add_links( $links ) {
112 foreach ( $links as $rel => $set ) {
113 // If it's a single link, wrap with an array for consistent handling.
114 if ( isset( $set['href'] ) ) {
115 $set = array( $set );
116 }
117
118 foreach ( $set as $attributes ) {
119 $this->add_link( $rel, $attributes['href'], $attributes );
120 }
121 }
122 }
123
124 /**
125 * Retrieves links for the response.
126 *
127 * @since 4.4.0
128 *
129 * @return array List of links.
130 */
131 public function get_links() {
132 return $this->links;
133 }
134
135 /**
136 * Sets a single link header.
137 *
138 * {@internal The $rel parameter is first, as this looks nicer when sending multiple.}
139 *
140 * @since 4.4.0
141 *
142 * @link https://tools.ietf.org/html/rfc5988
143 * @link https://www.iana.org/assignments/link-relations/link-relations.xml
144 *
145 * @param string $rel Link relation. Either an IANA registered type, or an absolute URL.
146 * @param string $link Target IRI for the link.
147 * @param array $other Optional. Other parameters to send, as an associative array.
148 * Default empty array.
149 */
150 public function link_header( $rel, $link, $other = array() ) {
151 $header = '<' . $link . '>; rel="' . $rel . '"';
152
153 foreach ( $other as $key => $value ) {
154 if ( 'title' === $key ) {
155 $value = '"' . $value . '"';
156 }
157
158 $header .= '; ' . $key . '=' . $value;
159 }
160 $this->header( 'Link', $header, false );
161 }
162
163 /**
164 * Retrieves the route that was used.
165 *
166 * @since 4.4.0
167 *
168 * @return string The matched route.
169 */
170 public function get_matched_route() {
171 return $this->matched_route;
172 }
173
174 /**
175 * Sets the route (regex for path) that caused the response.
176 *
177 * @since 4.4.0
178 *
179 * @param string $route Route name.
180 */
181 public function set_matched_route( $route ) {
182 $this->matched_route = $route;
183 }
184
185 /**
186 * Retrieves the handler that was used to generate the response.
187 *
188 * @since 4.4.0
189 *
190 * @return null|array The handler that was used to create the response.
191 */
192 public function get_matched_handler() {
193 return $this->matched_handler;
194 }
195
196 /**
197 * Sets the handler that was responsible for generating the response.
198 *
199 * @since 4.4.0
200 *
201 * @param array $handler The matched handler.
202 */
203 public function set_matched_handler( $handler ) {
204 $this->matched_handler = $handler;
205 }
206
207 /**
208 * Checks if the response is an error, i.e. >= 400 response code.
209 *
210 * @since 4.4.0
211 *
212 * @return bool Whether the response is an error.
213 */
214 public function is_error() {
215 return $this->get_status() >= 400;
216 }
217
218 /**
219 * Retrieves a WP_Error object from the response.
220 *
221 * @since 4.4.0
222 *
223 * @return WP_Error|null WP_Error or null on not an errored response.
224 */
225 public function as_error() {
226 if ( ! $this->is_error() ) {
227 return null;
228 }
229
230 $error = new WP_Error();
231
232 if ( is_array( $this->get_data() ) ) {
233 $data = $this->get_data();
234 $error->add( $data['code'], $data['message'], $data['data'] );
235
236 if ( ! empty( $data['additional_errors'] ) ) {
237 foreach ( $data['additional_errors'] as $err ) {
238 $error->add( $err['code'], $err['message'], $err['data'] );
239 }
240 }
241 } else {
242 $error->add( $this->get_status(), '', array( 'status' => $this->get_status() ) );
243 }
244
245 return $error;
246 }
247
248 /**
249 * Retrieves the CURIEs (compact URIs) used for relations.
250 *
251 * @since 4.5.0
252 *
253 * @return array Compact URIs.
254 */
255 public function get_curies() {
256 $curies = array(
257 array(
258 'name' => 'wp',
259 'href' => 'https://api.w.org/{rel}',
260 'templated' => true,
261 ),
262 );
263
264 /**
265 * Filters extra CURIEs available on REST API responses.
266 *
267 * CURIEs allow a shortened version of URI relations. This allows a more
268 * usable form for custom relations than using the full URI. These work
269 * similarly to how XML namespaces work.
270 *
271 * Registered CURIES need to specify a name and URI template. This will
272 * automatically transform URI relations into their shortened version.
273 * The shortened relation follows the format `{name}:{rel}`. `{rel}` in
274 * the URI template will be replaced with the `{rel}` part of the
275 * shortened relation.
276 *
277 * For example, a CURIE with name `example` and URI template
278 * `http://w.org/{rel}` would transform a `http://w.org/term` relation
279 * into `example:term`.
280 *
281 * Well-behaved clients should expand and normalize these back to their
282 * full URI relation, however some naive clients may not resolve these
283 * correctly, so adding new CURIEs may break backward compatibility.
284 *
285 * @since 4.5.0
286 *
287 * @param array $additional Additional CURIEs to register with the REST API.
288 */
289 $additional = apply_filters( 'rest_response_link_curies', array() );
290
291 return array_merge( $curies, $additional );
292 }
293}
294