WordPress Version: 5.3
/**
* Remove directory and files of a plugin for a list of plugins.
*
* @since 2.6.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param string[] $plugins List of plugin paths to delete, relative to the plugins directory.
* @param string $deprecated Not used.
* @return bool|null|WP_Error True on success, false if `$plugins` is empty, `WP_Error` on failure.
* `null` if filesystem credentials are required to proceed.
*/
function delete_plugins($plugins, $deprecated = '')
{
global $wp_filesystem;
if (empty($plugins)) {
return false;
}
$checked = array();
foreach ($plugins as $plugin) {
$checked[] = 'checked[]=' . $plugin;
}
$url = wp_nonce_url('plugins.php?action=delete-selected&verify-delete=1&' . implode('&', $checked), 'bulk-plugins');
ob_start();
$credentials = request_filesystem_credentials($url);
$data = ob_get_clean();
if (false === $credentials) {
if (!empty($data)) {
include_once ABSPATH . 'wp-admin/admin-header.php';
echo $data;
include ABSPATH . 'wp-admin/admin-footer.php';
exit;
}
return;
}
if (!WP_Filesystem($credentials)) {
ob_start();
request_filesystem_credentials($url, '', true);
// Failed to connect, Error and request again.
$data = ob_get_clean();
if (!empty($data)) {
include_once ABSPATH . 'wp-admin/admin-header.php';
echo $data;
include ABSPATH . 'wp-admin/admin-footer.php';
exit;
}
return;
}
if (!is_object($wp_filesystem)) {
return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
}
if (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->has_errors()) {
return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors);
}
// Get the base plugin folder.
$plugins_dir = $wp_filesystem->wp_plugins_dir();
if (empty($plugins_dir)) {
return new WP_Error('fs_no_plugins_dir', __('Unable to locate WordPress plugin directory.'));
}
$plugins_dir = trailingslashit($plugins_dir);
$plugin_translations = wp_get_installed_translations('plugins');
$errors = array();
foreach ($plugins as $plugin_file) {
// Run Uninstall hook.
if (is_uninstallable_plugin($plugin_file)) {
uninstall_plugin($plugin_file);
}
/**
* Fires immediately before a plugin deletion attempt.
*
* @since 4.4.0
*
* @param string $plugin_file Path to the plugin file relative to the plugins directory.
*/
do_action('delete_plugin', $plugin_file);
$this_plugin_dir = trailingslashit(dirname($plugins_dir . $plugin_file));
// If plugin is in its own directory, recursively delete the directory.
if (strpos($plugin_file, '/') && $this_plugin_dir != $plugins_dir) {
//base check on if plugin includes directory separator AND that it's not the root plugin folder
$deleted = $wp_filesystem->delete($this_plugin_dir, true);
} else {
$deleted = $wp_filesystem->delete($plugins_dir . $plugin_file);
}
/**
* Fires immediately after a plugin deletion attempt.
*
* @since 4.4.0
*
* @param string $plugin_file Path to the plugin file relative to the plugins directory.
* @param bool $deleted Whether the plugin deletion was successful.
*/
do_action('deleted_plugin', $plugin_file, $deleted);
if (!$deleted) {
$errors[] = $plugin_file;
continue;
}
// Remove language files, silently.
$plugin_slug = dirname($plugin_file);
if ('.' !== $plugin_slug && !empty($plugin_translations[$plugin_slug])) {
$translations = $plugin_translations[$plugin_slug];
foreach ($translations as $translation => $data) {
$wp_filesystem->delete(WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po');
$wp_filesystem->delete(WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo');
$json_translation_files = glob(WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json');
if ($json_translation_files) {
array_map(array($wp_filesystem, 'delete'), $json_translation_files);
}
}
}
}
// Remove deleted plugins from the plugin updates list.
$current = get_site_transient('update_plugins');
if ($current) {
// Don't remove the plugins that weren't deleted.
$deleted = array_diff($plugins, $errors);
foreach ($deleted as $plugin_file) {
unset($current->response[$plugin_file]);
}
set_site_transient('update_plugins', $current);
}
if (!empty($errors)) {
if (1 === count($errors)) {
/* translators: %s: Plugin filename. */
$message = __('Could not fully remove the plugin %s.');
} else {
/* translators: %s: Comma-separated list of plugin filenames. */
$message = __('Could not fully remove the plugins %s.');
}
return new WP_Error('could_not_remove_plugin', sprintf($message, implode(', ', $errors)));
}
return true;
}