PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.9.4
Jetpack – WP Security, Backup, Speed, & Growth v8.9.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
← All changes | class.json-api.php +135 -541 12.4.28.9.4 View file →
@@ -1,194 +1,52 @@
1 -<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 -/**
3 - * Jetpack JSON API.
4 - *
5 - * @package automattic/jetpack
6 - */
1 +<?php
7 2
8 -use Automattic\Jetpack\Status;
3 +defined( 'WPCOM_JSON_API__DEBUG' ) or define( 'WPCOM_JSON_API__DEBUG', false );
9 4
10 -if ( ! defined( 'WPCOM_JSON_API__DEBUG' ) ) {
11 - define( 'WPCOM_JSON_API__DEBUG', false );
12 -}
5 +require_once dirname( __FILE__ ) . '/sal/class.json-api-platform.php';
13 6
14 -require_once __DIR__ . '/sal/class.json-api-platform.php';
15 -
16 -/**
17 - * Jetpack JSON API.
18 - */
19 7 class WPCOM_JSON_API {
20 - /**
21 - * Static instance.
22 - *
23 - * @todo This should be private.
24 - * @var self|null
25 - */
26 - public static $self = null;
8 + static $self = null;
27 9
28 - /**
29 - * Registered endpoints.
30 - *
31 - * @var WPCOM_JSON_API_Endpoint[]
32 - */
33 10 public $endpoints = array();
34 11
35 - /**
36 - * Endpoint being processed.
37 - *
38 - * @var WPCOM_JSON_API_Endpoint
39 - */
40 - public $endpoint = null;
41 -
42 - /**
43 - * Token details.
44 - *
45 - * @var array
46 - */
47 12 public $token_details = array();
48 13
49 - /**
50 - * Request HTTP method.
51 - *
52 - * @var string
53 - */
54 - public $method = '';
55 -
56 - /**
57 - * Request URL.
58 - *
59 - * @var string
60 - */
61 - public $url = '';
62 -
63 - /**
64 - * Path part of the request URL.
65 - *
66 - * @var string
67 - */
68 - public $path = '';
69 -
70 - /**
71 - * Version extracted from the request URL.
72 - *
73 - * @var string|null
74 - */
75 - public $version = null;
76 -
77 - /**
78 - * Parsed query data.
79 - *
80 - * @var array
81 - */
82 - public $query = array();
83 -
84 - /**
85 - * Post body, if the request is a POST.
86 - *
87 - * @var string|null
88 - */
89 - public $post_body = null;
90 -
91 - /**
92 - * Copy of `$_FILES` if the request is a POST.
93 - *
94 - * @var null|array
95 - */
96 - public $files = null;
97 -
98 - /**
99 - * Content type of the request.
100 - *
101 - * @var string|null
102 - */
14 + public $method = '';
15 + public $url = '';
16 + public $path = '';
17 + public $version = null;
18 + public $query = array();
19 + public $post_body = null;
20 + public $files = null;
103 21 public $content_type = null;
22 + public $accept = '';
104 23
105 - /**
106 - * Value of `$_SERVER['HTTP_ACCEPT']`, if any
107 - *
108 - * @var string
109 - */
110 - public $accept = '';
111 -
112 - /**
113 - * Value of `$_SERVER['HTTPS']`, or "--UNset--" if unset.
114 - *
115 - * @var string
116 - */
117 - public $_server_https; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
118 -
119 - /**
120 - * Whether to exit after serving a response.
121 - *
122 - * @var bool
123 - */
124 - public $exit = true;
125 -
126 - /**
127 - * Public API scheme.
128 - *
129 - * @var string
130 - */
24 + public $_server_https;
25 + public $exit = true;
131 26 public $public_api_scheme = 'https';
132 27
133 - /**
134 - * Output status code.
135 - *
136 - * @var int
137 - */
138 28 public $output_status_code = 200;
139 29
140 - /**
141 - * Trapped error.
142 - *
143 - * @var null|array
144 - */
145 30 public $trapped_error = null;
31 + public $did_output = false;
146 32
147 - /**
148 - * Whether output has been done.
149 - *
150 - * @var bool
151 - */
152 - public $did_output = false;
153 -
154 - /**
155 - * Extra HTTP headers.
156 - *
157 - * @var string
158 - */
159 33 public $extra_headers = array();
160 34
161 - /**
162 - * AMP source origin.
163 - *
164 - * @var string
165 - */
166 35 public $amp_source_origin = null;
167 36
168 37 /**
169 - * Initialize.
170 - *
171 - * @param string|null $method As for `$this->setup_inputs()`.
172 - * @param string|null $url As for `$this->setup_inputs()`.
173 - * @param string|null $post_body As for `$this->setup_inputs()`.
174 38 * @return WPCOM_JSON_API instance
175 39 */
176 - public static function init( $method = null, $url = null, $post_body = null ) {
40 + static function init( $method = null, $url = null, $post_body = null ) {
177 41 if ( ! self::$self ) {
178 - self::$self = new static( $method, $url, $post_body );
42 + $class = function_exists( 'get_called_class' ) ? get_called_class() : __CLASS__; // phpcs:ignore PHPCompatibility.PHP.NewFunctions.get_called_classFound
43 + self::$self = new $class( $method, $url, $post_body );
179 44 }
180 45 return self::$self;
181 46 }
182 47
183 - /**
184 - * Add an endpoint.
185 - *
186 - * @param WPCOM_JSON_API_Endpoint $endpoint Endpoint to add.
187 - */
188 - public function add( WPCOM_JSON_API_Endpoint $endpoint ) {
189 - // @todo Determine if anything depends on this being serialized rather than e.g. JSON.
190 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Legacy, possibly depended on elsewhere.
48 + function add( WPCOM_JSON_API_Endpoint $endpoint ) {
191 49 $path_versions = serialize(
192 50 array(
193 51 $endpoint->path,
194 52 $endpoint->min_version,
@@ -200,15 +58,9 @@
200 58 }
201 59 $this->endpoints[ $path_versions ][ $endpoint->method ] = $endpoint;
202 60 }
203 61
204 - /**
205 - * Determine if a string is truthy.
206 - *
207 - * @param string $value "1", "t", and "true" (case insensitive) are falsey, everything else isn't.
208 - * @return bool
209 - */
210 - public static function is_truthy( $value ) {
62 + static function is_truthy( $value ) {
211 63 switch ( strtolower( (string) $value ) ) {
212 64 case '1':
213 65 case 't':
214 66 case 'true':
@@ -217,15 +69,9 @@
217 69
218 70 return false;
219 71 }
220 72
221 - /**
222 - * Determine if a string is falsey.
223 - *
224 - * @param string $value "0", "f", and "false" (case insensitive) are falsey, everything else isn't.
225 - * @return bool
226 - */
227 - public static function is_falsy( $value ) {
73 + static function is_falsy( $value ) {
228 74 switch ( strtolower( (string) $value ) ) {
229 75 case '0':
230 76 case 'f':
231 77 case 'false':
@@ -234,36 +80,20 @@
234 80
235 81 return false;
236 82 }
237 83
238 - /**
239 - * Constructor.
240 - *
241 - * @todo This should be private.
242 - * @param string|null $method As for `$this->setup_inputs()`.
243 - * @param string|null $url As for `$this->setup_inputs()`.
244 - * @param string|null $post_body As for `$this->setup_inputs()`.
245 - */
246 - public function __construct( $method = null, $url = null, $post_body = null ) {
247 - $this->setup_inputs( $method, $url, $post_body );
84 + function __construct( ...$args ) {
85 + call_user_func_array( array( $this, 'setup_inputs' ), $args );
248 86 }
249 87
250 - /**
251 - * Setup inputs.
252 - *
253 - * @param string|null $method Request HTTP method. Fetched from `$_SERVER` if null.
254 - * @param string|null $url URL requested. Determined from `$_SERVER` if null.
255 - * @param string|null $post_body POST body. Read from `php://input` if null and method is POST.
256 - */
257 - public function setup_inputs( $method = null, $url = null, $post_body = null ) {
258 - if ( $method === null ) {
259 - $this->method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( filter_var( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : '';
88 + function setup_inputs( $method = null, $url = null, $post_body = null ) {
89 + if ( is_null( $method ) ) {
90 + $this->method = strtoupper( $_SERVER['REQUEST_METHOD'] );
260 91 } else {
261 92 $this->method = strtoupper( $method );
262 93 }
263 - if ( $url === null ) {
264 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the esc_url_raw.
265 - $this->url = esc_url_raw( set_url_scheme( 'http://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) );
94 + if ( is_null( $url ) ) {
95 + $this->url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
266 96 } else {
267 97 $this->url = $url;
268 98 }
269 99
@@ -275,20 +105,20 @@
275 105 if ( ! empty( $parsed['query'] ) ) {
276 106 wp_parse_str( $parsed['query'], $this->query );
277 107 }
278 108
279 - if ( ! empty( $_SERVER['HTTP_ACCEPT'] ) ) {
280 - $this->accept = filter_var( wp_unslash( $_SERVER['HTTP_ACCEPT'] ) );
109 + if ( isset( $_SERVER['HTTP_ACCEPT'] ) && $_SERVER['HTTP_ACCEPT'] ) {
110 + $this->accept = $_SERVER['HTTP_ACCEPT'];
281 111 }
282 112
283 113 if ( 'POST' === $this->method ) {
284 - if ( $post_body === null ) {
114 + if ( is_null( $post_body ) ) {
285 115 $this->post_body = file_get_contents( 'php://input' );
286 116
287 - if ( ! empty( $_SERVER['HTTP_CONTENT_TYPE'] ) ) {
288 - $this->content_type = filter_var( wp_unslash( $_SERVER['HTTP_CONTENT_TYPE'] ) );
289 - } elseif ( ! empty( $_SERVER['CONTENT_TYPE'] ) ) {
290 - $this->content_type = filter_var( wp_unslash( $_SERVER['CONTENT_TYPE'] ) );
117 + if ( isset( $_SERVER['HTTP_CONTENT_TYPE'] ) && $_SERVER['HTTP_CONTENT_TYPE'] ) {
118 + $this->content_type = $_SERVER['HTTP_CONTENT_TYPE'];
119 + } elseif ( isset( $_SERVER['CONTENT_TYPE'] ) && $_SERVER['CONTENT_TYPE'] ) {
120 + $this->content_type = $_SERVER['CONTENT_TYPE'];
291 121 } elseif ( '{' === $this->post_body[0] ) {
292 122 $this->content_type = 'application/json';
293 123 } else {
294 124 $this->content_type = 'application/x-www-form-urlencoded';
@@ -294,9 +124,8 @@
294 124 $this->content_type = 'application/x-www-form-urlencoded';
295 125 }
296 126
297 127 if ( 0 === strpos( strtolower( $this->content_type ), 'multipart/' ) ) {
298 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
299 128 $this->post_body = http_build_query( stripslashes_deep( $_POST ) );
300 129 $this->files = $_FILES;
301 130 $this->content_type = 'multipart/form-data';
302 131 }
@@ -301,9 +130,9 @@
301 130 $this->content_type = 'multipart/form-data';
302 131 }
303 132 } else {
304 133 $this->post_body = $post_body;
305 - $this->content_type = isset( $this->post_body[0] ) && '{' === $this->post_body[0] ? 'application/json' : 'application/x-www-form-urlencoded';
134 + $this->content_type = '{' === isset( $this->post_body[0] ) && $this->post_body[0] ? 'application/json' : 'application/x-www-form-urlencoded';
306 135 }
307 136 } else {
308 137 $this->post_body = null;
309 138 $this->content_type = null;
@@ -308,69 +137,23 @@
308 137 $this->post_body = null;
309 138 $this->content_type = null;
310 139 }
311 140
312 - $this->_server_https = array_key_exists( 'HTTPS', $_SERVER ) ? filter_var( wp_unslash( $_SERVER['HTTPS'] ) ) : '--UNset--';
141 + $this->_server_https = array_key_exists( 'HTTPS', $_SERVER ) ? $_SERVER['HTTPS'] : '--UNset--';
313 142 }
314 143
315 - /**
316 - * Initialize.
317 - *
318 - * @return null|WP_Error (although this implementation always returns null)
319 - */
320 - public function initialize() {
144 + function initialize() {
321 145 $this->token_details['blog_id'] = Jetpack_Options::get_option( 'id' );
322 - return null;
323 146 }
324 147
325 - /**
326 - * Checks if the current request is authorized with a blog token.
327 - * This method is overridden by a child class in WPCOM.
328 - *
329 - * @since 9.1.0
330 - *
331 - * @param boolean|int $site_id The site id.
332 - * @return boolean
333 - */
334 - public function is_jetpack_authorized_for_site( $site_id = false ) {
335 - if ( ! $this->token_details ) {
336 - return false;
337 - }
148 + function serve( $exit = true ) {
149 + ini_set( 'display_errors', false );
338 150
339 - $token_details = (object) $this->token_details;
340 -
341 - $site_in_token = (int) $token_details->blog_id;
342 -
343 - if ( $site_in_token < 1 ) {
344 - return false;
345 - }
346 -
347 - if ( $site_id && $site_in_token !== (int) $site_id ) {
348 - return false;
349 - }
350 -
351 - if ( (int) get_current_user_id() !== 0 ) {
352 - // If Jetpack blog token is used, no logged-in user should exist.
353 - return false;
354 - }
355 -
356 - return true;
357 - }
358 -
359 - /**
360 - * Serve.
361 - *
362 - * @param bool $exit Whether to exit.
363 - * @return string|null Content type (assuming it didn't exit), or null in certain error cases.
364 - */
365 - public function serve( $exit = true ) {
366 - ini_set( 'display_errors', false ); // phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted
367 -
368 151 $this->exit = (bool) $exit;
369 152
370 153 // This was causing problems with Jetpack, but is necessary for wpcom
371 154 // @see https://github.com/Automattic/jetpack/pull/2603
372 - // @see r124548-wpcom .
155 + // @see r124548-wpcom
373 156 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
374 157 add_filter( 'home_url', array( $this, 'ensure_http_scheme_of_home_url' ), 10, 3 );
375 158 }
376 159
@@ -378,9 +161,9 @@
378 161
379 162 add_filter( 'comment_edit_pre', array( $this, 'comment_edit_pre' ) );
380 163
381 164 $initialization = $this->initialize();
382 - if ( 'OPTIONS' === $this->method ) {
165 + if ( 'OPTIONS' == $this->method ) {
383 166 /**
384 167 * Fires before the page output.
385 168 * Can be used to specify custom header options.
386 169 *
@@ -396,9 +179,9 @@
396 179 $this->output_error( $initialization );
397 180 return;
398 181 }
399 182
400 - // Normalize path and extract API version.
183 + // Normalize path and extract API version
401 184 $this->path = untrailingslashit( $this->path );
402 185 preg_match( '#^/rest/v(\d+(\.\d+)*)#', $this->path, $matches );
403 186 $this->path = substr( $this->path, strlen( $matches[0] ) );
404 187 $this->version = $matches[1];
@@ -411,17 +194,17 @@
411 194
412 195 if ( $is_help ) {
413 196 $origin = get_http_origin();
414 197
415 - if ( ! empty( $origin ) && 'GET' === $this->method ) {
198 + if ( ! empty( $origin ) && 'GET' == $this->method ) {
416 199 header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
417 200 }
418 201
419 202 $this->path = substr( rtrim( $this->path, '/' ), 0, -5 );
420 - // Show help for all matching endpoints regardless of method.
203 + // Show help for all matching endpoints regardless of method
421 204 $methods = $allowed_methods;
422 205 $find_all_matching_endpoints = true;
423 - // How deep to truncate each endpoint's path to see if it matches this help request.
206 + // How deep to truncate each endpoint's path to see if it matches this help request
424 207 $depth = substr_count( $this->path, '/' ) + 1;
425 208 if ( false !== stripos( $this->accept, 'javascript' ) || false !== stripos( $this->accept, 'json' ) ) {
426 209 $help_content_type = 'json';
427 210 } else {
@@ -426,30 +209,30 @@
426 209 $help_content_type = 'json';
427 210 } else {
428 211 $help_content_type = 'html';
429 212 }
430 - } elseif ( in_array( $this->method, $allowed_methods, true ) ) {
431 - // Only serve requested method.
432 - $methods = array( $this->method );
433 - $find_all_matching_endpoints = false;
434 213 } else {
435 - // We don't allow this requested method - find matching endpoints and send 405.
436 - $methods = $allowed_methods;
437 - $find_all_matching_endpoints = true;
438 - $four_oh_five = true;
214 + if ( in_array( $this->method, $allowed_methods ) ) {
215 + // Only serve requested method
216 + $methods = array( $this->method );
217 + $find_all_matching_endpoints = false;
218 + } else {
219 + // We don't allow this requested method - find matching endpoints and send 405
220 + $methods = $allowed_methods;
221 + $find_all_matching_endpoints = true;
222 + $four_oh_five = true;
223 + }
439 224 }
440 225
441 - // Find which endpoint to serve.
226 + // Find which endpoint to serve
442 227 $found = false;
443 228 foreach ( $this->endpoints as $endpoint_path_versions => $endpoints_by_method ) {
444 - // @todo Determine if anything depends on this being serialized rather than e.g. JSON.
445 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Legacy, possibly depended on elsewhere.
446 229 $endpoint_path_versions = unserialize( $endpoint_path_versions );
447 230 $endpoint_path = $endpoint_path_versions[0];
448 231 $endpoint_min_version = $endpoint_path_versions[1];
449 232 $endpoint_max_version = $endpoint_path_versions[2];
450 233
451 - // Make sure max_version is not less than min_version.
234 + // Make sure max_version is not less than min_version
452 235 if ( version_compare( $endpoint_max_version, $endpoint_min_version, '<' ) ) {
453 236 $endpoint_max_version = $endpoint_min_version;
454 237 }
455 238
@@ -457,16 +240,16 @@
457 240 if ( ! isset( $endpoints_by_method[ $method ] ) ) {
458 241 continue;
459 242 }
460 243
461 - // Normalize.
244 + // Normalize
462 245 $endpoint_path = untrailingslashit( $endpoint_path );
463 246 if ( $is_help ) {
464 - // Truncate path at help depth.
465 - $endpoint_path = implode( '/', array_slice( explode( '/', $endpoint_path ), 0, $depth ) );
247 + // Truncate path at help depth
248 + $endpoint_path = join( '/', array_slice( explode( '/', $endpoint_path ), 0, $depth ) );
466 249 }
467 250
468 - // Generate regular expression from sprintf().
251 + // Generate regular expression from sprintf()
469 252 $endpoint_path_regex = str_replace( array( '%s', '%d' ), array( '([^/?&]+)', '(\d+)' ), $endpoint_path );
470 253
471 254 if ( ! preg_match( "#^$endpoint_path_regex\$#", $this->path, $path_pieces ) ) {
472 255 // This endpoint does not match the requested path.
@@ -482,9 +265,9 @@
482 265
483 266 if ( $find_all_matching_endpoints ) {
484 267 $matching_endpoints[] = array( $endpoints_by_method[ $method ], $path_pieces );
485 268 } else {
486 - // The method parameters are now in $path_pieces.
269 + // The method parameters are now in $path_pieces
487 270 $endpoint = $endpoints_by_method[ $method ];
488 271 break 2;
489 272 }
490 273 }
@@ -499,9 +282,9 @@
499 282 foreach ( $matching_endpoints as $matching_endpoint ) {
500 283 $allowed_methods[] = $matching_endpoint[0]->method;
501 284 }
502 285
503 - header( 'Allow: ' . strtoupper( implode( ',', array_unique( $allowed_methods ) ) ) );
286 + header( 'Allow: ' . strtoupper( join( ',', array_unique( $allowed_methods ) ) ) );
504 287 return $this->output(
505 288 405,
506 289 array(
507 290 'error' => 'not_allowed',
@@ -559,28 +342,14 @@
559 342
560 343 return $this->output( $output_status_code, $response, 'application/json', $this->extra_headers );
561 344 }
562 345
563 - /**
564 - * Process a request.
565 - *
566 - * @param WPCOM_JSON_API_Endpoint $endpoint Endpoint.
567 - * @param array $path_pieces Path pieces.
568 - * @return array|WP_Error Return value from the endpoint's callback.
569 - */
570 - public function process_request( WPCOM_JSON_API_Endpoint $endpoint, $path_pieces ) {
346 + function process_request( WPCOM_JSON_API_Endpoint $endpoint, $path_pieces ) {
571 347 $this->endpoint = $endpoint;
572 348 return call_user_func_array( array( $endpoint, 'callback' ), $path_pieces );
573 349 }
574 350
575 - /**
576 - * Output a response or error without exiting.
577 - *
578 - * @param int $status_code HTTP status code.
579 - * @param mixed $response Response data.
580 - * @param string $content_type Content type of the response.
581 - */
582 - public function output_early( $status_code, $response = null, $content_type = 'application/json' ) {
351 + function output_early( $status_code, $response = null, $content_type = 'application/json' ) {
583 352 $exit = $this->exit;
584 353 $this->exit = false;
585 354 if ( is_wp_error( $response ) ) {
586 355 $this->output_error( $response );
@@ -592,30 +361,14 @@
592 361 $this->finish_request();
593 362 }
594 363 }
595 364
596 - /**
597 - * Set output status code.
598 - *
599 - * @param int $code HTTP status code.
600 - */
601 - public function set_output_status_code( $code = 200 ) {
365 + function set_output_status_code( $code = 200 ) {
602 366 $this->output_status_code = $code;
603 367 }
604 368
605 - /**
606 - * Output a response.
607 - *
608 - * @param int $status_code HTTP status code.
609 - * @param mixed $response Response data.
610 - * @param string $content_type Content type of the response.
611 - * @param array $extra Additional HTTP headers.
612 - * @return string Content type (assuming it didn't exit).
613 - */
614 - public function output( $status_code, $response = null, $content_type = 'application/json', $extra = array() ) {
615 - $status_code = (int) $status_code;
616 -
617 - // In case output() was called before the callback returned.
369 + function output( $status_code, $response = null, $content_type = 'application/json', $extra = array() ) {
370 + // In case output() was called before the callback returned
618 371 if ( $this->did_output ) {
619 372 if ( $this->exit ) {
620 373 exit;
621 374 }
@@ -623,9 +376,9 @@
623 376 }
624 377 $this->did_output = true;
625 378
626 379 // 400s and 404s are allowed for all origins
627 - if ( 404 === $status_code || 400 === $status_code ) {
380 + if ( 404 == $status_code || 400 == $status_code ) {
628 381 header( 'Access-Control-Allow-Origin: *' );
629 382 }
630 383
631 384 /* Add headers for form submission from <amp-form/> */
@@ -633,9 +386,10 @@
633 386 header( 'Access-Control-Allow-Origin: ' . wp_unslash( $this->amp_source_origin ) );
634 387 header( 'Access-Control-Allow-Credentials: true' );
635 388 }
636 389
637 - if ( $response === null ) {
390 +
391 + if ( is_null( $response ) ) {
638 392 $response = new stdClass();
639 393 }
640 394
641 395 if ( 'text/plain' === $content_type ||
@@ -644,9 +398,9 @@
644 398 header( 'Content-Type: ' . $content_type );
645 399 foreach ( $extra as $key => $value ) {
646 400 header( "$key: $value" );
647 401 }
648 - echo $response; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
402 + echo $response;
649 403 if ( $this->exit ) {
650 404 exit;
651 405 }
652 406
@@ -689,13 +443,13 @@
689 443
690 444 if ( $callback ) {
691 445 // Mitigate Rosetta Flash [1] by setting the Content-Type-Options: nosniff header
692 446 // and by prepending the JSONP response with a JS comment.
693 - // [1] <https://blog.miki.it/2014/7/8/abusing-jsonp-with-rosetta-flash/index.html>.
694 - echo "/**/$callback("; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is JSONP output, not HTML.
447 + // [1] https://blog.miki.it/2014/7/8/abusing-jsonp-with-rosetta-flash/index.html
448 + echo "/**/$callback(";
695 449
696 450 }
697 - echo $this->json_encode( $response ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is JSON or JSONP output, not HTML.
451 + echo $this->json_encode( $response );
698 452 if ( $callback ) {
699 453 echo ');';
700 454 }
701 455
@@ -705,14 +459,8 @@
705 459
706 460 return $content_type;
707 461 }
708 462
709 - /**
710 - * Serialize an error.
711 - *
712 - * @param WP_Error $error Error.
713 - * @return array with 'status_code' and 'errors' data.
714 - */
715 463 public static function serializable_error( $error ) {
716 464
717 465 $status_code = $error->get_error_data();
718 466
@@ -727,10 +475,9 @@
727 475 'error' => $error->get_error_code(),
728 476 'message' => $error->get_error_message(),
729 477 );
730 478
731 - $additional_data = $error->get_error_data( 'additional_data' );
732 - if ( $additional_data ) {
479 + if ( $additional_data = $error->get_error_data( 'additional_data' ) ) {
733 480 $response['data'] = $additional_data;
734 481 }
735 482
736 483 return array(
@@ -738,27 +485,15 @@
738 485 'errors' => $response,
739 486 );
740 487 }
741 488
742 - /**
743 - * Output an error.
744 - *
745 - * @param WP_Error $error Error.
746 - * @return string Content type (assuming it didn't exit).
747 - */
748 - public function output_error( $error ) {
749 - $error_response = static::serializable_error( $error );
489 + function output_error( $error ) {
490 + $error_response = $this->serializable_error( $error );
750 491
751 492 return $this->output( $error_response['status_code'], $error_response['errors'] );
752 493 }
753 494
754 - /**
755 - * Filter fields in a response.
756 - *
757 - * @param array|object $response Response.
758 - * @return array|object Filtered response.
759 - */
760 - public function filter_fields( $response ) {
495 + function filter_fields( $response ) {
761 496 if ( empty( $this->query['fields'] ) || ( is_array( $response ) && ! empty( $response['error'] ) ) || ! empty( $this->endpoint->custom_fields_filtering ) ) {
762 497 return $response;
763 498 }
764 499
@@ -796,9 +531,8 @@
796 531
797 532 foreach ( $response[ $key_to_filter ] as $key => $values ) {
798 533 if ( is_object( $values ) ) {
799 534 if ( is_object( $response[ $key_to_filter ] ) ) {
800 - // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found -- False positive.
801 535 $response[ $key_to_filter ]->$key = (object) array_intersect_key( ( (array) $values ), array_flip( $fields ) );
802 536 } elseif ( is_array( $response[ $key_to_filter ] ) ) {
803 537 $response[ $key_to_filter ][ $key ] = (object) array_intersect_key( ( (array) $values ), array_flip( $fields ) );
804 538 }
@@ -821,19 +555,9 @@
821 555
822 556 return $response;
823 557 }
824 558
825 - /**
826 - * Filter for `home_url`.
827 - *
828 - * If `$original_scheme` is null, turns an https URL to http.
829 - *
830 - * @param string $url The complete home URL including scheme and path.
831 - * @param string $path Path relative to the home URL. Blank string if no path is specified.
832 - * @param string|null $original_scheme Scheme to give the home URL context. Accepts 'http', 'https', 'relative', 'rest', or null.
833 - * @return string URL.
834 - */
835 - public function ensure_http_scheme_of_home_url( $url, $path, $original_scheme ) {
559 + function ensure_http_scheme_of_home_url( $url, $path, $original_scheme ) {
836 560 if ( $original_scheme ) {
837 561 return $url;
838 562 }
839 563
@@ -839,78 +563,36 @@
839 563
840 564 return preg_replace( '#^https:#', 'http:', $url );
841 565 }
842 566
843 - /**
844 - * Decode HTML special characters in comment content.
845 - *
846 - * @param string $comment_content Comment content.
847 - * @return string
848 - */
849 - public function comment_edit_pre( $comment_content ) {
567 + function comment_edit_pre( $comment_content ) {
850 568 return htmlspecialchars_decode( $comment_content, ENT_QUOTES );
851 569 }
852 570
853 - /**
854 - * JSON encode.
855 - *
856 - * @param mixed $data Data.
857 - * @return string|false
858 - */
859 - public function json_encode( $data ) {
571 + function json_encode( $data ) {
860 572 return wp_json_encode( $data );
861 573 }
862 574
863 - /**
864 - * Test if a string ends with a string.
865 - *
866 - * @param string $haystack String to check.
867 - * @param string $needle Suffix to check.
868 - * @return bool
869 - */
870 - public function ends_with( $haystack, $needle ) {
871 - return substr( $haystack, -strlen( $needle ) ) === $needle;
575 + function ends_with( $haystack, $needle ) {
576 + return $needle === substr( $haystack, -strlen( $needle ) );
872 577 }
873 578
874 - /**
875 - * Returns the site's blog_id in the WP.com ecosystem
876 - *
877 - * @return int
878 - */
879 - public function get_blog_id_for_output() {
579 + // Returns the site's blog_id in the WP.com ecosystem
580 + function get_blog_id_for_output() {
880 581 return $this->token_details['blog_id'];
881 582 }
882 583
883 - /**
884 - * Returns the site's local blog_id.
885 - *
886 - * @param int $blog_id Blog ID.
887 - * @return int
888 - */
889 - public function get_blog_id( $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
584 + // Returns the site's local blog_id
585 + function get_blog_id( $blog_id ) {
890 586 return $GLOBALS['blog_id'];
891 587 }
892 588
893 - /**
894 - * Switch to blog and validate user.
895 - *
896 - * @param int $blog_id Blog ID.
897 - * @param bool $verify_token_for_blog Whether to verify the token.
898 - * @return int Blog ID.
899 - */
900 - public function switch_to_blog_and_validate_user( $blog_id = 0, $verify_token_for_blog = true ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
589 + function switch_to_blog_and_validate_user( $blog_id = 0, $verify_token_for_blog = true ) {
901 590 if ( $this->is_restricted_blog( $blog_id ) ) {
902 591 return new WP_Error( 'unauthorized', 'User cannot access this restricted blog', 403 );
903 592 }
904 - /**
905 - * If this is a private site we check for 2 things:
906 - * 1. In case of user based authentication, we need to check if the logged-in user has the 'read' capability.
907 - * 2. In case of site based authentication, make sure the endpoint accepts it.
908 - */
909 - if ( ( new Status() )->is_private_site() &&
910 - ! current_user_can( 'read' ) &&
911 - ! $this->endpoint->accepts_site_based_authentication()
912 - ) {
593 +
594 + if ( -1 == get_option( 'blog_public' ) && ! current_user_can( 'read' ) ) {
913 595 return new WP_Error( 'unauthorized', 'User cannot access this private blog.', 403 );
914 596 }
915 597
916 598 return $blog_id;
@@ -915,15 +597,10 @@
915 597
916 598 return $blog_id;
917 599 }
918 600
919 - /**
920 - * Returns true if the specified blog ID is a restricted blog
921 - *
922 - * @param int $blog_id Blog ID.
923 - * @return bool
924 - */
925 - public function is_restricted_blog( $blog_id ) {
601 + // Returns true if the specified blog ID is a restricted blog
602 + function is_restricted_blog( $blog_id ) {
926 603 /**
927 604 * Filters all REST API access and return a 403 unauthorized response for all Restricted blog IDs.
928 605 *
929 606 * @module json-api
@@ -932,73 +609,32 @@
932 609 *
933 610 * @param array $array Array of Blog IDs.
934 611 */
935 612 $restricted_blog_ids = apply_filters( 'wpcom_json_api_restricted_blog_ids', array() );
936 - return true === in_array( $blog_id, $restricted_blog_ids ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- I don't trust filters to return the right types.
613 + return true === in_array( $blog_id, $restricted_blog_ids );
937 614 }
938 615
939 - /**
940 - * Post like count.
941 - *
942 - * @param int $blog_id Blog ID.
943 - * @param int $post_id Post ID.
944 - * @return int
945 - */
946 - public function post_like_count( $blog_id, $post_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
616 + function post_like_count( $blog_id, $post_id ) {
947 617 return 0;
948 618 }
949 619
950 - /**
951 - * Is liked?
952 - *
953 - * @param int $blog_id Blog ID.
954 - * @param int $post_id Post ID.
955 - * @return bool
956 - */
957 - public function is_liked( $blog_id, $post_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
620 + function is_liked( $blog_id, $post_id ) {
958 621 return false;
959 622 }
960 623
961 - /**
962 - * Is reblogged?
963 - *
964 - * @param int $blog_id Blog ID.
965 - * @param int $post_id Post ID.
966 - * @return bool
967 - */
968 - public function is_reblogged( $blog_id, $post_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
624 + function is_reblogged( $blog_id, $post_id ) {
969 625 return false;
970 626 }
971 627
972 - /**
973 - * Is following?
974 - *
975 - * @param int $blog_id Blog ID.
976 - * @return bool
977 - */
978 - public function is_following( $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
628 + function is_following( $blog_id ) {
979 629 return false;
980 630 }
981 631
982 - /**
983 - * Add global ID.
984 - *
985 - * @param int $blog_id Blog ID.
986 - * @param int $post_id Post ID.
987 - * @return string
988 - */
989 - public function add_global_ID( $blog_id, $post_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
632 + function add_global_ID( $blog_id, $post_id ) {
990 633 return '';
991 634 }
992 635
993 - /**
994 - * Get avatar URL.
995 - *
996 - * @param string $email Email.
997 - * @param array $avatar_size Args for `get_avatar_url()`.
998 - * @return string|false
999 - */
1000 - public function get_avatar_url( $email, $avatar_size = null ) {
636 + function get_avatar_url( $email, $avatar_size = null ) {
1001 637 if ( function_exists( 'wpcom_get_avatar_url' ) ) {
1002 638 return null === $avatar_size
1003 639 ? wpcom_get_avatar_url( $email )
1004 640 : wpcom_get_avatar_url( $email, $avatar_size );
@@ -1009,11 +645,11 @@
1009 645 }
1010 646 }
1011 647
1012 648 /**
1013 - * Counts the number of comments on a site, including certain comment types.
649 + * Counts the number of comments on a site, excluding certain comment types.
1014 650 *
1015 - * @param int $post_id Post ID.
651 + * @param $post_id int Post ID.
1016 652 * @return array Array of counts, matching the output of https://developer.wordpress.org/reference/functions/get_comment_count/.
1017 653 */
1018 654 public function wp_count_comments( $post_id ) {
1019 655 global $wpdb;
@@ -1026,44 +662,30 @@
1026 662 'all' => 0,
1027 663 );
1028 664
1029 665 /**
1030 - * Exclude certain comment types from comment counts in the REST API.
1031 - *
1032 - * @since 6.9.0
1033 - * @deprecated 11.1
1034 - * @module json-api
1035 - *
1036 - * @param array Array of comment types to exclude (default: 'order_note', 'webhook_delivery', 'review', 'action_log')
1037 - */
1038 - $exclude = apply_filters_deprecated( 'jetpack_api_exclude_comment_types_count', array( 'order_note', 'webhook_delivery', 'review', 'action_log' ), 'jetpack-11.1', 'jetpack_api_include_comment_types_count' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1039 -
1040 - /**
1041 - * Include certain comment types in comment counts in the REST API.
1042 - * Note: the default array of comment types includes an empty string,
1043 - * to support comments posted before WP 5.5, that used an empty string as comment type.
1044 - *
1045 - * @since 11.1
1046 - * @module json-api
1047 - *
1048 - * @param array Array of comment types to include (default: 'comment', 'pingback', 'trackback')
1049 - */
1050 - $include = apply_filters(
1051 - 'jetpack_api_include_comment_types_count',
1052 - array( 'comment', 'pingback', 'trackback', '' )
666 + * Exclude certain comment types from comment counts in the REST API.
667 + *
668 + * @since 6.9.0
669 + * @module json-api
670 + *
671 + * @param array Array of comment types to exclude (default: 'order_note', 'webhook_delivery', 'review', 'action_log')
672 + */
673 + $exclude = apply_filters(
674 + 'jetpack_api_exclude_comment_types_count',
675 + array( 'order_note', 'webhook_delivery', 'review', 'action_log' )
1053 676 );
1054 677
1055 - if ( empty( $include ) ) {
678 + if ( empty( $exclude ) ) {
1056 679 return wp_count_comments( $post_id );
1057 680 }
1058 681
1059 - array_walk( $include, 'esc_sql' );
682 + array_walk( $exclude, 'esc_sql' );
1060 683 $where = sprintf(
1061 - "WHERE comment_type IN ( '%s' )",
1062 - implode( "','", $include )
684 + "WHERE comment_type NOT IN ( '%s' )",
685 + implode( "','", $exclude )
1063 686 );
1064 687
1065 - // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- `$where` is built with escaping just above.
1066 688 $count = $wpdb->get_results(
1067 689 "SELECT comment_approved, COUNT(*) AS num_comments
1068 690 FROM $wpdb->comments
1069 691 {$where}
@@ -1069,9 +691,8 @@
1069 691 {$where}
1070 692 GROUP BY comment_approved
1071 693 "
1072 694 );
1073 - // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1074 695
1075 696 $approved = array(
1076 697 '0' => 'moderated',
1077 698 '1' => 'approved',
@@ -1079,9 +700,9 @@
1079 700 'trash' => 'trash',
1080 701 'post-trashed' => 'post-trashed',
1081 702 );
1082 703
1083 - // <https://developer.wordpress.org/reference/functions/get_comment_count/#source>
704 + // https://developer.wordpress.org/reference/functions/get_comment_count/#source
1084 705 foreach ( $count as $row ) {
1085 706 if ( ! in_array( $row->comment_approved, array( 'post-trashed', 'trash', 'spam' ), true ) ) {
1086 707 $counts['all'] += $row->num_comments;
1087 708 $counts['total_comments'] += $row->num_comments;
@@ -1104,30 +725,19 @@
1104 725 return $counts;
1105 726 }
1106 727
1107 728 /**
1108 - * Traps `wp_die()` calls and outputs a JSON response instead.
729 + * traps `wp_die()` calls and outputs a JSON response instead.
1109 730 * The result is always output, never returned.
1110 731 *
1111 732 * @param string|null $error_code Call with string to start the trapping. Call with null to stop.
1112 733 * @param int $http_status HTTP status code, 400 by default.
1113 734 */
1114 - public function trap_wp_die( $error_code = null, $http_status = 400 ) {
1115 - // Determine the filter name; based on the conditionals inside the wp_die function.
1116 - if ( wp_is_json_request() ) {
1117 - $die_handler = 'wp_die_json_handler';
1118 - } elseif ( wp_is_jsonp_request() ) {
1119 - $die_handler = 'wp_die_jsonp_handler';
1120 - } elseif ( wp_is_xml_request() ) {
1121 - $die_handler = 'wp_die_xml_handler';
1122 - } else {
1123 - $die_handler = 'wp_die_handler';
1124 - }
1125 -
1126 - if ( $error_code === null ) {
735 + function trap_wp_die( $error_code = null, $http_status = 400 ) {
736 + if ( is_null( $error_code ) ) {
1127 737 $this->trapped_error = null;
1128 - // Stop trapping.
1129 - remove_filter( $die_handler, array( $this, 'wp_die_handler_callback' ) );
738 + // Stop trapping
739 + remove_filter( 'wp_die_handler', array( $this, 'wp_die_handler_callback' ) );
1130 740 return;
1131 741 }
1132 742
1133 743 // If API called via PHP, bail: don't do our custom wp_die(). Do the normal wp_die().
@@ -1134,10 +744,12 @@
1134 744 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1135 745 if ( ! defined( 'REST_API_REQUEST' ) || ! REST_API_REQUEST ) {
1136 746 return;
1137 747 }
1138 - } elseif ( ! defined( 'XMLRPC_REQUEST' ) || ! XMLRPC_REQUEST ) {
1139 - return;
748 + } else {
749 + if ( ! defined( 'XMLRPC_REQUEST' ) || ! XMLRPC_REQUEST ) {
750 + return;
751 + }
1140 752 }
1141 753
1142 754 $this->trapped_error = array(
1143 755 'status' => $http_status,
@@ -1143,29 +755,17 @@
1143 755 'status' => $http_status,
1144 756 'code' => $error_code,
1145 757 'message' => '',
1146 758 );
1147 - // Start trapping.
1148 - add_filter( $die_handler, array( $this, 'wp_die_handler_callback' ) );
759 + // Start trapping
760 + add_filter( 'wp_die_handler', array( $this, 'wp_die_handler_callback' ) );
1149 761 }
1150 762
1151 - /**
1152 - * Filter function for `wp_die_handler` and similar filters.
1153 - *
1154 - * @return callable
1155 - */
1156 - public function wp_die_handler_callback() {
763 + function wp_die_handler_callback() {
1157 764 return array( $this, 'wp_die_handler' );
1158 765 }
1159 766
1160 - /**
1161 - * Handler for `wp_die` calls.
1162 - *
1163 - * @param string|WP_Error $message As for `wp_die()`.
1164 - * @param string|int $title As for `wp_die()`.
1165 - * @param string|array|int $args As for `wp_die()`.
1166 - */
1167 - public function wp_die_handler( $message, $title = '', $args = array() ) {
767 + function wp_die_handler( $message, $title = '', $args = array() ) {
1168 768 // Allow wp_die calls to override HTTP status code...
1169 769 $args = wp_parse_args(
1170 770 $args,
1171 771 array(
@@ -1173,9 +773,9 @@
1173 773 )
1174 774 );
1175 775
1176 776 // ... unless it's 500
1177 - if ( 500 !== (int) $args['response'] ) {
777 + if ( (int) $args['response'] !== 500 ) {
1178 778 $this->trapped_error['status'] = $args['response'];
1179 779 }
1180 780
1181 781 if ( $title ) {
@@ -1194,17 +794,14 @@
1194 794 break;
1195 795 }
1196 796
1197 797 // We still want to exit so that code execution stops where it should.
1198 - // Attach the JSON output to the WordPress shutdown handler.
798 + // Attach the JSON output to the WordPress shutdown handler
1199 799 add_action( 'shutdown', array( $this, 'output_trapped_error' ), 0 );
1200 800 exit;
1201 801 }
1202 802
1203 - /**
1204 - * Output the trapped error.
1205 - */
1206 - public function output_trapped_error() {
803 + function output_trapped_error() {
1207 804 $this->exit = false; // We're already exiting once. Don't do it twice.
1208 805 $this->output(
1209 806 $this->trapped_error['status'],
1210 807 (object) array(
@@ -1213,12 +810,9 @@
1213 810 )
1214 811 );
1215 812 }
1216 813
1217 - /**
1218 - * Finish the request.
1219 - */
1220 - public function finish_request() {
814 + function finish_request() {
1221 815 if ( function_exists( 'fastcgi_finish_request' ) ) {
1222 816 return fastcgi_finish_request();
1223 817 }
1224 818 }