run:R W Run
7.85 KB
2026-03-11 16:18:51
R W Run
3.54 KB
2026-03-11 16:18:51
R W Run
148.33 KB
2026-03-11 16:18:51
R W Run
11.45 KB
2026-03-11 16:18:51
R W Run
3.58 KB
2026-03-11 16:18:51
R W Run
2.53 KB
2026-03-11 16:18:51
R W Run
2.6 KB
2026-03-11 16:18:51
R W Run
6.59 KB
2026-03-11 16:18:51
R W Run
14.83 KB
2026-03-11 16:18:51
R W Run
21.18 KB
2026-03-11 16:18:51
R W Run
48.13 KB
2026-03-11 16:18:51
R W Run
4.07 KB
2026-03-11 16:18:51
R W Run
5.3 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
26.73 KB
2026-03-11 16:18:51
R W Run
2.8 KB
2026-03-11 16:18:51
R W Run
15.2 KB
2026-03-11 16:18:51
R W Run
192.08 KB
2026-03-11 16:18:51
R W Run
11.77 KB
2026-03-11 16:18:51
R W Run
3.2 KB
2026-03-11 16:18:51
R W Run
22.89 KB
2026-03-11 16:18:51
R W Run
12.77 KB
2026-03-11 16:18:51
R W Run
4.08 KB
2026-03-11 16:18:51
R W Run
26.27 KB
2026-03-11 16:18:51
R W Run
4.97 KB
2026-03-11 16:18:51
R W Run
5.57 KB
2026-03-11 16:18:51
R W Run
13.93 KB
2026-03-11 16:18:51
R W Run
4.09 KB
2026-03-11 16:18:51
R W Run
6.79 KB
2026-03-11 16:18:51
R W Run
60.45 KB
2026-03-11 16:18:51
R W Run
32.4 KB
2026-03-11 16:18:51
R W Run
18.24 KB
2026-03-11 16:18:51
R W Run
66.01 KB
2026-03-11 16:18:51
R W Run
23.84 KB
2026-03-11 16:18:51
R W Run
17.72 KB
2026-03-11 16:18:51
R W Run
22.71 KB
2026-03-11 16:18:51
R W Run
18.05 KB
2026-03-11 16:18:51
R W Run
22.76 KB
2026-03-11 16:18:51
R W Run
7.34 KB
2026-03-11 16:18:51
R W Run
4.51 KB
2026-03-11 16:18:51
R W Run
9.02 KB
2026-03-11 16:18:51
R W Run
1.46 KB
2026-03-11 16:18:51
R W Run
51.76 KB
2026-03-11 16:18:51
R W Run
25.29 KB
2026-03-11 16:18:51
R W Run
21.61 KB
2026-03-11 16:18:51
R W Run
27.77 KB
2026-03-11 16:18:51
R W Run
15.35 KB
2026-03-11 16:18:51
R W Run
24.54 KB
2026-03-11 16:18:51
R W Run
56.44 KB
2026-03-11 16:18:51
R W Run
1.42 KB
2026-03-11 16:18:51
R W Run
63.66 KB
2026-03-11 16:18:51
R W Run
31.9 KB
2026-03-11 16:18:51
R W Run
14.44 KB
2026-03-11 16:18:51
R W Run
36.47 KB
2026-03-11 16:18:51
R W Run
14 KB
2026-03-11 16:18:51
R W Run
121.89 KB
2026-03-11 16:18:51
R W Run
6.26 KB
2026-03-11 16:18:51
R W Run
20.73 KB
2026-03-11 16:18:51
R W Run
15.23 KB
2026-03-11 16:18:51
R W Run
10.14 KB
2026-03-11 16:18:51
R W Run
6.94 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
46.85 KB
2026-03-11 16:18:51
R W Run
18.61 KB
2026-03-11 16:18:51
R W Run
6.08 KB
2026-03-11 16:18:51
R W Run
20.06 KB
2026-03-11 16:18:51
R W Run
5.73 KB
2026-03-11 16:18:51
R W Run
68.18 KB
2026-03-11 16:18:51
R W Run
40.8 KB
2026-03-11 16:18:51
R W Run
1.44 KB
2026-03-11 16:18:51
R W Run
25.26 KB
2026-03-11 16:18:51
R W Run
95.94 KB
2026-03-11 16:18:51
R W Run
43.12 KB
2026-03-11 16:18:51
R W Run
41.73 KB
2026-03-11 16:18:51
R W Run
6.46 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
116.31 KB
2026-03-11 16:18:51
R W Run
9.39 KB
2026-03-11 16:18:51
R W Run
64.34 KB
2026-03-11 16:18:51
R W Run
44.73 KB
2026-03-11 16:18:51
R W Run
1.27 KB
2026-03-11 16:18:51
R W Run
3.68 KB
2026-03-11 16:18:51
R W Run
33.53 KB
2026-03-11 16:18:51
R W Run
48.84 KB
2026-03-11 16:18:51
R W Run
26.35 KB
2026-03-11 16:18:51
R W Run
1.12 KB
2026-03-11 16:18:51
R W Run
4.19 KB
2026-03-11 16:18:51
R W Run
38.19 KB
2026-03-11 16:18:51
R W Run
91.33 KB
2026-03-11 16:18:51
R W Run
80.39 KB
2026-03-11 16:18:51
R W Run
32.67 KB
2026-03-11 16:18:51
R W Run
16.18 KB
2026-03-11 16:18:51
R W Run
44.46 KB
2026-03-11 16:18:51
R W Run
6.23 KB
2026-03-11 16:18:51
R W Run
8.23 KB
2026-03-11 16:18:51
R W Run
96.96 KB
2026-03-11 16:18:51
R W Run
6.83 KB
2026-03-11 16:18:51
R W Run
46.62 KB
2026-03-11 16:18:51
R W Run
10.82 KB
2026-03-11 16:18:51
R W Run
68.86 KB
2026-03-11 16:18:51
R W Run
33.63 KB
2026-03-11 16:18:51
R W Run
113.3 KB
2026-03-11 16:18:51
R W Run
22.98 KB
2026-03-11 16:18:51
R W Run
10.66 KB
2026-03-11 16:18:51
R W Run
error_log
📄class-plugin-upgrader.php
1<?php
2/**
3 * Upgrade API: Plugin_Upgrader class
4 *
5 * @package WordPress
6 * @subpackage Upgrader
7 * @since 4.6.0
8 */
9
10/**
11 * Core class used for upgrading/installing plugins.
12 *
13 * It is designed to upgrade/install plugins from a local zip, remote zip URL,
14 * or uploaded zip file.
15 *
16 * @since 2.8.0
17 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
18 *
19 * @see WP_Upgrader
20 */
21class Plugin_Upgrader extends WP_Upgrader {
22
23 /**
24 * Plugin upgrade result.
25 *
26 * @since 2.8.0
27 * @var array|WP_Error $result
28 *
29 * @see WP_Upgrader::$result
30 */
31 public $result;
32
33 /**
34 * Whether a bulk upgrade/installation is being performed.
35 *
36 * @since 2.9.0
37 * @var bool $bulk
38 */
39 public $bulk = false;
40
41 /**
42 * New plugin info.
43 *
44 * @since 5.5.0
45 * @var array $new_plugin_data
46 *
47 * @see check_package()
48 */
49 public $new_plugin_data = array();
50
51 /**
52 * Initializes the upgrade strings.
53 *
54 * @since 2.8.0
55 */
56 public function upgrade_strings() {
57 $this->strings['up_to_date'] = __( 'The plugin is at the latest version.' );
58 $this->strings['no_package'] = __( 'Update package not available.' );
59 /* translators: %s: Package URL. */
60 $this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s&#8230;' ), '<span class="code pre">%s</span>' );
61 $this->strings['unpack_package'] = __( 'Unpacking the update&#8230;' );
62 $this->strings['remove_old'] = __( 'Removing the old version of the plugin&#8230;' );
63 $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' );
64 $this->strings['process_failed'] = __( 'Plugin update failed.' );
65 $this->strings['process_success'] = __( 'Plugin updated successfully.' );
66 $this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' );
67 }
68
69 /**
70 * Initializes the installation strings.
71 *
72 * @since 2.8.0
73 */
74 public function install_strings() {
75 $this->strings['no_package'] = __( 'Installation package not available.' );
76 /* translators: %s: Package URL. */
77 $this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s&#8230;' ), '<span class="code pre">%s</span>' );
78 $this->strings['unpack_package'] = __( 'Unpacking the package&#8230;' );
79 $this->strings['installing_package'] = __( 'Installing the plugin&#8230;' );
80 $this->strings['remove_old'] = __( 'Removing the current plugin&#8230;' );
81 $this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' );
82 $this->strings['no_files'] = __( 'The plugin contains no files.' );
83 $this->strings['process_failed'] = __( 'Plugin installation failed.' );
84 $this->strings['process_success'] = __( 'Plugin installed successfully.' );
85 /* translators: 1: Plugin name, 2: Plugin version. */
86 $this->strings['process_success_specific'] = __( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' );
87
88 if ( ! empty( $this->skin->overwrite ) ) {
89 if ( 'update-plugin' === $this->skin->overwrite ) {
90 $this->strings['installing_package'] = __( 'Updating the plugin&#8230;' );
91 $this->strings['process_failed'] = __( 'Plugin update failed.' );
92 $this->strings['process_success'] = __( 'Plugin updated successfully.' );
93 }
94
95 if ( 'downgrade-plugin' === $this->skin->overwrite ) {
96 $this->strings['installing_package'] = __( 'Downgrading the plugin&#8230;' );
97 $this->strings['process_failed'] = __( 'Plugin downgrade failed.' );
98 $this->strings['process_success'] = __( 'Plugin downgraded successfully.' );
99 }
100 }
101 }
102
103 /**
104 * Install a plugin package.
105 *
106 * @since 2.8.0
107 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
108 *
109 * @param string $package The full local path or URI of the package.
110 * @param array $args {
111 * Optional. Other arguments for installing a plugin package. Default empty array.
112 *
113 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
114 * Default true.
115 * }
116 * @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise.
117 */
118 public function install( $package, $args = array() ) {
119 $defaults = array(
120 'clear_update_cache' => true,
121 'overwrite_package' => false, // Do not overwrite files.
122 );
123 $parsed_args = wp_parse_args( $args, $defaults );
124
125 $this->init();
126 $this->install_strings();
127
128 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
129
130 if ( $parsed_args['clear_update_cache'] ) {
131 // Clear cache so wp_update_plugins() knows about the new plugin.
132 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 );
133 }
134
135 $this->run(
136 array(
137 'package' => $package,
138 'destination' => WP_PLUGIN_DIR,
139 'clear_destination' => $parsed_args['overwrite_package'],
140 'clear_working' => true,
141 'hook_extra' => array(
142 'type' => 'plugin',
143 'action' => 'install',
144 ),
145 )
146 );
147
148 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
149 remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
150
151 if ( ! $this->result || is_wp_error( $this->result ) ) {
152 return $this->result;
153 }
154
155 // Force refresh of plugin update information.
156 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
157
158 if ( $parsed_args['overwrite_package'] ) {
159 /**
160 * Fires when the upgrader has successfully overwritten a currently installed
161 * plugin or theme with an uploaded zip package.
162 *
163 * @since 5.5.0
164 *
165 * @param string $package The package file.
166 * @param array $data The new plugin or theme data.
167 * @param string $package_type The package type ('plugin' or 'theme').
168 */
169 do_action( 'upgrader_overwrote_package', $package, $this->new_plugin_data, 'plugin' );
170 }
171
172 return true;
173 }
174
175 /**
176 * Upgrades a plugin.
177 *
178 * @since 2.8.0
179 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
180 *
181 * @param string $plugin Path to the plugin file relative to the plugins directory.
182 * @param array $args {
183 * Optional. Other arguments for upgrading a plugin package. Default empty array.
184 *
185 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
186 * Default true.
187 * }
188 * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
189 */
190 public function upgrade( $plugin, $args = array() ) {
191 $defaults = array(
192 'clear_update_cache' => true,
193 );
194 $parsed_args = wp_parse_args( $args, $defaults );
195
196 $this->init();
197 $this->upgrade_strings();
198
199 $current = get_site_transient( 'update_plugins' );
200 if ( ! isset( $current->response[ $plugin ] ) ) {
201 $this->skin->before();
202 $this->skin->set_result( false );
203 $this->skin->error( 'up_to_date' );
204 $this->skin->after();
205 return false;
206 }
207
208 // Get the URL to the zip file.
209 $upgrade_data = $current->response[ $plugin ];
210
211 add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 );
212 add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 );
213 add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
214 add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 );
215 /*
216 * There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins.
217 * 'source_selection' => array( $this, 'source_selection' ),
218 */
219 if ( $parsed_args['clear_update_cache'] ) {
220 // Clear cache so wp_update_plugins() knows about the new plugin.
221 add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 );
222 }
223
224 $this->run(
225 array(
226 'package' => $upgrade_data->package,
227 'destination' => WP_PLUGIN_DIR,
228 'clear_destination' => true,
229 'clear_working' => true,
230 'hook_extra' => array(
231 'plugin' => $plugin,
232 'type' => 'plugin',
233 'action' => 'update',
234 'temp_backup' => array(
235 'slug' => dirname( $plugin ),
236 'src' => WP_PLUGIN_DIR,
237 'dir' => 'plugins',
238 ),
239 ),
240 )
241 );
242
243 // Cleanup our hooks, in case something else does an upgrade on this connection.
244 remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
245 remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) );
246 remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) );
247 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
248 remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) );
249
250 if ( ! $this->result || is_wp_error( $this->result ) ) {
251 return $this->result;
252 }
253
254 // Force refresh of plugin update information.
255 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
256
257 /*
258 * Ensure any future auto-update failures trigger a failure email by removing
259 * the last failure notification from the list when plugins update successfully.
260 */
261 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
262
263 if ( isset( $past_failure_emails[ $plugin ] ) ) {
264 unset( $past_failure_emails[ $plugin ] );
265 update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
266 }
267
268 return true;
269 }
270
271 /**
272 * Upgrades several plugins at once.
273 *
274 * @since 2.8.0
275 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
276 *
277 * @param string[] $plugins Array of paths to plugin files relative to the plugins directory.
278 * @param array $args {
279 * Optional. Other arguments for upgrading several plugins at once.
280 *
281 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. Default true.
282 * }
283 * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem.
284 */
285 public function bulk_upgrade( $plugins, $args = array() ) {
286 $wp_version = wp_get_wp_version();
287
288 $defaults = array(
289 'clear_update_cache' => true,
290 );
291 $parsed_args = wp_parse_args( $args, $defaults );
292
293 $this->init();
294 $this->bulk = true;
295 $this->upgrade_strings();
296
297 $current = get_site_transient( 'update_plugins' );
298
299 add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
300
301 $this->skin->header();
302
303 // Connect to the filesystem first.
304 $connected = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
305 if ( ! $connected ) {
306 $this->skin->footer();
307 return false;
308 }
309
310 $this->skin->bulk_header();
311
312 /*
313 * Only start maintenance mode if:
314 * - running Multisite and there are one or more plugins specified, OR
315 * - a plugin with an update available is currently active.
316 * @todo For multisite, maintenance mode should only kick in for individual sites if at all possible.
317 */
318 $maintenance = ( is_multisite() && ! empty( $plugins ) );
319 foreach ( $plugins as $plugin ) {
320 $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin ] ) );
321 }
322 if ( $maintenance ) {
323 $this->maintenance_mode( true );
324 }
325
326 $results = array();
327
328 $this->update_count = count( $plugins );
329 $this->update_current = 0;
330 foreach ( $plugins as $plugin ) {
331 ++$this->update_current;
332 $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true );
333
334 if ( ! isset( $current->response[ $plugin ] ) ) {
335 $this->skin->set_result( 'up_to_date' );
336 $this->skin->before();
337 $this->skin->feedback( 'up_to_date' );
338 $this->skin->after();
339 $results[ $plugin ] = true;
340 continue;
341 }
342
343 // Get the URL to the zip file.
344 $upgrade_data = $current->response[ $plugin ];
345
346 $this->skin->plugin_active = is_plugin_active( $plugin );
347
348 if ( isset( $upgrade_data->requires ) && ! is_wp_version_compatible( $upgrade_data->requires ) ) {
349 $result = new WP_Error(
350 'incompatible_wp_required_version',
351 sprintf(
352 /* translators: 1: Current WordPress version, 2: WordPress version required by the new plugin version. */
353 __( 'Your WordPress version is %1$s, however the new plugin version requires %2$s.' ),
354 $wp_version,
355 $upgrade_data->requires
356 )
357 );
358
359 $this->skin->before( $result );
360 $this->skin->error( $result );
361 $this->skin->after();
362 } elseif ( isset( $upgrade_data->requires_php ) && ! is_php_version_compatible( $upgrade_data->requires_php ) ) {
363 $result = new WP_Error(
364 'incompatible_php_required_version',
365 sprintf(
366 /* translators: 1: Current PHP version, 2: PHP version required by the new plugin version. */
367 __( 'The PHP version on your server is %1$s, however the new plugin version requires %2$s.' ),
368 PHP_VERSION,
369 $upgrade_data->requires_php
370 )
371 );
372
373 $this->skin->before( $result );
374 $this->skin->error( $result );
375 $this->skin->after();
376 } else {
377 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
378 $result = $this->run(
379 array(
380 'package' => $upgrade_data->package,
381 'destination' => WP_PLUGIN_DIR,
382 'clear_destination' => true,
383 'clear_working' => true,
384 'is_multi' => true,
385 'hook_extra' => array(
386 'plugin' => $plugin,
387 'temp_backup' => array(
388 'slug' => dirname( $plugin ),
389 'src' => WP_PLUGIN_DIR,
390 'dir' => 'plugins',
391 ),
392 ),
393 )
394 );
395 remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
396 }
397
398 $results[ $plugin ] = $result;
399
400 // Prevent credentials auth screen from displaying multiple times.
401 if ( false === $result ) {
402 break;
403 }
404 } // End foreach $plugins.
405
406 $this->maintenance_mode( false );
407
408 // Force refresh of plugin update information.
409 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
410
411 /** This action is documented in wp-admin/includes/class-wp-upgrader.php */
412 do_action(
413 'upgrader_process_complete',
414 $this,
415 array(
416 'action' => 'update',
417 'type' => 'plugin',
418 'bulk' => true,
419 'plugins' => $plugins,
420 )
421 );
422
423 $this->skin->bulk_footer();
424
425 $this->skin->footer();
426
427 // Cleanup our hooks, in case something else does an upgrade on this connection.
428 remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
429
430 /*
431 * Ensure any future auto-update failures trigger a failure email by removing
432 * the last failure notification from the list when plugins update successfully.
433 */
434 $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
435
436 foreach ( $results as $plugin => $result ) {
437 // Maintain last failure notification when plugins failed to update manually.
438 if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $plugin ] ) ) {
439 continue;
440 }
441
442 unset( $past_failure_emails[ $plugin ] );
443 }
444
445 update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
446
447 return $results;
448 }
449
450 /**
451 * Checks that the source package contains a valid plugin.
452 *
453 * Hooked to the {@see 'upgrader_source_selection'} filter by Plugin_Upgrader::install().
454 *
455 * @since 3.3.0
456 *
457 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
458 *
459 * @param string $source The path to the downloaded package source.
460 * @return string|WP_Error The source as passed, or a WP_Error object on failure.
461 */
462 public function check_package( $source ) {
463 global $wp_filesystem;
464
465 $wp_version = wp_get_wp_version();
466 $this->new_plugin_data = array();
467
468 if ( is_wp_error( $source ) ) {
469 return $source;
470 }
471
472 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source );
473 if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation.
474 return $source;
475 }
476
477 // Check that the folder contains at least 1 valid plugin.
478 $files = glob( $working_directory . '*.php' );
479 if ( $files ) {
480 foreach ( $files as $file ) {
481 $new_plugin_data = get_plugin_data( $file, false, false );
482 if ( ! empty( $new_plugin_data['Name'] ) ) {
483 $this->new_plugin_data = $new_plugin_data;
484 break;
485 }
486 }
487 }
488
489 if ( empty( $this->new_plugin_data ) ) {
490 return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) );
491 }
492
493 $requires_php = isset( $new_plugin_data['RequiresPHP'] ) ? $new_plugin_data['RequiresPHP'] : null;
494 $requires_wp = isset( $new_plugin_data['RequiresWP'] ) ? $new_plugin_data['RequiresWP'] : null;
495
496 if ( ! is_php_version_compatible( $requires_php ) ) {
497 $error = sprintf(
498 /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */
499 __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ),
500 PHP_VERSION,
501 $requires_php
502 );
503
504 return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error );
505 }
506
507 if ( ! is_wp_version_compatible( $requires_wp ) ) {
508 $error = sprintf(
509 /* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */
510 __( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ),
511 $wp_version,
512 $requires_wp
513 );
514
515 return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error );
516 }
517
518 return $source;
519 }
520
521 /**
522 * Retrieves the path to the file that contains the plugin info.
523 *
524 * This isn't used internally in the class, but is called by the skins.
525 *
526 * @since 2.8.0
527 *
528 * @return string|false The full path to the main plugin file, or false.
529 */
530 public function plugin_info() {
531 if ( ! is_array( $this->result ) ) {
532 return false;
533 }
534 if ( empty( $this->result['destination_name'] ) ) {
535 return false;
536 }
537
538 // Ensure to pass with leading slash.
539 $plugin = get_plugins( '/' . $this->result['destination_name'] );
540 if ( empty( $plugin ) ) {
541 return false;
542 }
543
544 // Assume the requested plugin is the first in the list.
545 $plugin_files = array_keys( $plugin );
546
547 return $this->result['destination_name'] . '/' . $plugin_files[0];
548 }
549
550 /**
551 * Deactivates a plugin before it is upgraded.
552 *
553 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade().
554 *
555 * @since 2.8.0
556 * @since 4.1.0 Added a return value.
557 *
558 * @param bool|WP_Error $response The installation response before the installation has started.
559 * @param array $plugin Plugin package arguments.
560 * @return bool|WP_Error The original `$response` parameter or WP_Error.
561 */
562 public function deactivate_plugin_before_upgrade( $response, $plugin ) {
563
564 if ( is_wp_error( $response ) ) { // Bypass.
565 return $response;
566 }
567
568 // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it.
569 if ( wp_doing_cron() ) {
570 return $response;
571 }
572
573 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
574 if ( empty( $plugin ) ) {
575 return new WP_Error( 'bad_request', $this->strings['bad_request'] );
576 }
577
578 if ( is_plugin_active( $plugin ) ) {
579 // Deactivate the plugin silently, Prevent deactivation hooks from running.
580 deactivate_plugins( $plugin, true );
581 }
582
583 return $response;
584 }
585
586 /**
587 * Turns on maintenance mode before attempting to background update an active plugin.
588 *
589 * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade().
590 *
591 * @since 5.4.0
592 *
593 * @param bool|WP_Error $response The installation response before the installation has started.
594 * @param array $plugin Plugin package arguments.
595 * @return bool|WP_Error The original `$response` parameter or WP_Error.
596 */
597 public function active_before( $response, $plugin ) {
598 if ( is_wp_error( $response ) ) {
599 return $response;
600 }
601
602 // Only enable maintenance mode when in cron (background update).
603 if ( ! wp_doing_cron() ) {
604 return $response;
605 }
606
607 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
608
609 // Only run if plugin is active.
610 if ( ! is_plugin_active( $plugin ) ) {
611 return $response;
612 }
613
614 // Change to maintenance mode. Bulk edit handles this separately.
615 if ( ! $this->bulk ) {
616 $this->maintenance_mode( true );
617 }
618
619 return $response;
620 }
621
622 /**
623 * Turns off maintenance mode after upgrading an active plugin.
624 *
625 * Hooked to the {@see 'upgrader_post_install'} filter by Plugin_Upgrader::upgrade().
626 *
627 * @since 5.4.0
628 *
629 * @param bool|WP_Error $response The installation response after the installation has finished.
630 * @param array $plugin Plugin package arguments.
631 * @return bool|WP_Error The original `$response` parameter or WP_Error.
632 */
633 public function active_after( $response, $plugin ) {
634 if ( is_wp_error( $response ) ) {
635 return $response;
636 }
637
638 // Only disable maintenance mode when in cron (background update).
639 if ( ! wp_doing_cron() ) {
640 return $response;
641 }
642
643 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
644
645 // Only run if plugin is active.
646 if ( ! is_plugin_active( $plugin ) ) {
647 return $response;
648 }
649
650 // Time to remove maintenance mode. Bulk edit handles this separately.
651 if ( ! $this->bulk ) {
652 $this->maintenance_mode( false );
653 }
654
655 return $response;
656 }
657
658 /**
659 * Deletes the old plugin during an upgrade.
660 *
661 * Hooked to the {@see 'upgrader_clear_destination'} filter by
662 * Plugin_Upgrader::upgrade() and Plugin_Upgrader::bulk_upgrade().
663 *
664 * @since 2.8.0
665 *
666 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
667 *
668 * @param bool|WP_Error $removed Whether the destination was cleared.
669 * True on success, WP_Error on failure.
670 * @param string $local_destination The local package destination.
671 * @param string $remote_destination The remote package destination.
672 * @param array $plugin Extra arguments passed to hooked filters.
673 * @return bool|WP_Error
674 */
675 public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) {
676 global $wp_filesystem;
677
678 if ( is_wp_error( $removed ) ) {
679 return $removed; // Pass errors through.
680 }
681
682 $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : '';
683 if ( empty( $plugin ) ) {
684 return new WP_Error( 'bad_request', $this->strings['bad_request'] );
685 }
686
687 $plugins_dir = $wp_filesystem->wp_plugins_dir();
688 $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin ) );
689
690 if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished.
691 return $removed;
692 }
693
694 /*
695 * If plugin is in its own directory, recursively delete the directory.
696 * Base check on if plugin includes directory separator AND that it's not the root plugin folder.
697 */
698 if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) {
699 $deleted = $wp_filesystem->delete( $this_plugin_dir, true );
700 } else {
701 $deleted = $wp_filesystem->delete( $plugins_dir . $plugin );
702 }
703
704 if ( ! $deleted ) {
705 return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
706 }
707
708 return true;
709 }
710}
711