Merge remote-tracking branch 'refs/remotes/origin/extract-scheduler'

This commit is contained in:
Yahnis Elsts 2016-01-14 20:17:11 +02:00
commit f9373e7149
3 changed files with 368 additions and 291 deletions

View File

@ -48,20 +48,21 @@ class PluginUpdateCheckerPanel extends Debug_Bar_Panel {
}
$this->row('Metadata URL', htmlentities($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
if ( $this->updateChecker->checkPeriod > 0 ) {
$this->row('Automatic checks', 'Every ' . $this->updateChecker->checkPeriod . ' hours');
$scheduler = $this->updateChecker->scheduler;
if ( $scheduler->checkPeriod > 0 ) {
$this->row('Automatic checks', 'Every ' . $scheduler->checkPeriod . ' hours');
} else {
$this->row('Automatic checks', 'Disabled');
}
if ( isset($this->updateChecker->throttleRedundantChecks) ) {
if ( $this->updateChecker->throttleRedundantChecks && ($this->updateChecker->checkPeriod > 0) ) {
if ( isset($scheduler->throttleRedundantChecks) ) {
if ( $scheduler->throttleRedundantChecks && ($scheduler->checkPeriod > 0) ) {
$this->row(
'Throttling',
sprintf(
'Enabled. If an update is already available, check for updates every %1$d hours instead of every %2$d hours.',
$this->updateChecker->throttledCheckPeriod,
$this->updateChecker->checkPeriod
$scheduler->throttledCheckPeriod,
$scheduler->checkPeriod
)
);
} else {
@ -86,7 +87,7 @@ class PluginUpdateCheckerPanel extends Debug_Bar_Panel {
$this->row('Last check', 'Never');
}
$nextCheck = wp_next_scheduled($this->updateChecker->getCronHookName());
$nextCheck = wp_next_scheduled($this->updateChecker->scheduler->getCronHookName());
$this->row('Next automatic check', $this->formatTimeWithDelta($nextCheck));
if ( isset($state, $state->checkedVersion) ) {

View File

@ -1,8 +1,8 @@
<?php
if ( !class_exists('PucGitHubChecker_2_3', false) ):
if ( !class_exists('PucGitHubChecker_3_0', false) ):
class PucGitHubChecker_2_3 extends PluginUpdateChecker_2_3 {
class PucGitHubChecker_3_0 extends PluginUpdateChecker_3_0 {
/**
* @var string GitHub username.
*/
@ -57,7 +57,7 @@ class PucGitHubChecker_2_3 extends PluginUpdateChecker_2_3 {
* @return PluginInfo
*/
public function requestInfo($unusedQueryArgs = array()) {
$info = new PluginInfo_2_3();
$info = new PluginInfo_3_0();
$info->filename = $this->pluginFile;
$info->slug = $this->slug;
$info->sections = array();
@ -360,7 +360,7 @@ class PucGitHubChecker_2_3 extends PluginUpdateChecker_2_3 {
* Copy plugin metadata from a file header to a PluginInfo object.
*
* @param array $fileHeader
* @param PluginInfo_2_3 $pluginInfo
* @param PluginInfo_3_0 $pluginInfo
*/
protected function setInfoFromHeader($fileHeader, $pluginInfo) {
$headerToPropertyMap = array(
@ -394,7 +394,7 @@ class PucGitHubChecker_2_3 extends PluginUpdateChecker_2_3 {
* Copy plugin metadata from the remote readme.txt file.
*
* @param string $ref GitHub tag or branch where to look for the readme.
* @param PluginInfo_2_3 $pluginInfo
* @param PluginInfo_3_0 $pluginInfo
*/
protected function setInfoFromRemoteReadme($ref, $pluginInfo) {
$readmeTxt = $this->getRemoteFile('readme.txt', $ref);

View File

@ -1,43 +1,39 @@
<?php
/**
* Plugin Update Checker Library 2.3.0
* Plugin Update Checker Library 3.0
* http://w-shadow.com/
*
* Copyright 2015 Janis Elsts
* Copyright 2016 Janis Elsts
* Released under the MIT license. See license.txt for details.
*/
if ( !class_exists('PluginUpdateChecker_2_3', false) ):
if ( !class_exists('PluginUpdateChecker_3_0', false) ):
/**
* A custom plugin update checker.
*
* @author Janis Elsts
* @copyright 2015
* @version 2.3
* @copyright 2016
* @version 3.0
* @access public
*/
class PluginUpdateChecker_2_3 {
class PluginUpdateChecker_3_0 {
public $metadataUrl = ''; //The URL of the plugin's metadata file.
public $pluginAbsolutePath = ''; //Full path of the main plugin file.
public $pluginFile = ''; //Plugin filename relative to the plugins directory. Many WP APIs use this to identify plugins.
public $slug = ''; //Plugin slug.
public $checkPeriod = 12; //How often to check for updates (in hours).
public $optionName = ''; //Where to store the update info.
public $muPluginFile = ''; //For MU plugins, the plugin filename relative to the mu-plugins directory.
public $debugMode = false; //Set to TRUE to enable error reporting. Errors are raised using trigger_error()
//and should be logged to the standard PHP error log.
public $scheduler;
public $throttleRedundantChecks = false; //Check less often if we already know that an update is available.
public $throttledCheckPeriod = 72;
protected $upgraderStatus;
private $cronHook = null;
private $debugBarPlugin = null;
private $cachedInstalledVersion = null;
private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress.
private $metadataHost = ''; //The host component of $metadataUrl.
/**
@ -55,7 +51,6 @@ class PluginUpdateChecker_2_3 {
$this->pluginAbsolutePath = $pluginFile;
$this->pluginFile = plugin_basename($this->pluginAbsolutePath);
$this->muPluginFile = $muPluginFile;
$this->checkPeriod = $checkPeriod;
$this->slug = $slug;
$this->optionName = $optionName;
$this->debugMode = (bool)(constant('WP_DEBUG'));
@ -75,9 +70,25 @@ class PluginUpdateChecker_2_3 {
if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) {
$this->muPluginFile = $this->pluginFile;
}
$this->scheduler = $this->createScheduler($checkPeriod);
$this->upgraderStatus = new PucUpgraderStatus_3_0();
$this->installHooks();
}
/**
* Create an instance of the scheduler.
*
* This is implemented as a method to make it possible for plugins to subclass the update checker
* and substitute their own scheduler.
*
* @param int $checkPeriod
* @return PucScheduler_3_0
*/
protected function createScheduler($checkPeriod) {
return new PucScheduler_3_0($this, $checkPeriod);
}
/**
* Install the hooks required to run periodic update checks and inject update info
@ -101,50 +112,6 @@ class PluginUpdateChecker_2_3 {
add_filter('upgrader_post_install', array($this, 'clearCachedVersion'));
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion'));
//Set up the periodic update checks
$this->cronHook = 'check_plugin_updates-' . $this->slug;
if ( $this->checkPeriod > 0 ){
//Trigger the check via Cron.
//Try to use one of the default schedules if possible as it's less likely to conflict
//with other plugins and their custom schedules.
$defaultSchedules = array(
1 => 'hourly',
12 => 'twicedaily',
24 => 'daily',
);
if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) {
$scheduleName = $defaultSchedules[$this->checkPeriod];
} else {
//Use a custom cron schedule.
$scheduleName = 'every' . $this->checkPeriod . 'hours';
add_filter('cron_schedules', array($this, '_addCustomSchedule'));
}
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
wp_schedule_event(time(), $scheduleName, $this->cronHook);
}
add_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
register_deactivation_hook($this->pluginFile, array($this, '_removeUpdaterCron'));
//In case Cron is disabled or unreliable, we also manually trigger
//the periodic checks while the user is browsing the Dashboard.
add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
//Like WordPress itself, we check more often on certain pages.
/** @see wp_update_plugins */
add_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
add_action('load-plugins.php', array($this, 'maybeCheckForUpdates'));
add_action('load-update.php', array($this, 'maybeCheckForUpdates'));
//This hook fires after a bulk update is complete.
add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0);
} else {
//Periodic checks are disabled.
wp_clear_scheduled_hook($this->cronHook);
}
if ( did_action('plugins_loaded') ) {
$this->initDebugBarPanel();
} else {
@ -168,41 +135,6 @@ class PluginUpdateChecker_2_3 {
add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2);
}
/**
* Add our custom schedule to the array of Cron schedules used by WP.
*
* @param array $schedules
* @return array
*/
public function _addCustomSchedule($schedules){
if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
$scheduleName = 'every' . $this->checkPeriod . 'hours';
$schedules[$scheduleName] = array(
'interval' => $this->checkPeriod * 3600,
'display' => sprintf('Every %d hours', $this->checkPeriod),
);
}
return $schedules;
}
/**
* Remove the scheduled cron event that the library uses to check for updates.
*
* @return void
*/
public function _removeUpdaterCron(){
wp_clear_scheduled_hook($this->cronHook);
}
/**
* Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
*
* @return string
*/
public function getCronHookName() {
return $this->cronHook;
}
/**
* Explicitly allow HTTP requests to the metadata URL.
*
@ -266,7 +198,7 @@ class PluginUpdateChecker_2_3 {
$status = $this->validateApiResponse($result);
$pluginInfo = null;
if ( !is_wp_error($status) ){
$pluginInfo = PluginInfo_2_3::fromJson($result['body']);
$pluginInfo = PluginInfo_3_0::fromJson($result['body']);
if ( $pluginInfo !== null ) {
$pluginInfo->filename = $this->pluginFile;
$pluginInfo->slug = $this->slug;
@ -326,7 +258,7 @@ class PluginUpdateChecker_2_3 {
if ( $pluginInfo == null ){
return null;
}
return PluginUpdate_2_3::fromPluginInfo($pluginInfo);
return PluginUpdate_3_0::fromPluginInfo($pluginInfo);
}
/**
@ -416,62 +348,6 @@ class PluginUpdateChecker_2_3 {
return $this->getUpdate();
}
/**
* Check for updates if the configured check interval has already elapsed.
* Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron.
*
* You can override the default behaviour by using the "puc_check_now-$slug" filter.
* The filter callback will be passed three parameters:
* - Current decision. TRUE = check updates now, FALSE = don't check now.
* - Last check time as a Unix timestamp.
* - Configured check period in hours.
* Return TRUE to check for updates immediately, or FALSE to cancel.
*
* This method is declared public because it's a hook callback. Calling it directly is not recommended.
*/
public function maybeCheckForUpdates(){
if ( empty($this->checkPeriod) ){
return;
}
$currentFilter = current_filter();
if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) {
//Check more often when the user visits "Dashboard -> Updates" or does a bulk update.
$timeout = 60;
} else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) {
//Also check more often on the "Plugins" page and /wp-admin/update.php.
$timeout = 3600;
} else if ( $this->throttleRedundantChecks && ($this->getUpdate() !== null) ) {
//Check less frequently if it's already known that an update is available.
$timeout = $this->throttledCheckPeriod * 3600;
} else if ( defined('DOING_CRON') && constant('DOING_CRON') ) {
//WordPress cron schedules are not exact, so lets do an update check even
//if slightly less than $checkPeriod hours have elapsed since the last check.
$cronFuzziness = 20 * 60;
$timeout = $this->checkPeriod * 3600 - $cronFuzziness;
} else {
$timeout = $this->checkPeriod * 3600;
}
$state = $this->getUpdateState();
$shouldCheck =
empty($state) ||
!isset($state->lastCheck) ||
( (time() - $state->lastCheck) >= $timeout );
//Let plugin authors substitute their own algorithm.
$shouldCheck = apply_filters(
'puc_check_now-' . $this->slug,
$shouldCheck,
(!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0,
$this->checkPeriod
);
if ( $shouldCheck ){
$this->checkForUpdates();
}
}
/**
* Load the update checker state from the DB.
*
@ -484,7 +360,7 @@ class PluginUpdateChecker_2_3 {
}
if ( !empty($state) && isset($state->update) && is_object($state->update) ){
$state->update = PluginUpdate_2_3::fromObject($state->update);
$state->update = PluginUpdate_3_0::fromObject($state->update);
}
return $state;
}
@ -703,121 +579,11 @@ class PluginUpdateChecker_2_3 {
/**
* Is there and update being installed RIGHT NOW, for this specific plugin?
*
* Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading,
* and upgrader implementations are liable to change without notice.
*
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
* @return bool
*/
public function isPluginBeingUpgraded($upgrader = null) {
if ( isset($upgrader) ) {
$pluginFile = $this->getPluginBeingUpgradedBy($upgrader);
if ( !empty($pluginFile) ) {
$this->upgradedPluginFile = $pluginFile;
}
}
return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $this->pluginFile) );
}
/**
* Get the file name of the plugin that's currently being upgraded.
*
* @param Plugin_Upgrader|WP_Upgrader $upgrader
* @return string|null
*/
private function getPluginBeingUpgradedBy($upgrader) {
if ( !isset($upgrader, $upgrader->skin) ) {
return null;
}
//Figure out which plugin is being upgraded.
$pluginFile = null;
$skin = $upgrader->skin;
if ( $skin instanceof Plugin_Upgrader_Skin ) {
if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) {
$pluginFile = $skin->plugin;
}
} elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) {
//This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin
//filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can
//do is compare those headers to the headers of installed plugins.
$pluginFile = $this->identifyPluginByHeaders($skin->plugin_info);
}
return $pluginFile;
}
/**
* Identify an installed plugin based on its headers.
*
* @param array $searchHeaders The plugin file header to look for.
* @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin.
*/
private function identifyPluginByHeaders($searchHeaders) {
if ( !function_exists('get_plugins') ){
/** @noinspection PhpIncludeInspection */
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
}
$installedPlugins = get_plugins();
$matches = array();
foreach($installedPlugins as $pluginBasename => $headers) {
$diff1 = array_diff_assoc($headers, $searchHeaders);
$diff2 = array_diff_assoc($searchHeaders, $headers);
if ( empty($diff1) && empty($diff2) ) {
$matches[] = $pluginBasename;
}
}
//It's possible (though very unlikely) that there could be two plugins with identical
//headers. In that case, we can't unambiguously identify the plugin that's being upgraded.
if ( count($matches) !== 1 ) {
return null;
}
return reset($matches);
}
/**
* @access private
*
* @param mixed $input
* @param array $hookExtra
* @return mixed Returns $input unaltered.
*/
public function setUpgradedPlugin($input, $hookExtra) {
if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) {
$this->upgradedPluginFile = $hookExtra['plugin'];
} else {
$this->upgradedPluginFile = null;
}
return $input;
}
/**
* @access private
*
* @param array $options
* @return array
*/
public function setUpgradedPluginFromOptions($options) {
if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) {
$this->upgradedPluginFile = $options['hook_extra']['plugin'];
} else {
$this->upgradedPluginFile = null;
}
return $options;
}
/**
* @access private
*
* @param mixed $input
* @return mixed Returns $input unaltered.
*/
public function clearUpgradedPlugin($input = null) {
$this->upgradedPluginFile = null;
return $input;
return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader);
}
/**
@ -1065,17 +831,17 @@ class PluginUpdateChecker_2_3 {
endif;
if ( !class_exists('PluginInfo_2_3', false) ):
if ( !class_exists('PluginInfo_3_0', false) ):
/**
* A container class for holding and transforming various plugin metadata.
*
* @author Janis Elsts
* @copyright 2015
* @version 2.3
* @copyright 2016
* @version 3.0
* @access public
*/
class PluginInfo_2_3 {
class PluginInfo_3_0 {
//Most fields map directly to the contents of the plugin's info.json file.
//See the relevant docs for a description of their meaning.
public $name;
@ -1206,17 +972,17 @@ class PluginInfo_2_3 {
endif;
if ( !class_exists('PluginUpdate_2_3', false) ):
if ( !class_exists('PluginUpdate_3_0', false) ):
/**
* A simple container class for holding information about an available update.
*
* @author Janis Elsts
* @copyright 2015
* @version 2.3
* @copyright 2016
* @version 3.0
* @access public
*/
class PluginUpdate_2_3 {
class PluginUpdate_3_0 {
public $id = 0;
public $slug;
public $version;
@ -1237,7 +1003,7 @@ class PluginUpdate_2_3 {
//Since update-related information is simply a subset of the full plugin info,
//we can parse the update JSON as if it was a plugin info string, then copy over
//the parts that we care about.
$pluginInfo = PluginInfo_2_3::fromJson($json);
$pluginInfo = PluginInfo_3_0::fromJson($json);
if ( $pluginInfo != null ) {
return self::fromPluginInfo($pluginInfo);
} else {
@ -1321,6 +1087,316 @@ class PluginUpdate_2_3 {
endif;
if ( !class_exists('PucScheduler_3_0', false) ):
/**
* The scheduler decides when and how often to check for updates.
* It calls @see PluginUpdateChecker::checkForUpdates() to perform the actual checks.
*
* @version 3.0
*/
class PucScheduler_3_0 {
public $checkPeriod = 12; //How often to check for updates (in hours).
public $throttleRedundantChecks = false; //Check less often if we already know that an update is available.
public $throttledCheckPeriod = 72;
/**
* @var PluginUpdateChecker_3_0
*/
protected $updateChecker;
private $cronHook = null;
/**
* Scheduler constructor.
*
* @param PluginUpdateChecker_3_0 $updateChecker
* @param int $checkPeriod How often to check for updates (in hours).
*/
public function __construct($updateChecker, $checkPeriod) {
$this->updateChecker = $updateChecker;
$this->checkPeriod = $checkPeriod;
//Set up the periodic update checks
$this->cronHook = 'check_plugin_updates-' . $this->updateChecker->slug;
if ( $this->checkPeriod > 0 ){
//Trigger the check via Cron.
//Try to use one of the default schedules if possible as it's less likely to conflict
//with other plugins and their custom schedules.
$defaultSchedules = array(
1 => 'hourly',
12 => 'twicedaily',
24 => 'daily',
);
if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) {
$scheduleName = $defaultSchedules[$this->checkPeriod];
} else {
//Use a custom cron schedule.
$scheduleName = 'every' . $this->checkPeriod . 'hours';
add_filter('cron_schedules', array($this, '_addCustomSchedule'));
}
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
wp_schedule_event(time(), $scheduleName, $this->cronHook);
}
add_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
register_deactivation_hook($this->updateChecker->pluginFile, array($this, '_removeUpdaterCron'));
//In case Cron is disabled or unreliable, we also manually trigger
//the periodic checks while the user is browsing the Dashboard.
add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
//Like WordPress itself, we check more often on certain pages.
/** @see wp_update_plugins */
add_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
add_action('load-plugins.php', array($this, 'maybeCheckForUpdates'));
add_action('load-update.php', array($this, 'maybeCheckForUpdates'));
//This hook fires after a bulk update is complete.
add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0);
} else {
//Periodic checks are disabled.
wp_clear_scheduled_hook($this->cronHook);
}
}
/**
* Check for updates if the configured check interval has already elapsed.
* Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron.
*
* You can override the default behaviour by using the "puc_check_now-$slug" filter.
* The filter callback will be passed three parameters:
* - Current decision. TRUE = check updates now, FALSE = don't check now.
* - Last check time as a Unix timestamp.
* - Configured check period in hours.
* Return TRUE to check for updates immediately, or FALSE to cancel.
*
* This method is declared public because it's a hook callback. Calling it directly is not recommended.
*/
public function maybeCheckForUpdates(){
if ( empty($this->checkPeriod) ){
return;
}
$currentFilter = current_filter();
if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) {
//Check more often when the user visits "Dashboard -> Updates" or does a bulk update.
$timeout = 60;
} else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) {
//Also check more often on the "Plugins" page and /wp-admin/update.php.
$timeout = 3600;
} else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) {
//Check less frequently if it's already known that an update is available.
$timeout = $this->throttledCheckPeriod * 3600;
} else if ( defined('DOING_CRON') && constant('DOING_CRON') ) {
//WordPress cron schedules are not exact, so lets do an update check even
//if slightly less than $checkPeriod hours have elapsed since the last check.
$cronFuzziness = 20 * 60;
$timeout = $this->checkPeriod * 3600 - $cronFuzziness;
} else {
$timeout = $this->checkPeriod * 3600;
}
$state = $this->updateChecker->getUpdateState();
$shouldCheck =
empty($state) ||
!isset($state->lastCheck) ||
( (time() - $state->lastCheck) >= $timeout );
//Let plugin authors substitute their own algorithm.
$shouldCheck = apply_filters(
'puc_check_now-' . $this->updateChecker->slug,
$shouldCheck,
(!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0,
$this->checkPeriod
);
if ( $shouldCheck ) {
$this->updateChecker->checkForUpdates();
}
}
/**
* Add our custom schedule to the array of Cron schedules used by WP.
*
* @param array $schedules
* @return array
*/
public function _addCustomSchedule($schedules){
if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
$scheduleName = 'every' . $this->checkPeriod . 'hours';
$schedules[$scheduleName] = array(
'interval' => $this->checkPeriod * 3600,
'display' => sprintf('Every %d hours', $this->checkPeriod),
);
}
return $schedules;
}
/**
* Remove the scheduled cron event that the library uses to check for updates.
*
* @return void
*/
public function _removeUpdaterCron(){
wp_clear_scheduled_hook($this->cronHook);
}
/**
* Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
*
* @return string
*/
public function getCronHookName() {
return $this->cronHook;
}
}
endif;
/**
* A utility class that helps figure out which plugin WordPress is upgrading.
*
* It may seem strange to have an separate class just for that, but the task is surprisingly complicated.
* Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT).
* This class uses a few workarounds and heuristics to get the file name.
*/
class PucUpgraderStatus_3_0 {
private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress.
public function __construct() {
//Keep track of which plugin WordPress is currently upgrading.
add_filter('upgrader_pre_install', array($this, 'setUpgradedPlugin'), 10, 2);
add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1);
add_filter('upgrader_post_install', array($this, 'clearUpgradedPlugin'), 10, 1);
add_action('upgrader_process_complete', array($this, 'clearUpgradedPlugin'), 10, 1);
}
/**
* Is there and update being installed RIGHT NOW, for a specific plugin?
*
* Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading,
* and upgrader implementations are liable to change without notice.
*
* @param string $pluginFile The plugin to check.
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
* @return bool True if the plugin identified by $pluginFile is being upgraded.
*/
public function isPluginBeingUpgraded($pluginFile, $upgrader = null) {
if ( isset($upgrader) ) {
$upgradedPluginFile = $this->getPluginBeingUpgradedBy($upgrader);
if ( !empty($upgradedPluginFile) ) {
$this->upgradedPluginFile = $upgradedPluginFile;
}
}
return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $pluginFile) );
}
/**
* Get the file name of the plugin that's currently being upgraded.
*
* @param Plugin_Upgrader|WP_Upgrader $upgrader
* @return string|null
*/
private function getPluginBeingUpgradedBy($upgrader) {
if ( !isset($upgrader, $upgrader->skin) ) {
return null;
}
//Figure out which plugin is being upgraded.
$pluginFile = null;
$skin = $upgrader->skin;
if ( $skin instanceof Plugin_Upgrader_Skin ) {
if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) {
$pluginFile = $skin->plugin;
}
} elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) {
//This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin
//filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can
//do is compare those headers to the headers of installed plugins.
$pluginFile = $this->identifyPluginByHeaders($skin->plugin_info);
}
return $pluginFile;
}
/**
* Identify an installed plugin based on its headers.
*
* @param array $searchHeaders The plugin file header to look for.
* @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin.
*/
private function identifyPluginByHeaders($searchHeaders) {
if ( !function_exists('get_plugins') ){
/** @noinspection PhpIncludeInspection */
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
}
$installedPlugins = get_plugins();
$matches = array();
foreach($installedPlugins as $pluginBasename => $headers) {
$diff1 = array_diff_assoc($headers, $searchHeaders);
$diff2 = array_diff_assoc($searchHeaders, $headers);
if ( empty($diff1) && empty($diff2) ) {
$matches[] = $pluginBasename;
}
}
//It's possible (though very unlikely) that there could be two plugins with identical
//headers. In that case, we can't unambiguously identify the plugin that's being upgraded.
if ( count($matches) !== 1 ) {
return null;
}
return reset($matches);
}
/**
* @access private
*
* @param mixed $input
* @param array $hookExtra
* @return mixed Returns $input unaltered.
*/
public function setUpgradedPlugin($input, $hookExtra) {
if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) {
$this->upgradedPluginFile = $hookExtra['plugin'];
} else {
$this->upgradedPluginFile = null;
}
return $input;
}
/**
* @access private
*
* @param array $options
* @return array
*/
public function setUpgradedPluginFromOptions($options) {
if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) {
$this->upgradedPluginFile = $options['hook_extra']['plugin'];
} else {
$this->upgradedPluginFile = null;
}
return $options;
}
/**
* @access private
*
* @param mixed $input
* @return mixed Returns $input unaltered.
*/
public function clearUpgradedPlugin($input = null) {
$this->upgradedPluginFile = null;
return $input;
}
}
if ( !class_exists('PucFactory', false) ):
/**
@ -1413,23 +1489,23 @@ endif;
require_once(dirname(__FILE__) . '/github-checker.php');
//Register classes defined in this file with the factory.
PucFactory::addVersion('PluginUpdateChecker', 'PluginUpdateChecker_2_3', '2.3');
PucFactory::addVersion('PluginUpdate', 'PluginUpdate_2_3', '2.3');
PucFactory::addVersion('PluginInfo', 'PluginInfo_2_3', '2.3');
PucFactory::addVersion('PucGitHubChecker', 'PucGitHubChecker_2_3', '2.3');
PucFactory::addVersion('PluginUpdateChecker', 'PluginUpdateChecker_3_0', '3.0');
PucFactory::addVersion('PluginUpdate', 'PluginUpdate_3_0', '3.0');
PucFactory::addVersion('PluginInfo', 'PluginInfo_3_0', '3.0');
PucFactory::addVersion('PucGitHubChecker', 'PucGitHubChecker_3_0', '3.0');
/**
* Create non-versioned variants of the update checker classes. This allows for backwards
* compatibility with versions that did not use a factory, and it simplifies doc-comments.
*/
if ( !class_exists('PluginUpdateChecker', false) ) {
class PluginUpdateChecker extends PluginUpdateChecker_2_3 { }
class PluginUpdateChecker extends PluginUpdateChecker_3_0 { }
}
if ( !class_exists('PluginUpdate', false) ) {
class PluginUpdate extends PluginUpdate_2_3 {}
class PluginUpdate extends PluginUpdate_3_0 {}
}
if ( !class_exists('PluginInfo', false) ) {
class PluginInfo extends PluginInfo_2_3 {}
class PluginInfo extends PluginInfo_3_0 {}
}