These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * REST Controller |
||
4 | * |
||
5 | * This class extend `WP_REST_Controller` in order to include /batch endpoint |
||
6 | * for almost all endpoints in WooCommerce REST API. |
||
7 | * |
||
8 | * It's required to follow "Controller Classes" guide before extending this class: |
||
9 | * <https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/> |
||
10 | * |
||
11 | * NOTE THAT ONLY CODE RELEVANT FOR MOST ENDPOINTS SHOULD BE INCLUDED INTO THIS CLASS. |
||
12 | * If necessary extend this class and create new abstract classes like `WC_REST_CRUD_Controller` or `WC_REST_Terms_Controller`. |
||
13 | * |
||
14 | * @class WC_REST_Controller |
||
15 | * @package WooCommerce/Abstracts |
||
16 | * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/ |
||
17 | */ |
||
18 | |||
19 | 1 | if ( ! defined( 'ABSPATH' ) ) { |
|
20 | exit; |
||
21 | } |
||
22 | |||
23 | /** |
||
24 | * Abstract Rest Controller Class |
||
25 | * |
||
26 | * @package WooCommerce/Abstracts |
||
27 | * @extends WP_REST_Controller |
||
28 | * @version 2.6.0 |
||
29 | */ |
||
30 | abstract class WC_REST_Controller extends WP_REST_Controller { |
||
31 | |||
32 | /** |
||
33 | * Endpoint namespace. |
||
34 | * |
||
35 | * @var string |
||
36 | */ |
||
37 | protected $namespace = 'wc/v1'; |
||
38 | |||
39 | /** |
||
40 | * Route base. |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $rest_base = ''; |
||
45 | |||
46 | /** |
||
47 | * Add the schema from additional fields to an schema array. |
||
48 | * |
||
49 | * The type of object is inferred from the passed schema. |
||
50 | * |
||
51 | * @param array $schema Schema array. |
||
52 | * |
||
53 | * @return array |
||
54 | */ |
||
55 | 426 | protected function add_additional_fields_schema( $schema ) { |
|
56 | 426 | if ( empty( $schema['title'] ) ) { |
|
57 | return $schema; |
||
58 | } |
||
59 | |||
60 | /** |
||
61 | * Can't use $this->get_object_type otherwise we cause an inf loop. |
||
62 | */ |
||
63 | 426 | $object_type = $schema['title']; |
|
64 | |||
65 | 426 | $additional_fields = $this->get_additional_fields( $object_type ); |
|
66 | |||
67 | 426 | foreach ( $additional_fields as $field_name => $field_options ) { |
|
68 | if ( ! $field_options['schema'] ) { |
||
69 | continue; |
||
70 | } |
||
71 | |||
72 | $schema['properties'][ $field_name ] = $field_options['schema']; |
||
73 | } |
||
74 | |||
75 | 426 | $schema['properties'] = apply_filters( 'woocommerce_rest_' . $object_type . '_schema', $schema['properties'] ); |
|
76 | |||
77 | 426 | return $schema; |
|
78 | } |
||
79 | |||
80 | /** |
||
81 | * Get normalized rest base. |
||
82 | * |
||
83 | * @return string |
||
84 | */ |
||
85 | 14 | protected function get_normalized_rest_base() { |
|
86 | 14 | return preg_replace( '/\(.*\)\//i', '', $this->rest_base ); |
|
87 | } |
||
88 | |||
89 | /** |
||
90 | * Check batch limit. |
||
91 | * |
||
92 | * @param array $items Request items. |
||
93 | * @return bool|WP_Error |
||
94 | */ |
||
95 | 14 | protected function check_batch_limit( $items ) { |
|
96 | 14 | $limit = apply_filters( 'woocommerce_rest_batch_items_limit', 100, $this->get_normalized_rest_base() ); |
|
97 | 14 | $total = 0; |
|
98 | |||
99 | 14 | if ( ! empty( $items['create'] ) ) { |
|
100 | 10 | $total += count( $items['create'] ); |
|
101 | } |
||
102 | |||
103 | 14 | if ( ! empty( $items['update'] ) ) { |
|
104 | 14 | $total += count( $items['update'] ); |
|
105 | } |
||
106 | |||
107 | 14 | if ( ! empty( $items['delete'] ) ) { |
|
108 | 12 | $total += count( $items['delete'] ); |
|
109 | } |
||
110 | |||
111 | 14 | if ( $total > $limit ) { |
|
112 | /* translators: %s: items limit */ |
||
113 | return new WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) ); |
||
114 | } |
||
115 | |||
116 | 14 | return true; |
|
117 | } |
||
118 | |||
119 | /** |
||
120 | * Bulk create, update and delete items. |
||
121 | * |
||
122 | * @param WP_REST_Request $request Full details about the request. |
||
123 | * @return array Of WP_Error or WP_REST_Response. |
||
124 | */ |
||
125 | 14 | public function batch_items( $request ) { |
|
126 | /** |
||
127 | * REST Server |
||
128 | * |
||
129 | * @var WP_REST_Server $wp_rest_server |
||
130 | */ |
||
131 | global $wp_rest_server; |
||
132 | |||
133 | // Get the request params. |
||
134 | 14 | $items = array_filter( $request->get_params() ); |
|
135 | 14 | $response = array(); |
|
136 | |||
137 | // Check batch limit. |
||
138 | 14 | $limit = $this->check_batch_limit( $items ); |
|
139 | 14 | if ( is_wp_error( $limit ) ) { |
|
140 | return $limit; |
||
141 | } |
||
142 | |||
143 | 14 | if ( ! empty( $items['create'] ) ) { |
|
144 | 10 | foreach ( $items['create'] as $item ) { |
|
145 | 10 | $_item = new WP_REST_Request( 'POST' ); |
|
146 | |||
147 | // Default parameters. |
||
148 | 10 | $defaults = array(); |
|
149 | 10 | $schema = $this->get_public_item_schema(); |
|
150 | 10 | foreach ( $schema['properties'] as $arg => $options ) { |
|
151 | 10 | if ( isset( $options['default'] ) ) { |
|
152 | 8 | $defaults[ $arg ] = $options['default']; |
|
153 | } |
||
154 | } |
||
155 | 10 | $_item->set_default_params( $defaults ); |
|
156 | |||
157 | // Set request parameters. |
||
158 | 10 | $_item->set_body_params( $item ); |
|
159 | 10 | $_response = $this->create_item( $_item ); |
|
160 | |||
161 | 10 | View Code Duplication | if ( is_wp_error( $_response ) ) { |
162 | $response['create'][] = array( |
||
163 | 'id' => 0, |
||
164 | 'error' => array( |
||
165 | 'code' => $_response->get_error_code(), |
||
166 | 'message' => $_response->get_error_message(), |
||
167 | 'data' => $_response->get_error_data(), |
||
168 | ), |
||
169 | ); |
||
170 | } else { |
||
171 | 10 | $response['create'][] = $wp_rest_server->response_to_data( $_response, '' ); |
|
172 | } |
||
173 | } |
||
174 | } |
||
175 | |||
176 | 14 | if ( ! empty( $items['update'] ) ) { |
|
177 | 14 | foreach ( $items['update'] as $item ) { |
|
178 | 14 | $_item = new WP_REST_Request( 'PUT' ); |
|
179 | 14 | $_item->set_body_params( $item ); |
|
180 | 14 | $_response = $this->update_item( $_item ); |
|
181 | |||
182 | 14 | View Code Duplication | if ( is_wp_error( $_response ) ) { |
183 | $response['update'][] = array( |
||
184 | 'id' => $item['id'], |
||
185 | 'error' => array( |
||
186 | 'code' => $_response->get_error_code(), |
||
187 | 'message' => $_response->get_error_message(), |
||
188 | 'data' => $_response->get_error_data(), |
||
189 | ), |
||
190 | ); |
||
191 | } else { |
||
192 | 14 | $response['update'][] = $wp_rest_server->response_to_data( $_response, '' ); |
|
193 | } |
||
194 | } |
||
195 | } |
||
196 | |||
197 | 14 | if ( ! empty( $items['delete'] ) ) { |
|
198 | 12 | foreach ( $items['delete'] as $id ) { |
|
199 | 12 | $id = (int) $id; |
|
200 | |||
201 | 12 | if ( 0 === $id ) { |
|
202 | continue; |
||
203 | } |
||
204 | |||
205 | 12 | $_item = new WP_REST_Request( 'DELETE' ); |
|
206 | 12 | $_item->set_query_params( |
|
207 | array( |
||
208 | 12 | 'id' => $id, |
|
209 | 'force' => true, |
||
210 | ) |
||
211 | ); |
||
212 | 12 | $_response = $this->delete_item( $_item ); |
|
213 | |||
214 | 12 | View Code Duplication | if ( is_wp_error( $_response ) ) { |
215 | $response['delete'][] = array( |
||
216 | 'id' => $id, |
||
217 | 'error' => array( |
||
218 | 'code' => $_response->get_error_code(), |
||
219 | 'message' => $_response->get_error_message(), |
||
220 | 'data' => $_response->get_error_data(), |
||
221 | ), |
||
222 | ); |
||
223 | } else { |
||
224 | 12 | $response['delete'][] = $wp_rest_server->response_to_data( $_response, '' ); |
|
225 | } |
||
226 | } |
||
227 | } |
||
228 | |||
229 | 14 | return $response; |
|
230 | } |
||
231 | |||
232 | /** |
||
233 | * Validate a text value for a text based setting. |
||
234 | * |
||
235 | * @since 3.0.0 |
||
236 | * @param string $value Value. |
||
237 | * @param array $setting Setting. |
||
238 | * @return string |
||
239 | */ |
||
240 | 14 | public function validate_setting_text_field( $value, $setting ) { |
|
241 | 14 | $value = is_null( $value ) ? '' : $value; |
|
242 | 14 | return wp_kses_post( trim( stripslashes( $value ) ) ); |
|
243 | } |
||
244 | |||
245 | /** |
||
246 | * Validate select based settings. |
||
247 | * |
||
248 | * @since 3.0.0 |
||
249 | * @param string $value Value. |
||
250 | * @param array $setting Setting. |
||
251 | * @return string|WP_Error |
||
252 | */ |
||
253 | 14 | public function validate_setting_select_field( $value, $setting ) { |
|
254 | 14 | if ( array_key_exists( $value, $setting['options'] ) ) { |
|
255 | 14 | return $value; |
|
256 | } else { |
||
257 | 8 | return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); |
|
258 | } |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Validate multiselect based settings. |
||
263 | * |
||
264 | * @since 3.0.0 |
||
265 | * @param array $values Values. |
||
266 | * @param array $setting Setting. |
||
267 | * @return array|WP_Error |
||
268 | */ |
||
269 | 2 | public function validate_setting_multiselect_field( $values, $setting ) { |
|
270 | 2 | if ( empty( $values ) ) { |
|
271 | return array(); |
||
272 | } |
||
273 | |||
274 | 2 | if ( ! is_array( $values ) ) { |
|
275 | return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
276 | } |
||
277 | |||
278 | 2 | $final_values = array(); |
|
279 | 2 | foreach ( $values as $value ) { |
|
280 | 2 | if ( array_key_exists( $value, $setting['options'] ) ) { |
|
281 | 2 | $final_values[] = $value; |
|
282 | } |
||
283 | } |
||
284 | |||
285 | 2 | return $final_values; |
|
286 | } |
||
287 | |||
288 | /** |
||
289 | * Validate image_width based settings. |
||
290 | * |
||
291 | * @since 3.0.0 |
||
292 | * @param array $values Values. |
||
293 | * @param array $setting Setting. |
||
294 | * @return string|WP_Error |
||
295 | */ |
||
296 | public function validate_setting_image_width_field( $values, $setting ) { |
||
297 | if ( ! is_array( $values ) ) { |
||
298 | return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
299 | } |
||
300 | |||
301 | $current = $setting['value']; |
||
302 | if ( isset( $values['width'] ) ) { |
||
303 | $current['width'] = intval( $values['width'] ); |
||
304 | } |
||
305 | if ( isset( $values['height'] ) ) { |
||
306 | $current['height'] = intval( $values['height'] ); |
||
307 | } |
||
308 | if ( isset( $values['crop'] ) ) { |
||
309 | $current['crop'] = (bool) $values['crop']; |
||
310 | } |
||
311 | return $current; |
||
312 | } |
||
313 | |||
314 | /** |
||
315 | * Validate radio based settings. |
||
316 | * |
||
317 | * @since 3.0.0 |
||
318 | * @param string $value Value. |
||
319 | * @param array $setting Setting. |
||
320 | * @return string|WP_Error |
||
321 | */ |
||
322 | 2 | public function validate_setting_radio_field( $value, $setting ) { |
|
323 | 2 | return $this->validate_setting_select_field( $value, $setting ); |
|
324 | } |
||
325 | |||
326 | /** |
||
327 | * Validate checkbox based settings. |
||
328 | * |
||
329 | * @since 3.0.0 |
||
330 | * @param string $value Value. |
||
331 | * @param array $setting Setting. |
||
332 | * @return string|WP_Error |
||
333 | */ |
||
334 | 4 | public function validate_setting_checkbox_field( $value, $setting ) { |
|
335 | 4 | if ( in_array( $value, array( 'yes', 'no' ) ) ) { |
|
336 | 4 | return $value; |
|
337 | 2 | } elseif ( empty( $value ) ) { |
|
338 | $value = isset( $setting['default'] ) ? $setting['default'] : 'no'; |
||
339 | return $value; |
||
340 | } else { |
||
341 | 2 | return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); |
|
342 | } |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Validate textarea based settings. |
||
347 | * |
||
348 | * @since 3.0.0 |
||
349 | * @param string $value Value. |
||
350 | * @param array $setting Setting. |
||
351 | * @return string |
||
352 | */ |
||
353 | View Code Duplication | public function validate_setting_textarea_field( $value, $setting ) { |
|
0 ignored issues
–
show
|
|||
354 | $value = is_null( $value ) ? '' : $value; |
||
355 | return wp_kses( |
||
356 | trim( stripslashes( $value ) ), |
||
357 | array_merge( |
||
358 | array( |
||
359 | 'iframe' => array( |
||
360 | 'src' => true, |
||
361 | 'style' => true, |
||
362 | 'id' => true, |
||
363 | 'class' => true, |
||
364 | ), |
||
365 | ), |
||
366 | wp_kses_allowed_html( 'post' ) |
||
367 | ) |
||
368 | ); |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Add meta query. |
||
373 | * |
||
374 | * @since 3.0.0 |
||
375 | * @param array $args Query args. |
||
376 | * @param array $meta_query Meta query. |
||
377 | * @return array |
||
378 | */ |
||
379 | protected function add_meta_query( $args, $meta_query ) { |
||
380 | if ( empty( $args['meta_query'] ) ) { |
||
381 | $args['meta_query'] = array(); |
||
0 ignored issues
–
show
|
|||
382 | } |
||
383 | |||
384 | $args['meta_query'][] = $meta_query; |
||
385 | |||
386 | return $args['meta_query']; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Get the batch schema, conforming to JSON Schema. |
||
391 | * |
||
392 | * @return array |
||
393 | */ |
||
394 | public function get_public_batch_schema() { |
||
395 | $schema = array( |
||
396 | '$schema' => 'http://json-schema.org/draft-04/schema#', |
||
397 | 'title' => 'batch', |
||
398 | 'type' => 'object', |
||
399 | 'properties' => array( |
||
400 | 'create' => array( |
||
401 | 'description' => __( 'List of created resources.', 'woocommerce' ), |
||
402 | 'type' => 'array', |
||
403 | 'context' => array( 'view', 'edit' ), |
||
404 | 'items' => array( |
||
405 | 'type' => 'object', |
||
406 | ), |
||
407 | ), |
||
408 | 'update' => array( |
||
409 | 'description' => __( 'List of updated resources.', 'woocommerce' ), |
||
410 | 'type' => 'array', |
||
411 | 'context' => array( 'view', 'edit' ), |
||
412 | 'items' => array( |
||
413 | 'type' => 'object', |
||
414 | ), |
||
415 | ), |
||
416 | 'delete' => array( |
||
417 | 'description' => __( 'List of delete resources.', 'woocommerce' ), |
||
418 | 'type' => 'array', |
||
419 | 'context' => array( 'view', 'edit' ), |
||
420 | 'items' => array( |
||
421 | 'type' => 'integer', |
||
422 | ), |
||
423 | ), |
||
424 | ), |
||
425 | ); |
||
426 | |||
427 | return $schema; |
||
428 | } |
||
429 | |||
430 | /** |
||
431 | * Gets an array of fields to be included on the response. |
||
432 | * Included fields are based on item schema and `_fields=` request argument. |
||
433 | * Introduced to support WordPress 4.9.6 changes. |
||
434 | * |
||
435 | * @since 3.5.0 |
||
436 | * @param WP_REST_Request $request Full details about the request. |
||
437 | * @return array Fields to be included in the response. |
||
438 | */ |
||
439 | 194 | public function get_fields_for_response( $request ) { |
|
440 | 194 | $schema = $this->get_item_schema(); |
|
441 | 194 | $fields = isset( $schema['properties'] ) ? array_keys( $schema['properties'] ) : array(); |
|
442 | |||
443 | 194 | $additional_fields = $this->get_additional_fields(); |
|
444 | 194 | foreach ( $additional_fields as $field_name => $field_options ) { |
|
445 | // For back-compat, include any field with an empty schema |
||
446 | // because it won't be present in $this->get_item_schema(). |
||
447 | if ( is_null( $field_options['schema'] ) ) { |
||
448 | $fields[] = $field_name; |
||
449 | } |
||
450 | } |
||
451 | |||
452 | 194 | if ( ! isset( $request['_fields'] ) ) { |
|
453 | 194 | return $fields; |
|
454 | } |
||
455 | 3 | $requested_fields = is_array( $request['_fields'] ) ? $request['_fields'] : preg_split( '/[\s,]+/', $request['_fields'] ); |
|
456 | 3 | if ( 0 === count( $requested_fields ) ) { |
|
457 | return $fields; |
||
458 | } |
||
459 | // Trim off outside whitespace from the comma delimited list. |
||
460 | 3 | $requested_fields = array_map( 'trim', $requested_fields ); |
|
461 | // Always persist 'id', because it can be needed for add_additional_fields_to_object(). |
||
462 | 3 | if ( in_array( 'id', $fields, true ) ) { |
|
463 | 3 | $requested_fields[] = 'id'; |
|
464 | } |
||
465 | 3 | return array_intersect( $fields, $requested_fields ); |
|
466 | } |
||
467 | } |
||
468 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.