PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 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 All 506 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php +145 -86 12.8.3 → 16.3-a.5 View file →
@@ -7,9 +7,14 @@
7 7
8 8 namespace Automattic\Jetpack\Sync\Modules;
9 9
10 10 use Automattic\Jetpack\Constants as Jetpack_Constants;
11 +use WP_Error;
11 12
13 +if ( ! defined( 'ABSPATH' ) ) {
14 + exit( 0 );
15 +}
16 +
12 17 /**
13 18 * Class to handle sync for plugins.
14 19 */
15 20 class Plugins extends Module {
@@ -40,8 +45,44 @@
40 45 */
41 46 private $plugins = array();
42 47
43 48 /**
49 + * List of all updated plugins.
50 + *
51 + * @access private
52 + *
53 + * @var array
54 + */
55 + private $plugins_updated = array();
56 +
57 + /**
58 + * List of plugins installed during this request.
59 + *
60 + * @access private
61 + *
62 + * @var array
63 + */
64 + private $plugins_installed = array();
65 +
66 + /**
67 + * List of all plugin update failures during this request.
68 + *
69 + * @access private
70 + *
71 + * @var array
72 + */
73 + private $plugins_update_failures = array();
74 +
75 + /**
76 + * State
77 + *
78 + * @access private
79 + *
80 + * @var array
81 + */
82 + private $state = array();
83 +
84 + /**
44 85 * Sync module name.
45 86 *
46 87 * @access public
47 88 *
@@ -69,9 +110,8 @@
69 110 add_action( 'upgrader_process_complete', array( $this, 'on_upgrader_completion' ), 10, 2 );
70 111 add_action( 'jetpack_plugin_installed', $callable, 10, 1 );
71 112 add_action( 'jetpack_plugin_update_failed', $callable, 10, 4 );
72 113 add_action( 'jetpack_plugins_updated', $callable, 10, 2 );
73 - add_action( 'admin_action_update', array( $this, 'check_plugin_edit' ) );
74 114 add_action( 'jetpack_edited_plugin', $callable, 10, 2 );
75 115 add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'plugin_edit_ajax' ), 0 );
76 116
77 117 // Note that we don't simply 'expand_plugin_data' on the 'delete_plugin' action here because the plugin file is deleted when that action finishes.
@@ -113,9 +153,9 @@
113 153 if ( ! isset( $details['action'] ) ) {
114 154 return;
115 155 }
116 156
117 - $plugins = ( isset( $details['plugins'] ) ? $details['plugins'] : null );
157 + $plugins = ( $details['plugins'] ?? null );
118 158 if ( empty( $plugins ) ) {
119 159 $plugins = ( isset( $details['plugin'] ) ? array( $details['plugin'] ) : null );
120 160 }
121 161
@@ -120,8 +160,9 @@
120 160 }
121 161
122 162 // For plugin installer.
123 163 if ( empty( $plugins ) && method_exists( $upgrader, 'plugin_info' ) ) {
164 + // @phan-suppress-next-line PhanUndeclaredMethod -- Checked above. See also https://github.com/phan/phan/issues/1204.
124 165 $plugins = array( $upgrader->plugin_info() );
125 166 }
126 167
127 168 if ( empty( $plugins ) ) {
@@ -129,61 +170,43 @@
129 170 }
130 171
131 172 switch ( $details['action'] ) {
132 173 case 'update':
133 - $state = array(
174 + $this->state = array(
134 175 'is_autoupdate' => Jetpack_Constants::is_true( 'JETPACK_PLUGIN_AUTOUPDATE' ),
135 176 );
136 - $errors = $this->get_errors( $upgrader->skin );
177 + $errors = $this->get_errors( $upgrader->skin );
137 178 if ( $errors ) {
138 - foreach ( $plugins as $slug ) {
139 - /**
140 - * Sync that a plugin update failed
141 - *
142 - * @since 1.6.3
143 - * @since-jetpack 5.8.0
144 - *
145 - * @module sync
146 - *
147 - * @param string $plugin , Plugin slug
148 - * @param string Error code
149 - * @param string Error message
150 - */
151 - do_action( 'jetpack_plugin_update_failed', $this->get_plugin_info( $slug ), $errors['code'], $errors['message'], $state );
179 + foreach ( $plugins as $slug ) { // Accumulate failures and defer to shutdown, to reduce request-time lag.
180 + $this->plugins_update_failures[] = array(
181 + 'plugin' => $this->get_plugin_info( $slug ),
182 + 'code' => $errors['code'],
183 + 'message' => $errors['message'],
184 + 'state' => $this->state,
185 + );
152 186 }
187 + if ( ! has_action( 'shutdown', array( $this, 'sync_plugins_update_failed' ) ) ) {
188 + add_action( 'shutdown', array( $this, 'sync_plugins_update_failed' ), 9 );
189 + }
153 190
154 191 return;
155 192 }
156 - /**
157 - * Sync that a plugin update
158 - *
159 - * @since 1.6.3
160 - * @since-jetpack 5.8.0
161 - *
162 - * @module sync
163 - *
164 - * @param array () $plugin, Plugin Data
165 - */
166 - do_action( 'jetpack_plugins_updated', array_map( array( $this, 'get_plugin_info' ), $plugins ), $state );
193 +
194 + $this->plugins_updated = array_map( array( $this, 'get_plugin_info' ), $plugins );
195 + add_action( 'shutdown', array( $this, 'sync_plugins_updated' ), 9 );
196 +
167 197 break;
168 198 case 'install':
169 - }
199 + // Accumulate installs and defer to shutdown.
200 + $this->plugins_installed = array_merge(
201 + $this->plugins_installed,
202 + array_map( array( $this, 'get_plugin_info' ), $plugins )
203 + );
204 + if ( ! has_action( 'shutdown', array( $this, 'sync_plugins_installed' ) ) ) {
205 + add_action( 'shutdown', array( $this, 'sync_plugins_installed' ), 9 );
206 + }
170 207
171 - if ( 'install' === $details['action'] ) {
172 - /**
173 - * Signals to the sync listener that a plugin was installed and a sync action
174 - * reflecting the installation and the plugin info should be sent
175 - *
176 - * @since 1.6.3
177 - * @since-jetpack 5.8.0
178 - *
179 - * @module sync
180 - *
181 - * @param array () $plugin, Plugin Data
182 - */
183 - do_action( 'jetpack_plugin_installed', array_map( array( $this, 'get_plugin_info' ), $plugins ) );
184 -
185 - return;
208 + break;
186 209 }
187 210 }
188 211
189 212 /**
@@ -211,8 +234,9 @@
211 234 * @param \Automatic_Upgrader_Skin|\WP_Upgrader_Skin $skin The upgrader skin being used.
212 235 * @return array|boolean Error on error, false otherwise.
213 236 */
214 237 private function get_errors( $skin ) {
238 + // @phan-suppress-next-line PhanUndeclaredMethod -- Checked before being called. See also https://github.com/phan/phan/issues/1204.
215 239 $errors = method_exists( $skin, 'get_errors' ) ? $skin->get_errors() : null;
216 240 if ( is_wp_error( $errors ) ) {
217 241 $error_code = $errors->get_error_code();
218 242 if ( ! empty( $error_code ) ) {
@@ -242,41 +266,8 @@
242 266 return false;
243 267 }
244 268
245 269 /**
246 - * Handle plugin edit in the administration.
247 - *
248 - * @access public
249 - *
250 - * @todo The `admin_action_update` hook is called only for logged in users, but maybe implement nonce verification?
251 - */
252 - public function check_plugin_edit() {
253 - $screen = get_current_screen();
254 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
255 - if ( 'plugin-editor' !== $screen->base || ! isset( $_POST['newcontent'] ) || ! isset( $_POST['plugin'] ) ) {
256 - return;
257 - }
258 -
259 - // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
260 - $plugin = wp_unslash( $_POST['plugin'] );
261 - $plugins = get_plugins();
262 - if ( ! isset( $plugins[ $plugin ] ) ) {
263 - return;
264 - }
265 -
266 - /**
267 - * Helps Sync log that a plugin was edited
268 - *
269 - * @since 1.6.3
270 - * @since-jetpack 4.9.0
271 - *
272 - * @param string $plugin, Plugin slug
273 - * @param mixed $plugins[ $plugin ], Array of plugin data
274 - */
275 - do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
276 - }
277 -
278 - /**
279 270 * Handle plugin ajax edit in the administration.
280 271 *
281 272 * @access public
282 273 *
@@ -283,36 +274,35 @@
283 274 * @todo Update this method to use WP_Filesystem instead of fopen/fclose.
284 275 */
285 276 public function plugin_edit_ajax() {
286 277 // This validation is based on wp_edit_theme_plugin_file().
287 - $args = wp_unslash( $_POST );
288 - if ( empty( $args['file'] ) ) {
278 + if ( empty( $_POST['file'] ) ) {
289 279 return;
290 280 }
291 281
292 - $file = $args['file'];
282 + $file = wp_unslash( $_POST['file'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
293 283 if ( 0 !== validate_file( $file ) ) {
294 284 return;
295 285 }
296 286
297 - if ( ! isset( $args['newcontent'] ) ) {
287 + if ( ! isset( $_POST['newcontent'] ) ) {
298 288 return;
299 289 }
300 290
301 - if ( ! isset( $args['nonce'] ) ) {
291 + if ( ! isset( $_POST['nonce'] ) ) {
302 292 return;
303 293 }
304 294
305 - if ( empty( $args['plugin'] ) ) {
295 + if ( empty( $_POST['plugin'] ) ) {
306 296 return;
307 297 }
308 298
309 - $plugin = $args['plugin'];
299 + $plugin = wp_unslash( $_POST['plugin'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
310 300 if ( ! current_user_can( 'edit_plugins' ) ) {
311 301 return;
312 302 }
313 303
314 - if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) {
304 + if ( ! wp_verify_nonce( $_POST['nonce'], 'edit-plugin_' . $file ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP core doesn't pre-sanitize nonces either.
315 305 return;
316 306 }
317 307 $plugins = get_plugins();
318 308 if ( ! array_key_exists( $plugin, $plugins ) ) {
@@ -409,6 +399,75 @@
409 399 $args[0],
410 400 $args[1],
411 401 $plugin_data,
412 402 );
403 + }
404 +
405 + /**
406 + * Helper method for firing the 'jetpack_plugins_updated' action on shutdown.
407 + *
408 + * @access public
409 + */
410 + public function sync_plugins_updated() {
411 + /**
412 + * Sync that a plugin update
413 + *
414 + * @since 1.6.3
415 + * @since-jetpack 5.8.0
416 + *
417 + * @module sync
418 + *
419 + * @param array () $plugin, Plugin Data
420 + */
421 + do_action( 'jetpack_plugins_updated', $this->plugins_updated, $this->state );
422 + }
423 +
424 + /**
425 + * Helper method for firing the 'jetpack_plugin_installed' action on shutdown.
426 + *
427 + * @access public
428 + */
429 + public function sync_plugins_installed() {
430 + if ( empty( $this->plugins_installed ) ) {
431 + return;
432 + }
433 + /**
434 + * Signals to the sync listener that a plugin was installed and a sync action
435 + * reflecting the installation and the plugin info should be sent.
436 + *
437 + * @since 1.6.3
438 + * @since-jetpack 5.8.0
439 + *
440 + * @module sync
441 + *
442 + * @param array () $plugin, Plugin Data
443 + */
444 + do_action( 'jetpack_plugin_installed', $this->plugins_installed );
445 + }
446 +
447 + /**
448 + * Helper method for firing the 'jetpack_plugin_update_failed' actions on shutdown.
449 + *
450 + * @access public
451 + */
452 + public function sync_plugins_update_failed() {
453 + if ( empty( $this->plugins_update_failures ) ) {
454 + return;
455 + }
456 + foreach ( $this->plugins_update_failures as $failure ) {
457 + /**
458 + * Sync that a plugin update failed
459 + *
460 + * @since 1.6.3
461 + * @since-jetpack 5.8.0
462 + *
463 + * @module sync
464 + *
465 + * @param array $plugin Plugin Data
466 + * @param string $code Error code
467 + * @param string $message Error message
468 + * @param array $state State data
469 + */
470 + do_action( 'jetpack_plugin_update_failed', $failure['plugin'], $failure['code'], $failure['message'], $failure['state'] );
471 + }
413 472 }
414 473 }