PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
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 13.9.2 14.0.1 All 504 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php +149 -97 12.0.3 → 16.3-a.1 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,22 +110,14 @@
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 - }
77 116
78 - /**
79 - * Initialize the module in the sender.
80 - *
81 - * @access public
82 - */
83 - public function init_before_send() {
84 - add_filter( 'jetpack_sync_before_send_activated_plugin', array( $this, 'expand_plugin_data' ) );
85 - add_filter( 'jetpack_sync_before_send_deactivated_plugin', array( $this, 'expand_plugin_data' ) );
86 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.
118 + add_filter( 'jetpack_sync_before_enqueue_activated_plugin', array( $this, 'expand_plugin_data' ) );
119 + add_filter( 'jetpack_sync_before_enqueue_deactivated_plugin', array( $this, 'expand_plugin_data' ) );
87 120 }
88 121
89 122 /**
90 123 * Fetch and populate all current plugins before upgrader installation.
@@ -120,9 +153,9 @@
120 153 if ( ! isset( $details['action'] ) ) {
121 154 return;
122 155 }
123 156
124 - $plugins = ( isset( $details['plugins'] ) ? $details['plugins'] : null );
157 + $plugins = ( $details['plugins'] ?? null );
125 158 if ( empty( $plugins ) ) {
126 159 $plugins = ( isset( $details['plugin'] ) ? array( $details['plugin'] ) : null );
127 160 }
128 161
@@ -127,8 +160,9 @@
127 160 }
128 161
129 162 // For plugin installer.
130 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.
131 165 $plugins = array( $upgrader->plugin_info() );
132 166 }
133 167
134 168 if ( empty( $plugins ) ) {
@@ -136,61 +170,43 @@
136 170 }
137 171
138 172 switch ( $details['action'] ) {
139 173 case 'update':
140 - $state = array(
174 + $this->state = array(
141 175 'is_autoupdate' => Jetpack_Constants::is_true( 'JETPACK_PLUGIN_AUTOUPDATE' ),
142 176 );
143 - $errors = $this->get_errors( $upgrader->skin );
177 + $errors = $this->get_errors( $upgrader->skin );
144 178 if ( $errors ) {
145 - foreach ( $plugins as $slug ) {
146 - /**
147 - * Sync that a plugin update failed
148 - *
149 - * @since 1.6.3
150 - * @since-jetpack 5.8.0
151 - *
152 - * @module sync
153 - *
154 - * @param string $plugin , Plugin slug
155 - * @param string Error code
156 - * @param string Error message
157 - */
158 - 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 + );
159 186 }
187 + if ( ! has_action( 'shutdown', array( $this, 'sync_plugins_update_failed' ) ) ) {
188 + add_action( 'shutdown', array( $this, 'sync_plugins_update_failed' ), 9 );
189 + }
160 190
161 191 return;
162 192 }
163 - /**
164 - * Sync that a plugin update
165 - *
166 - * @since 1.6.3
167 - * @since-jetpack 5.8.0
168 - *
169 - * @module sync
170 - *
171 - * @param array () $plugin, Plugin Data
172 - */
173 - 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 +
174 197 break;
175 198 case 'install':
176 - }
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 + }
177 207
178 - if ( 'install' === $details['action'] ) {
179 - /**
180 - * Signals to the sync listener that a plugin was installed and a sync action
181 - * reflecting the installation and the plugin info should be sent
182 - *
183 - * @since 1.6.3
184 - * @since-jetpack 5.8.0
185 - *
186 - * @module sync
187 - *
188 - * @param array () $plugin, Plugin Data
189 - */
190 - do_action( 'jetpack_plugin_installed', array_map( array( $this, 'get_plugin_info' ), $plugins ) );
191 -
192 - return;
208 + break;
193 209 }
194 210 }
195 211
196 212 /**
@@ -218,8 +234,9 @@
218 234 * @param \Automatic_Upgrader_Skin|\WP_Upgrader_Skin $skin The upgrader skin being used.
219 235 * @return array|boolean Error on error, false otherwise.
220 236 */
221 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.
222 239 $errors = method_exists( $skin, 'get_errors' ) ? $skin->get_errors() : null;
223 240 if ( is_wp_error( $errors ) ) {
224 241 $error_code = $errors->get_error_code();
225 242 if ( ! empty( $error_code ) ) {
@@ -249,41 +266,8 @@
249 266 return false;
250 267 }
251 268
252 269 /**
253 - * Handle plugin edit in the administration.
254 - *
255 - * @access public
256 - *
257 - * @todo The `admin_action_update` hook is called only for logged in users, but maybe implement nonce verification?
258 - */
259 - public function check_plugin_edit() {
260 - $screen = get_current_screen();
261 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
262 - if ( 'plugin-editor' !== $screen->base || ! isset( $_POST['newcontent'] ) || ! isset( $_POST['plugin'] ) ) {
263 - return;
264 - }
265 -
266 - // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
267 - $plugin = wp_unslash( $_POST['plugin'] );
268 - $plugins = get_plugins();
269 - if ( ! isset( $plugins[ $plugin ] ) ) {
270 - return;
271 - }
272 -
273 - /**
274 - * Helps Sync log that a plugin was edited
275 - *
276 - * @since 1.6.3
277 - * @since-jetpack 4.9.0
278 - *
279 - * @param string $plugin, Plugin slug
280 - * @param mixed $plugins[ $plugin ], Array of plugin data
281 - */
282 - do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
283 - }
284 -
285 - /**
286 270 * Handle plugin ajax edit in the administration.
287 271 *
288 272 * @access public
289 273 *
@@ -290,36 +274,35 @@
290 274 * @todo Update this method to use WP_Filesystem instead of fopen/fclose.
291 275 */
292 276 public function plugin_edit_ajax() {
293 277 // This validation is based on wp_edit_theme_plugin_file().
294 - $args = wp_unslash( $_POST );
295 - if ( empty( $args['file'] ) ) {
278 + if ( empty( $_POST['file'] ) ) {
296 279 return;
297 280 }
298 281
299 - $file = $args['file'];
282 + $file = wp_unslash( $_POST['file'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
300 283 if ( 0 !== validate_file( $file ) ) {
301 284 return;
302 285 }
303 286
304 - if ( ! isset( $args['newcontent'] ) ) {
287 + if ( ! isset( $_POST['newcontent'] ) ) {
305 288 return;
306 289 }
307 290
308 - if ( ! isset( $args['nonce'] ) ) {
291 + if ( ! isset( $_POST['nonce'] ) ) {
309 292 return;
310 293 }
311 294
312 - if ( empty( $args['plugin'] ) ) {
295 + if ( empty( $_POST['plugin'] ) ) {
313 296 return;
314 297 }
315 298
316 - $plugin = $args['plugin'];
299 + $plugin = wp_unslash( $_POST['plugin'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
317 300 if ( ! current_user_can( 'edit_plugins' ) ) {
318 301 return;
319 302 }
320 303
321 - 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.
322 305 return;
323 306 }
324 307 $plugins = get_plugins();
325 308 if ( ! array_key_exists( $plugin, $plugins ) ) {
@@ -331,10 +314,10 @@
331 314 }
332 315
333 316 $real_file = WP_PLUGIN_DIR . '/' . $file;
334 317
335 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writeable
336 - if ( ! is_writeable( $real_file ) ) {
318 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
319 + if ( ! is_writable( $real_file ) ) {
337 320 return;
338 321 }
339 322
340 323 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
@@ -416,6 +399,75 @@
416 399 $args[0],
417 400 $args[1],
418 401 $plugin_data,
419 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 + }
420 472 }
421 473 }