Completed
Push — branch-8.8translations-built ( d14c3a...6664d3 )
by Jeremy
77:12 queued 67:56
created

class.jetpack-plan.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
/**
3
 * Handles fetching of the site's plan from WordPress.com and caching the value locally.
4
 *
5
 * Not to be confused with the `Jetpack_Plans` class (in `_inc/lib/plans.php`), which
6
 * fetches general information about all available plans from WordPress.com, side-effect free.
7
 *
8
 * @package Jetpack
9
 */
10
11
use Automattic\Jetpack\Connection\Client;
12
13
/**
14
 * Provides methods methods for fetching the plan from WordPress.com.
15
 */
16
class Jetpack_Plan {
17
	/**
18
	 * A cache variable to hold the active plan for the current request.
19
	 *
20
	 * @var array
21
	 */
22
	private static $active_plan_cache;
23
24
	const PLAN_OPTION = 'jetpack_active_plan';
25
26
	const PLAN_DATA = array(
27
		'free'     => array(
28
			'plans'    => array(
29
				'jetpack_free',
30
			),
31
			'supports' => array(
32
				'opentable',
33
				'calendly',
34
				'send-a-message',
35
			),
36
		),
37
		'personal' => array(
38
			'plans'    => array(
39
				'jetpack_personal',
40
				'jetpack_personal_monthly',
41
				'personal-bundle',
42
				'personal-bundle-monthly',
43
				'personal-bundle-2y',
44
			),
45
			'supports' => array(
46
				'akismet',
47
				'recurring-payments',
48
			),
49
		),
50
		'premium'  => array(
51
			'plans'    => array(
52
				'jetpack_premium',
53
				'jetpack_premium_monthly',
54
				'value_bundle',
55
				'value_bundle-monthly',
56
				'value_bundle-2y',
57
			),
58
			'supports' => array(
59
				'simple-payments',
60
				'vaultpress',
61
				'videopress',
62
			),
63
		),
64
		'business' => array(
65
			'plans'    => array(
66
				'jetpack_business',
67
				'jetpack_business_monthly',
68
				'business-bundle',
69
				'business-bundle-monthly',
70
				'business-bundle-2y',
71
				'ecommerce-bundle',
72
				'ecommerce-bundle-monthly',
73
				'ecommerce-bundle-2y',
74
				'vip',
75
			),
76
			'supports' => array(),
77
		),
78
	);
79
80
	/**
81
	 * Given a response to the `/sites/%d` endpoint, will parse the response and attempt to set the
82
	 * plan from the response.
83
	 *
84
	 * @param array $response The response from `/sites/%d`.
85
	 * @return bool Was the plan successfully updated?
86
	 */
87
	public static function update_from_sites_response( $response ) {
88
		// Bail if there was an error or malformed response.
89
		if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
90
			return false;
91
		}
92
93
		$body = wp_remote_retrieve_body( $response );
94
		if ( is_wp_error( $body ) ) {
95
			return false;
96
		}
97
98
		// Decode the results.
99
		$results = json_decode( $body, true );
100
101
		// Bail if there were no results or plan details returned.
102
		if ( ! is_array( $results ) || ! isset( $results['plan'] ) ) {
103
			return false;
104
		}
105
106
		// Store the new plan in an option and return true if updated.
107
		$result = update_option( self::PLAN_OPTION, $results['plan'], true );
108
		if ( ! $result ) {
109
			// If we got to this point, then we know we need to update. So, assume there is an issue
110
			// with caching. To fix that issue, we can delete the current option and then update.
111
			delete_option( self::PLAN_OPTION );
112
			$result = update_option( self::PLAN_OPTION, $results['plan'], true );
113
		}
114
115
		if ( $result ) {
116
			// Reset the cache since we've just updated the plan.
117
			self::$active_plan_cache = null;
118
		}
119
120
		return $result;
121
	}
122
123
	/**
124
	 * Make an API call to WordPress.com for plan status
125
	 *
126
	 * @uses Jetpack_Options::get_option()
127
	 * @uses Client::wpcom_json_api_request_as_blog()
128
	 * @uses update_option()
129
	 *
130
	 * @access public
131
	 * @static
132
	 *
133
	 * @return bool True if plan is updated, false if no update
134
	 */
135
	public static function refresh_from_wpcom() {
136
		// Make the API request.
137
		$request  = sprintf( '/sites/%d', Jetpack_Options::get_option( 'id' ) );
138
		$response = Client::wpcom_json_api_request_as_blog( $request, '1.1' );
139
140
		return self::update_from_sites_response( $response );
141
	}
142
143
	/**
144
	 * Get the plan that this Jetpack site is currently using.
145
	 *
146
	 * @uses get_option()
147
	 *
148
	 * @access public
149
	 * @static
150
	 *
151
	 * @return array Active Jetpack plan details
152
	 */
153
	public static function get() {
154
		// this can be expensive to compute so we cache for the duration of a request.
155
		if ( is_array( self::$active_plan_cache ) && ! empty( self::$active_plan_cache ) ) {
156
			return self::$active_plan_cache;
157
		}
158
159
		$plan = get_option( self::PLAN_OPTION, array() );
160
161
		// Set the default options.
162
		$plan = wp_parse_args(
163
			$plan,
164
			array(
0 ignored issues
show
array('product_slug' => ...y('active' => array())) is of type array<string,string|arra...active\":\"array\"}>"}>, but the function expects a string.

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...
165
				'product_slug' => 'jetpack_free',
166
				'class'        => 'free',
167
				'features'     => array(
168
					'active' => array(),
169
				),
170
			)
171
		);
172
173
		list( $plan['class'], $supports ) = self::get_class_and_features( $plan['product_slug'] );
174
175
		// get available features.
176
		foreach ( Jetpack::get_available_modules() as $module_slug ) {
177
			$module = Jetpack::get_module( $module_slug );
178
			if ( ! isset( $module ) || ! is_array( $module ) ) {
179
				continue;
180
			}
181
			if ( in_array( 'free', $module['plan_classes'], true ) || in_array( $plan['class'], $module['plan_classes'], true ) ) {
182
				$supports[] = $module_slug;
183
			}
184
		}
185
186
		$plan['supports'] = $supports;
187
188
		self::$active_plan_cache = $plan;
189
190
		return $plan;
191
	}
192
193
	/**
194
	 * Get the class of plan and a list of features it supports
195
	 *
196
	 * @param string $plan_slug The plan that we're interested in.
197
	 * @return array Two item array, the plan class and the an array of features.
198
	 */
199
	private static function get_class_and_features( $plan_slug ) {
200
		$features = array();
201
		foreach ( self::PLAN_DATA as $class => $details ) {
202
			$features = array_merge( $features, $details['supports'] );
203
			if ( in_array( $plan_slug, $details['plans'], true ) ) {
204
				return array( $class, $features );
205
			}
206
		}
207
		return array( 'free', self::PLAN_DATA['free']['supports'] );
208
	}
209
210
	/**
211
	 * Gets the minimum plan slug that supports the given feature
212
	 *
213
	 * @param string $feature The name of the feature.
214
	 * @return string|bool The slug for the minimum plan that supports.
215
	 *  the feature or false if not found
216
	 */
217
	public static function get_minimum_plan_for_feature( $feature ) {
218
		foreach ( self::PLAN_DATA as $class => $details ) {
219
			if ( in_array( $feature, $details['supports'], true ) ) {
220
				return $details['plans'][0];
221
			}
222
		}
223
		return false;
224
	}
225
226
	/**
227
	 * Determine whether the active plan supports a particular feature
228
	 *
229
	 * @uses Jetpack_Plan::get()
230
	 *
231
	 * @access public
232
	 * @static
233
	 *
234
	 * @param string $feature The module or feature to check.
235
	 *
236
	 * @return bool True if plan supports feature, false if not
237
	 */
238
	public static function supports( $feature ) {
239
		// Search product bypasses plan feature check.
240
		if ( 'search' === $feature && (bool) get_option( 'has_jetpack_search_product' ) ) {
241
			return true;
242
		}
243
244
		$plan = self::get();
245
246
		// Manually mapping WordPress.com features to Jetpack module slugs.
247
		foreach ( $plan['features']['active'] as $wpcom_feature ) {
248
			switch ( $wpcom_feature ) {
249
				case 'wordads-jetpack':
250
					// WordAds are supported for this site.
251
					if ( 'wordads' === $feature ) {
252
						return true;
253
					}
254
					break;
255
			}
256
		}
257
258
		if (
259
			in_array( $feature, $plan['supports'], true )
260
			|| in_array( $feature, $plan['features']['active'], true )
261
		) {
262
			return true;
263
		}
264
265
		return false;
266
	}
267
}
268