Completed
Push — add/stats-package ( c3aabb...99d182 )
by
unknown
148:14 queued 140:08
created

WPCOM_JSON_API_Site_Settings_Endpoint   F

Complexity

Total Complexity 169

Size/Duplication

Total Lines 749
Duplicated Lines 4.14 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 31
loc 749
rs 1.851
c 0
b 0
f 0
wmc 169
lcom 1
cbo 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
B callback() 0 37 8
A wpcom_restapi_copy_theme_plugin_actions() 0 23 2
A jetpack_relatedposts_supported() 0 9 1
A get_category_details() 0 6 1
A get_cast_option_value_or_null() 0 8 2
F get_settings_response() 0 211 32
A get_locale() 11 11 4
A get_google_analytics() 0 4 3
F update_settings() 20 382 116

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WPCOM_JSON_API_Site_Settings_Endpoint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPCOM_JSON_API_Site_Settings_Endpoint, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
new WPCOM_JSON_API_Site_Settings_Endpoint( array(
4
	'description' => 'Get detailed settings information about a site.',
5
	'group'       => '__do_not_document',
6
	'stat'        => 'sites:X',
7
	'max_version'   => '1.1',
8
	'new_version' => '1.2',
9
	'method'      => 'GET',
10
	'path'        => '/sites/%s/settings',
11
	'path_labels' => array(
12
		'$site' => '(int|string) Site ID or domain',
13
	),
14
15
	'query_parameters' => array(
16
		'context' => false,
17
	),
18
19
	'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format,
20
21
	'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
22
) );
23
24
new WPCOM_JSON_API_Site_Settings_Endpoint( array(
25
	'description' => 'Update settings for a site.',
26
	'group'       => '__do_not_document',
27
	'stat'        => 'sites:X',
28
	'max_version' => '1.1',
29
	'new_version' => '1.2',
30
	'method'      => 'POST',
31
	'path'        => '/sites/%s/settings',
32
	'path_labels' => array(
33
		'$site' => '(int|string) Site ID or domain',
34
	),
35
36
	'request_format'  => array(
37
		'blogname'                     => '(string) Blog name',
38
		'blogdescription'              => '(string) Blog description',
39
		'default_pingback_flag'        => '(bool) Notify blogs linked from article?',
40
		'default_ping_status'          => '(bool) Allow link notifications from other blogs?',
41
		'default_comment_status'       => '(bool) Allow comments on new articles?',
42
		'blog_public'                  => '(string) Site visibility; -1: private, 0: discourage search engines, 1: allow search engines',
43
		'jetpack_sync_non_public_post_stati' => '(bool) allow sync of post and pages with non-public posts stati',
44
		'jetpack_relatedposts_enabled' => '(bool) Enable related posts?',
45
		'jetpack_relatedposts_show_headline' => '(bool) Show headline in related posts?',
46
		'jetpack_relatedposts_show_thumbnails' => '(bool) Show thumbnails in related posts?',
47
		'jetpack_protect_whitelist'    => '(array) List of IP addresses to whitelist',
48
		'jetpack_search_enabled'       => '(bool) Enable Jetpack Search',
49
		'jetpack_search_supported'     => '(bool) Jetpack Search is supported',
50
		'infinite_scroll'              => '(bool) Support infinite scroll of posts?',
51
		'default_category'             => '(int) Default post category',
52
		'default_post_format'          => '(string) Default post format',
53
		'require_name_email'           => '(bool) Require comment authors to fill out name and email?',
54
		'comment_registration'         => '(bool) Require users to be registered and logged in to comment?',
55
		'close_comments_for_old_posts' => '(bool) Automatically close comments on old posts?',
56
		'close_comments_days_old'      => '(int) Age at which to close comments',
57
		'thread_comments'              => '(bool) Enable threaded comments?',
58
		'thread_comments_depth'        => '(int) Depth to thread comments',
59
		'page_comments'                => '(bool) Break comments into pages?',
60
		'comments_per_page'            => '(int) Number of comments to display per page',
61
		'default_comments_page'        => '(string) newest|oldest Which page of comments to display first',
62
		'comment_order'                => '(string) asc|desc Order to display comments within page',
63
		'comments_notify'              => '(bool) Email me when someone comments?',
64
		'moderation_notify'            => '(bool) Email me when a comment is helf for moderation?',
65
		'social_notifications_like'    => '(bool) Email me when someone likes my post?',
66
		'social_notifications_reblog'  => '(bool) Email me when someone reblogs my post?',
67
		'social_notifications_subscribe' => '(bool) Email me when someone follows my blog?',
68
		'comment_moderation'           => '(bool) Moderate comments for manual approval?',
69
		'comment_whitelist'            => '(bool) Moderate comments unless author has a previously-approved comment?',
70
		'comment_max_links'            => '(int) Moderate comments that contain X or more links',
71
		'moderation_keys'              => '(string) Words or phrases that trigger comment moderation, one per line',
72
		'blacklist_keys'               => '(string) Words or phrases that mark comment spam, one per line',
73
		'lang_id'                      => '(int) ID for language blog is written in',
74
		'wga'                          => '(array) Google Analytics Settings',
75
		'disabled_likes'               => '(bool) Are likes globally disabled (they can still be turned on per post)?',
76
		'disabled_reblogs'             => '(bool) Are reblogs disabled on posts?',
77
		'jetpack_comment_likes_enabled' => '(bool) Are comment likes enabled for all comments?',
78
		'sharing_button_style'         => '(string) Style to use for sharing buttons (icon-text, icon, text, or official)',
79
		'sharing_label'                => '(string) Label to use for sharing buttons, e.g. "Share this:"',
80
		'sharing_show'                 => '(string|array:string) Post type or array of types where sharing buttons are to be displayed',
81
		'sharing_open_links'           => '(string) Link target for sharing buttons (same or new)',
82
		'twitter_via'                  => '(string) Twitter username to include in tweets when people share using the Twitter button',
83
		'jetpack-twitter-cards-site-tag' => '(string) The Twitter username of the owner of the site\'s domain.',
84
		'eventbrite_api_token'         => '(int) The Keyring token ID for an Eventbrite token to associate with the site',
85
		'timezone_string'              => '(string) PHP-compatible timezone string like \'UTC-5\'',
86
		'gmt_offset'                   => '(int) Site offset from UTC in hours',
87
		'date_format'                  => '(string) PHP Date-compatible date format',
88
		'time_format'                  => '(string) PHP Date-compatible time format',
89
		'start_of_week'                => '(int) Starting day of week (0 = Sunday, 6 = Saturday)',
90
		'jetpack_testimonial'          => '(bool) Whether testimonial custom post type is enabled for the site',
91
		'jetpack_testimonial_posts_per_page' => '(int) Number of testimonials to show per page',
92
		'jetpack_portfolio'            => '(bool) Whether portfolio custom post type is enabled for the site',
93
		'jetpack_portfolio_posts_per_page' => '(int) Number of portfolio projects to show per page',
94
		Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => '(string) The seo meta description for the site.',
95
		Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => '(array) SEO meta title formats. Allowed keys: front_page, posts, pages, groups, archives',
96
		'verification_services_codes'  => '(array) Website verification codes. Allowed keys: google, pinterest, bing, yandex',
97
		'markdown_supported'            => '(bool) Whether markdown is supported for this site',
98
		'wpcom_publish_posts_with_markdown' => '(bool) Whether markdown is enabled for posts',
99
		'wpcom_publish_comments_with_markdown' => '(bool) Whether markdown is enabled for comments',
100
		'amp_is_enabled'   => '(bool) Whether AMP is enabled for this site',
101
		'site_icon'                    => '(int) Media attachment ID to use as site icon. Set to zero or an otherwise empty value to clear',
102
		'api_cache'                    => '(bool) Turn on/off the Jetpack JSON API cache',
103
		'posts_per_page'               => '(int) Number of posts to show on blog pages',
104
		'posts_per_rss'                => '(int) Number of posts to show in the RSS feed',
105
		'rss_use_excerpt'              => '(bool) Whether the RSS feed will use post excerpts',
106
	),
107
108
	'response_format' => array(
109
		'updated' => '(array)'
110
	),
111
112
	'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
113
) );
114
115
class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
116
117
	public static $site_format = array(
118
		'ID'                => '(int) Site ID',
119
		'name'              => '(string) Title of site',
120
		'description'       => '(string) Tagline or description of site',
121
		'URL'               => '(string) Full URL to the site',
122
		'lang'              => '(string) Primary language code of the site',
123
		'locale_variant'    => '(string) Locale variant code for the site, if set',
124
		'settings'          => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
125
	);
126
127
	// GET /sites/%s/settings
128
	// POST /sites/%s/settings
129
	function callback( $path = '', $blog_id = 0 ) {
130
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
131
		if ( is_wp_error( $blog_id ) ) {
132
			return $blog_id;
133
		}
134
135
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
136
			// Source & include the infinite scroll compatibility files prior to loading theme functions
137
			add_filter( 'restapi_theme_action_copy_dirs', array( 'WPCOM_JSON_API_Site_Settings_Endpoint', 'wpcom_restapi_copy_theme_plugin_actions' ) );
138
			$this->load_theme_functions();
139
		}
140
141
		if ( ! is_user_logged_in() ) {
142
			return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
143
		} else if ( ! current_user_can( 'manage_options' ) ) {
144
			return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Forbidden'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
145
		}
146
147
		if ( 'GET' === $this->api->method ) {
148
			/**
149
			 * Fires on each GET request to a specific endpoint.
150
			 *
151
			 * @module json-api
152
			 *
153
			 * @since 3.2.0
154
			 *
155
			 * @param string sites.
156
			 */
157
			do_action( 'wpcom_json_api_objects', 'sites' );
158
			return $this->get_settings_response();
159
		} else if ( 'POST' === $this->api->method ) {
160
			return $this->update_settings();
161
		} else {
162
			return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'bad_request'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
163
		}
164
165
	}
166
167
	/**
168
	 * Includes additional theme-specific files to be included in REST API theme
169
	 * context loading action copying.
170
	 *
171
	 * @see WPCOM_JSON_API_Endpoint#load_theme_functions
172
	 * @see the_neverending_home_page_theme_support
173
	 */
174
	function wpcom_restapi_copy_theme_plugin_actions( $copy_dirs ) {
175
		$theme_name = get_stylesheet();
176
		$default_file_name = WP_CONTENT_DIR . "/mu-plugins/infinity/themes/{$theme_name}.php";
177
178
		/**
179
		 * Filter the path to the Infinite Scroll compatibility file.
180
		 *
181
		 * @module infinite-scroll
182
		 *
183
		 * @since 2.0.0
184
		 *
185
		 * @param string $str IS compatibility file path.
186
		 * @param string $theme_name Theme name.
187
		 */
188
		$customization_file = apply_filters( 'infinite_scroll_customization_file', $default_file_name, $theme_name );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $theme_name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
189
190
		if ( is_readable( $customization_file ) ) {
191
			require_once $customization_file;
192
			$copy_dirs[] = $customization_file;
193
		}
194
195
		return $copy_dirs;
196
	}
197
198
	/**
199
	 * Determines whether jetpack_relatedposts is supported
200
	 *
201
	 * @return bool
202
	 */
203
	public function jetpack_relatedposts_supported() {
204
		$wpcom_related_posts_theme_blacklist = array(
205
			'Expound',
206
			'Traveler',
207
			'Opti',
208
			'Currents',
209
		);
210
		return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist ) );
211
	}
212
213
	/**
214
	 * Returns category details
215
	 *
216
	 * @return array
217
	 */
218
	public function get_category_details( $category ) {
219
		return array(
220
			'value' => $category->term_id,
221
			'name' => $category->name
222
		);
223
	}
224
225
	/**
226
	 * Returns an option value as the result of the callable being applied to
227
	 * it if a value is set, otherwise null.
228
	 *
229
	 * @param string $option_name Option name
230
	 * @param callable $cast_callable Callable to invoke on option value
231
	 * @return int|null Numeric option value or null
232
	 */
233
	protected function get_cast_option_value_or_null( $option_name, $cast_callable ) {
234
		$option_value = get_option( $option_name, null );
235
		if ( is_null( $option_value ) ) {
236
			return $option_value;
237
		}
238
239
		return call_user_func( $cast_callable, $option_value );
240
	}
241
242
	/**
243
	 * Collects the necessary information to return for a get settings response.
244
	 *
245
	 * @return array
246
	 */
247
	public function get_settings_response() {
248
249
		// Allow update in later versions
250
		/**
251
		 * Filter the structure of site settings to return.
252
		 *
253
		 * @module json-api
254
		 *
255
		 * @since 3.9.3
256
		 *
257
		 * @param array $site_format Data structure.
258
		 */
259
		$response_format = apply_filters( 'site_settings_site_format', self::$site_format );
260
261
		$blog_id = (int) $this->api->get_blog_id_for_output();
262
		/** This filter is documented in class.json-api-endpoints.php */
263
		$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $blog_id.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
264
265
		foreach ( array_keys( $response_format ) as $key ) {
266
267
			// refactoring to change lang parameter to locale in 1.2
268
			if ( $lang_or_locale = $this->get_locale( $key ) ) {
269
				$response[$key] = $lang_or_locale;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
270
				continue;
271
			}
272
273
			switch ( $key ) {
274
			case 'ID' :
275
				$response[$key] = $blog_id;
0 ignored issues
show
Bug introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
276
				break;
277
			case 'name' :
278
				$response[$key] = (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
279
				break;
280
			case 'description' :
281
				$response[$key] = (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
282
				break;
283
			case 'URL' :
284
				$response[$key] = (string) home_url();
285
				break;
286
			case 'locale_variant':
287
				if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) {
288
					$blog_locale_variant = wpcom_l10n_get_blog_locale_variant();
289
					if ( $blog_locale_variant ) {
290
						$response[$key] = $blog_locale_variant;
291
					}
292
				}
293
				break;
294
			case 'settings':
295
296
				$jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts', array() );
297
				// If the option's enabled key is NOT SET, it is considered enabled by the plugin
298
				if ( ! isset( $jetpack_relatedposts_options['enabled'] ) ) {
299
					$jetpack_relatedposts_options['enabled'] = true;
300
				}
301
302
				if ( method_exists( 'Jetpack', 'is_module_active' ) ) {
303
					$jetpack_relatedposts_options[ 'enabled' ] = Jetpack::is_module_active( 'related-posts' );
304
				}
305
306
				$jetpack_search_supported = false;
307
				if ( function_exists( 'wpcom_is_jetpack_search_supported' ) ) {
308
					$jetpack_search_supported = wpcom_is_jetpack_search_supported( $blog_id );
309
				}
310
311
				$jetpack_search_active = false;
312
				if ( method_exists( 'Jetpack', 'is_module_active' ) ) {
313
					$jetpack_search_active = Jetpack::is_module_active( 'search' );
314
				}
315
				if ( function_exists( 'is_jetpack_module_active' ) ) {
316
					$jetpack_search_active = is_jetpack_module_active( 'search', $blog_id );
317
				}
318
319
				// array_values() is necessary to ensure the array starts at index 0.
320
				$post_categories = array_values(
321
					array_map(
322
						array( $this, 'get_category_details' ),
323
						get_categories( array( 'hide_empty' => false ) )
324
					)
325
				);
326
327
				$api_cache = $is_jetpack ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true;
328
329
				$response[ $key ] = array(
330
331
					// also exists as "options"
332
					'admin_url'               => get_admin_url(),
333
					'default_ping_status'     => (bool) ( 'closed' != get_option( 'default_ping_status' ) ),
334
					'default_comment_status'  => (bool) ( 'closed' != get_option( 'default_comment_status' ) ),
335
336
					// new stuff starts here
337
					'blog_public'             => (int) get_option( 'blog_public' ),
338
					'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
339
					'jetpack_relatedposts_allowed' => (bool) $this->jetpack_relatedposts_supported(),
340
					'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options[ 'enabled' ],
341
					'jetpack_relatedposts_show_headline' => (bool) isset( $jetpack_relatedposts_options[ 'show_headline' ] ) ? $jetpack_relatedposts_options[ 'show_headline' ] : false,
342
					'jetpack_relatedposts_show_thumbnails' => (bool) isset( $jetpack_relatedposts_options[ 'show_thumbnails' ] ) ? $jetpack_relatedposts_options[ 'show_thumbnails' ] : false,
343
					'jetpack_search_enabled'  => (bool) $jetpack_search_active,
344
					'jetpack_search_supported'=> (bool) $jetpack_search_supported,
345
					'default_category'        => (int) get_option('default_category'),
346
					'post_categories'         => (array) $post_categories,
347
					'default_post_format'     => get_option( 'default_post_format' ),
348
					'default_pingback_flag'   => (bool) get_option( 'default_pingback_flag' ),
349
					'require_name_email'      => (bool) get_option( 'require_name_email' ),
350
					'comment_registration'    => (bool) get_option( 'comment_registration' ),
351
					'close_comments_for_old_posts' => (bool) get_option( 'close_comments_for_old_posts' ),
352
					'close_comments_days_old' => (int) get_option( 'close_comments_days_old' ),
353
					'thread_comments'         => (bool) get_option( 'thread_comments' ),
354
					'thread_comments_depth'   => (int) get_option( 'thread_comments_depth' ),
355
					'page_comments'           => (bool) get_option( 'page_comments' ),
356
					'comments_per_page'       => (int) get_option( 'comments_per_page' ),
357
					'default_comments_page'   => get_option( 'default_comments_page' ),
358
					'comment_order'           => get_option( 'comment_order' ),
359
					'comments_notify'         => (bool) get_option( 'comments_notify' ),
360
					'moderation_notify'       => (bool) get_option( 'moderation_notify' ),
361
					'social_notifications_like' => ( "on" == get_option( 'social_notifications_like' ) ),
362
					'social_notifications_reblog' => ( "on" == get_option( 'social_notifications_reblog' ) ),
363
					'social_notifications_subscribe' => ( "on" == get_option( 'social_notifications_subscribe' ) ),
364
					'comment_moderation'      => (bool) get_option( 'comment_moderation' ),
365
					'comment_whitelist'       => (bool) get_option( 'comment_whitelist' ),
366
					'comment_max_links'       => (int) get_option( 'comment_max_links' ),
367
					'moderation_keys'         => get_option( 'moderation_keys' ),
368
					'blacklist_keys'          => get_option( 'blacklist_keys' ),
369
					'lang_id'                 => defined( 'IS_WPCOM' ) && IS_WPCOM
370
						? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) )
371
						: get_option( 'lang_id' ),
372
					'wga'                     => $this->get_google_analytics(),
373
					'disabled_likes'          => (bool) get_option( 'disabled_likes' ),
374
					'disabled_reblogs'        => (bool) get_option( 'disabled_reblogs' ),
375
					'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
376
					'twitter_via'             => (string) get_option( 'twitter_via' ),
377
					'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
378
					'eventbrite_api_token'    => $this->get_cast_option_value_or_null( 'eventbrite_api_token', 'intval' ),
379
					'gmt_offset'              => get_option( 'gmt_offset' ),
380
					'timezone_string'         => get_option( 'timezone_string' ),
381
					'date_format'             => get_option( 'date_format' ),
382
					'time_format'             => get_option( 'time_format' ),
383
					'start_of_week'           => get_option( 'start_of_week' ),
384
					'jetpack_testimonial'     => (bool) get_option( 'jetpack_testimonial', '0' ),
385
					'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ),
386
					'jetpack_portfolio'       => (bool) get_option( 'jetpack_portfolio', '0' ),
387
					'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ),
388
					'markdown_supported'      => true,
389
					'site_icon'               => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ),
390
					Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ),
391
					Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ),
392
					'amp_is_supported'        => (bool) function_exists( 'wpcom_is_amp_supported' ) && wpcom_is_amp_supported( $blog_id ),
393
					'amp_is_enabled'          => (bool) function_exists( 'wpcom_is_amp_enabled' ) && wpcom_is_amp_enabled( $blog_id ),
394
					'api_cache'               => $api_cache,
395
					'posts_per_page'          => (int) get_option( 'posts_per_page' ),
396
					'posts_per_rss'           => (int) get_option( 'posts_per_rss' ),
397
					'rss_use_excerpt'         => (bool) get_option( 'rss_use_excerpt' ),
398
				);
399
400
				if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
401
					$response[ $key ]['wpcom_publish_posts_with_markdown'] = (bool) WPCom_Markdown::is_posting_enabled();
402
					$response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::is_commenting_enabled();
403
404
					// WPCOM-specific Infinite Scroll Settings
405
					if ( is_callable( array( 'The_Neverending_Home_Page', 'get_settings' ) ) ) {
406
						/**
407
						 * Clear the cached copy of widget info so it's pulled fresh from blog options.
408
						 * It was primed during the initial load under the __REST API site__'s context.
409
						 * @see wp_get_sidebars_widgets https://core.trac.wordpress.org/browser/trunk/src/wp-includes/widgets.php?rev=42374#L931
410
						 */
411
						$GLOBALS['_wp_sidebars_widgets'] = array();
412
413
						$infinite_scroll_settings = The_Neverending_Home_Page::get_settings();
414
						$response[ $key ]['infinite_scroll'] = get_option( 'infinite_scroll', true ) && $infinite_scroll_settings->type === 'scroll';
415
						if ( $infinite_scroll_settings->footer_widgets || 'click' == $infinite_scroll_settings->requested_type ) {
416
							// The blog has footer widgets -- infinite scroll is blocked
417
							$response[ $key ]['infinite_scroll_blocked'] = 'footer';
418
						} else {
419
							$response[ $key ]['infinite_scroll_blocked'] = false;
420
						}
421
					}
422
				}
423
424
				//allow future versions of this endpoint to support additional settings keys
425
				/**
426
				 * Filter the current site setting in the returned response.
427
				 *
428
				 * @module json-api
429
				 *
430
				 * @since 3.9.3
431
				 *
432
				 * @param mixed $response_item A single site setting.
433
				 */
434
				$response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ] );
435
436
				if ( class_exists( 'Sharing_Service' ) ) {
437
					$ss = new Sharing_Service();
438
					$sharing = $ss->get_global_options();
439
					$response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
440
					$response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
441
					$response[ $key ]['sharing_show'] = (array) $sharing['show'];
442
					$response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
443
				}
444
445
				if ( function_exists( 'jetpack_protect_format_whitelist' ) ) {
446
					$response[ $key ]['jetpack_protect_whitelist'] = jetpack_protect_format_whitelist();
447
				}
448
449
				if ( ! current_user_can( 'edit_posts' ) )
450
					unset( $response[$key] );
451
				break;
452
			}
453
		}
454
455
		return $response;
456
457
	}
458
459 View Code Duplication
	protected function get_locale( $key ) {
460
		if ( 'lang' == $key ) {
461
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
462
				return (string) get_blog_lang_code();
463
			} else {
464
				return get_locale();
465
			}
466
		}
467
468
		return false;
469
	}
470
471
	protected function get_google_analytics () {
472
		$option_name = defined( 'IS_WPCOM' ) && IS_WPCOM ? 'wga' : 'jetpack_wga';
473
		return get_option( $option_name );
474
	}
475
476
	/**
477
	 * Updates site settings for authorized users
478
	 *
479
	 * @return array
480
	 */
481
	public function update_settings() {
482
		// $this->input() retrieves posted arguments whitelisted and casted to the $request_format
483
		// specs that get passed in when this class is instantiated
484
		$input = $this->input();
485
		$unfiltered_input = $this->input( false, false );
486
		/**
487
		 * Filters the settings to be updated on the site.
488
		 *
489
		 * @module json-api
490
		 *
491
		 * @since 3.6.0
492
		 * @since 6.1.1 Added $unfiltered_input parameter.
493
		 *
494
		 * @param array $input              Associative array of site settings to be updated.
495
		 *                                  Cast and filtered based on documentation.
496
		 * @param array $unfiltered_input   Associative array of site settings to be updated.
497
		 *                                  Neither cast nor filtered. Contains raw input.
498
		 */
499
		$input = apply_filters( 'rest_api_update_site_settings', $input, $unfiltered_input );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $unfiltered_input.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
500
501
		$blog_id = get_current_blog_id();
502
503
		$jetpack_relatedposts_options = array();
504
		$sharing_options = array();
505
		$updated = array();
506
507
		foreach ( $input as $key => $value ) {
508
509
			if ( ! is_array( $value ) ) {
510
				$value = trim( $value );
511
			}
512
			$value = wp_unslash( $value );
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array; however, wp_unslash() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
513
514
			switch ( $key ) {
515
516
				case 'default_ping_status':
517
				case 'default_comment_status':
518
					// settings are stored as closed|open
519
					$coerce_value = ( $value ) ? 'open' : 'closed';
520
					if ( update_option( $key, $coerce_value ) ) {
521
						$updated[ $key ] = $value;
522
					};
523
					break;
524
				case 'jetpack_protect_whitelist':
525
					if ( function_exists( 'jetpack_protect_save_whitelist' ) ) {
526
						$result = jetpack_protect_save_whitelist( $value );
527
						if ( is_wp_error( $result ) ) {
528
							return $result;
529
						}
530
						$updated[ $key ] = jetpack_protect_format_whitelist();
531
					}
532
					break;
533
				case 'jetpack_sync_non_public_post_stati':
534
					Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
535
					break;
536
				case 'jetpack_search_enabled':
537
					if ( ! method_exists( 'Jetpack', 'activate_module' ) ) {
538
						break;
539
					}
540
					$is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
541
					if ( $value ) {
542
						$jetpack_search_update_success = $is_wpcom
0 ignored issues
show
Unused Code introduced by
$jetpack_search_update_success is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
543
							? Jetpack::activate_module( $blog_id, 'search' )
0 ignored issues
show
Documentation introduced by
'search' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
544
							: Jetpack::activate_module( 'search', false, false );
545
					} else {
546
						$jetpack_search_update_success = $is_wpcom
0 ignored issues
show
Unused Code introduced by
$jetpack_search_update_success is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
547
							? Jetpack::deactivate_module( $blog_id, 'search' )
0 ignored issues
show
Unused Code introduced by
The call to Jetpack::deactivate_module() has too many arguments starting with 'search'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
548
							: Jetpack::deactivate_module( 'search' );
549
					}
550
					$updated[ $key ] = (bool) $value;
551
					break;
552
				case 'jetpack_relatedposts_enabled':
553
				case 'jetpack_relatedposts_show_thumbnails':
554
				case 'jetpack_relatedposts_show_headline':
555
					if ( ! $this->jetpack_relatedposts_supported() ) {
556
						break;
557
					}
558
					if ( 'jetpack_relatedposts_enabled' === $key && method_exists( 'Jetpack', 'is_module_active' ) && $this->jetpack_relatedposts_supported() ) {
559
						$before_action = Jetpack::is_module_active('related-posts');
560
						if ( $value ) {
561
							Jetpack::activate_module( 'related-posts', false, false );
562
						} else {
563
							Jetpack::deactivate_module( 'related-posts' );
564
						}
565
						$after_action = Jetpack::is_module_active('related-posts');
566
						if ( $after_action == $before_action ) {
567
							break;
568
						}
569
					}
570
					$just_the_key = substr( $key, 21 );
571
					$jetpack_relatedposts_options[ $just_the_key ] = $value;
572
				break;
573
574
				case 'social_notifications_like':
575
				case 'social_notifications_reblog':
576
				case 'social_notifications_subscribe':
577
					// settings are stored as on|off
578
					$coerce_value = ( $value ) ? 'on' : 'off';
579
					if ( update_option( $key, $coerce_value ) ) {
580
						$updated[ $key ] = $value;
581
					}
582
					break;
583
				case 'wga':
584
				case 'jetpack_wga':
585
					if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^UA-[\d-]+$/i', $value['code'] ) ) {
586
						return new WP_Error( 'invalid_code', 'Invalid UA ID' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_code'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
587
					}
588
589
					$is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
590
					$option_name = $is_wpcom ? 'wga' : 'jetpack_wga';
591
592
					$wga = get_option( $option_name, array() );
593
					$wga['code'] = $value['code']; // maintain compatibility with wp-google-analytics
594
595
					/**
596
					 * Allow newer versions of this endpoint to filter in additional fields for Google Analytics
597
					 *
598
					 * @since 5.4.0
599
					 *
600
					 * @param array $wga Associative array of existing Google Analytics settings.
601
					 * @param array $value Associative array of new Google Analytics settings passed to the endpoint.
602
					 */
603
					$wga = apply_filters( 'site_settings_update_wga', $wga, $value );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $value.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
604
605
					if ( update_option( $option_name, $wga ) ) {
606
						$updated[ $key ] = $value;
607
					}
608
609
					$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
610
611
					/** This action is documented in modules/widgets/social-media-icons.php */
612
					do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
613
614
					if ( $is_wpcom ) {
615
						$business_plugins = WPCOM_Business_Plugins::instance();
616
						$business_plugins->activate_plugin( 'wp-google-analytics' );
617
					}
618
					break;
619
620
				case 'jetpack_testimonial':
621
				case 'jetpack_portfolio':
622 View Code Duplication
				case 'jetpack_comment_likes_enabled':
623
					// settings are stored as 1|0
624
					$coerce_value = (int) $value;
625
					if ( update_option( $key, $coerce_value ) ) {
626
						$updated[ $key ] = (bool) $value;
627
					}
628
					break;
629
630
				case 'jetpack_testimonial_posts_per_page':
631 View Code Duplication
				case 'jetpack_portfolio_posts_per_page':
632
					// settings are stored as numeric
633
					$coerce_value = (int) $value;
634
					if ( update_option( $key, $coerce_value ) ) {
635
						$updated[ $key ] = $coerce_value;
636
					}
637
					break;
638
639
				// Sharing options
640
				case 'sharing_button_style':
641
				case 'sharing_show':
642
				case 'sharing_open_links':
643
					$sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
644
					break;
645
				case 'sharing_label':
646
					$sharing_options[ $key ] = $value;
647
					break;
648
649
				// Keyring token option
650
				case 'eventbrite_api_token':
651
					// These options can only be updated for sites hosted on WordPress.com
652
					if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
653
						if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
654
							if ( delete_option( $key ) ) {
655
								$updated[ $key ] = null;
656
							}
657
						} else if ( update_option( $key, $value ) ) {
658
							$updated[ $key ] = (int) $value;
659
						}
660
					}
661
					break;
662
663
				case 'api_cache':
664
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
665
						if ( delete_option( 'jetpack_api_cache_enabled' ) ) {
666
							$updated[ $key ] = false;
667
						}
668
					} else if ( update_option( 'jetpack_api_cache_enabled', true ) ) {
669
						$updated[ $key ] = true;
670
					}
671
					break;
672
673
				case 'timezone_string':
674
					// Map UTC+- timezones to gmt_offsets and set timezone_string to empty
675
					// https://github.com/WordPress/WordPress/blob/4.4.2/wp-admin/options.php#L175
676
					if ( ! empty( $value ) && preg_match( '/^UTC[+-]/', $value ) ) {
677
						$gmt_offset = preg_replace( '/UTC\+?/', '', $value );
678
						if ( update_option( 'gmt_offset', $gmt_offset ) ) {
679
							$updated[ 'gmt_offset' ] = $gmt_offset;
680
						}
681
682
						$value = '';
683
					}
684
685
					// Always set timezone_string either with the given value or with an
686
					// empty string
687
					if ( update_option( $key, $value ) ) {
688
						$updated[ $key ] = $value;
689
					}
690
					break;
691
692
				case 'date_format':
693
				case 'time_format':
694
					// settings are stored as strings
695
					if ( update_option( $key, sanitize_text_field( $value ) ) ) {
696
						$updated[ $key ] = $value;
697
					}
698
					break;
699
700
				case 'start_of_week':
701
					// setting is stored as int in 0-6 range (days of week)
702
					$coerce_value = (int) $value;
703
					$limit_value  = ( $coerce_value >= 0 && $coerce_value <= 6 ) ? $coerce_value : 0;
704
					if ( update_option( $key, $limit_value ) ) {
705
						$updated[ $key ] = $limit_value;
706
					}
707
					break;
708
709
				case 'site_icon':
710
					// settings are stored as deletable numeric (all empty
711
					// values as delete intent), validated as media image
712
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
713
						/**
714
						 * Fallback mechanism to clear a third party site icon setting. Can be used
715
						 * to unset the option when an API request instructs the site to remove the site icon.
716
						 *
717
						 * @module json-api
718
						 *
719
						 * @since 4.10
720
						 */
721
						if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false )  ) {
722
							$updated[ $key ] = null;
723
						}
724
					} else if ( is_numeric( $value ) ) {
725
						$coerce_value = (int) $value;
726
						if ( wp_attachment_is_image( $coerce_value ) && update_option( $key, $coerce_value ) ) {
727
							$updated[ $key ] = $coerce_value;
728
						}
729
					}
730
					break;
731
732
				case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
733
					if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() && ! Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
734
						return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
735
					}
736
737
					if ( ! is_string( $value ) ) {
738
						return new WP_Error( 'invalid_input', __( 'Invalid SEO meta description value.', 'jetpack' ), 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
739
					}
740
741
					$new_description = Jetpack_SEO_Utils::update_front_page_meta_description( $value );
742
743
					if ( ! empty( $new_description ) ) {
744
						$updated[ $key ] = $new_description;
745
					}
746
					break;
747
748
				case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
749
					if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
750
						return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
751
					}
752
753
					if ( ! Jetpack_SEO_Titles::are_valid_title_formats( $value ) ) {
0 ignored issues
show
Bug introduced by
It seems like $value defined by wp_unslash($value) on line 512 can also be of type string; however, Jetpack_SEO_Titles::are_valid_title_formats() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
754
						return new WP_Error( 'invalid_input', __( 'Invalid SEO title format.', 'jetpack' ), 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
755
					}
756
757
					$new_title_formats = Jetpack_SEO_Titles::update_title_formats( $value );
0 ignored issues
show
Bug introduced by
It seems like $value defined by wp_unslash($value) on line 512 can also be of type string; however, Jetpack_SEO_Titles::update_title_formats() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
758
759
					if ( ! empty( $new_title_formats ) ) {
760
						$updated[ $key ] = $new_title_formats;
761
					}
762
					break;
763
764
				case 'verification_services_codes':
765
					$verification_codes = jetpack_verification_validate( $value );
766
767
					if ( update_option( 'verification_services_codes', $verification_codes ) ) {
768
						$updated[ $key ] = $verification_codes;
769
					}
770
					break;
771
772
				case 'wpcom_publish_posts_with_markdown':
773 View Code Duplication
				case 'wpcom_publish_comments_with_markdown':
774
					$coerce_value = (bool) $value;
775
					if ( update_option( $key, $coerce_value ) ) {
776
						$updated[ $key ] = $coerce_value;
777
					}
778
					break;
779
780
				case 'amp_is_enabled':
781
					if ( function_exists( 'wpcom_update_amp_enabled' ) ) {
782
						$saved = wpcom_update_amp_enabled( $blog_id, $value );
783
						if ( $saved ) {
784
							$updated[ $key ] = (bool) $value;
785
						}
786
					}
787
					break;
788
789
				case 'rss_use_excerpt':
790
					update_option( 'rss_use_excerpt', (int)(bool) $value );
791
					break;
792
793
				default:
794
					//allow future versions of this endpoint to support additional settings keys
795
					if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
796
						/**
797
						 * Filter current site setting value to be updated.
798
						 *
799
						 * @module json-api
800
						 *
801
						 * @since 3.9.3
802
						 *
803
						 * @param mixed $response_item A single site setting value.
804
						 */
805
						$value = apply_filters( 'site_settings_endpoint_update_' . $key, $value );
806
						$updated[ $key ] = $value;
807
						break;
808
					}
809
810
					// no worries, we've already whitelisted and casted arguments above
811
					if ( update_option( $key, $value ) ) {
812
						$updated[ $key ] = $value;
813
					}
814
			}
815
		}
816
817
		if ( count( $jetpack_relatedposts_options ) ) {
818
			// track new jetpack_relatedposts options against old
819
			$old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
820
			if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options ) ) {
821
				foreach ( $jetpack_relatedposts_options as $key => $value ) {
822
					if ( isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ] ) {
823
						$updated[ 'jetpack_relatedposts_' . $key ] = $value;
824
					}
825
				}
826
			}
827
		}
828
829
		if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
830
			$ss = new Sharing_Service();
831
832
			// Merge current values with updated, since Sharing_Service expects
833
			// all values to be included when updating
834
			$current_sharing_options = $ss->get_global_options();
835
			foreach ( $current_sharing_options as $key => $val ) {
836
				if ( ! isset( $sharing_options[ $key ] ) ) {
837
					$sharing_options[ $key ] = $val;
838
				}
839
			}
840
841
			$updated_social_options = $ss->set_global_options( $sharing_options );
842
843
			if ( isset( $input['sharing_button_style'] ) ) {
844
				$updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
845
			}
846
			if ( isset( $input['sharing_label'] ) ) {
847
				// Sharing_Service won't report label as updated if set to default
848
				$updated['sharing_label'] = (string) $sharing_options['sharing_label'];
849
			}
850
			if ( isset( $input['sharing_show'] ) ) {
851
				$updated['sharing_show'] = (array) $updated_social_options['show'];
852
			}
853
			if ( isset( $input['sharing_open_links'] ) ) {
854
				$updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
855
			}
856
		}
857
858
		return array(
859
			'updated' => $updated
860
		);
861
862
	}
863
}
864