Enhance WooCommerce version checking for remote logging reliability (#51009)
* Enhance WooCommerce version checking using get_plugin_updates() * Update remote logger tool to toggle remote logging feature properly * Add changelog
This commit is contained in:
parent
44b5f54d08
commit
7971df1d28
|
@ -74,6 +74,7 @@ function toggle_remote_logging( $request ) {
|
||||||
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
|
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
|
||||||
update_option( 'woocommerce_allow_tracking', 'yes' );
|
update_option( 'woocommerce_allow_tracking', 'yes' );
|
||||||
update_option( 'woocommerce_remote_variant_assignment', 1 );
|
update_option( 'woocommerce_remote_variant_assignment', 1 );
|
||||||
|
set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, WC()->version );
|
||||||
} else {
|
} else {
|
||||||
update_option( 'woocommerce_feature_remote_logging_enabled', 'no' );
|
update_option( 'woocommerce_feature_remote_logging_enabled', 'no' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Update remote logger tool to toggle remote logging feature properly
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: enhancement
|
||||||
|
|
||||||
|
Enhance WooCommerce version checking for remote logging reliability
|
|
@ -23,8 +23,7 @@ class RemoteLogger extends \WC_Log_Handler {
|
||||||
const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash';
|
const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash';
|
||||||
const RATE_LIMIT_ID = 'woocommerce_remote_logging';
|
const RATE_LIMIT_ID = 'woocommerce_remote_logging';
|
||||||
const RATE_LIMIT_DELAY = 60; // 1 minute.
|
const RATE_LIMIT_DELAY = 60; // 1 minute.
|
||||||
const WC_LATEST_VERSION_TRANSIENT = 'latest_woocommerce_version';
|
const WC_NEW_VERSION_TRANSIENT = 'woocommerce_new_version';
|
||||||
const FETCH_LATEST_VERSION_RETRY = 'fetch_latest_woocommerce_version_retry';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a log entry.
|
* Handle a log entry.
|
||||||
|
@ -150,7 +149,7 @@ class RemoteLogger extends \WC_Log_Handler {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $this->is_latest_woocommerce_version() ) {
|
if ( ! $this->should_current_version_be_logged() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +220,7 @@ class RemoteLogger extends \WC_Log_Handler {
|
||||||
self::LOG_ENDPOINT,
|
self::LOG_ENDPOINT,
|
||||||
array(
|
array(
|
||||||
'body' => wp_json_encode( $body ),
|
'body' => wp_json_encode( $body ),
|
||||||
'timeout' => 2,
|
'timeout' => 3,
|
||||||
'headers' => array(
|
'headers' => array(
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
),
|
),
|
||||||
|
@ -256,14 +255,22 @@ class RemoteLogger extends \WC_Log_Handler {
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function is_latest_woocommerce_version() {
|
private function should_current_version_be_logged() {
|
||||||
$latest_wc_version = $this->fetch_latest_woocommerce_version();
|
$new_version = get_site_transient( self::WC_NEW_VERSION_TRANSIENT );
|
||||||
|
|
||||||
if ( is_null( $latest_wc_version ) ) {
|
if ( false === $new_version ) {
|
||||||
return false;
|
$new_version = $this->fetch_new_woocommerce_version();
|
||||||
|
// Cache the new version for a week since we want to keep logging in with the same version for a while even if the new version is available.
|
||||||
|
set_site_transient( self::WC_NEW_VERSION_TRANSIENT, $new_version, WEEK_IN_SECONDS );
|
||||||
}
|
}
|
||||||
|
|
||||||
return version_compare( WC()->version, $latest_wc_version, '>=' );
|
if ( ! is_string( $new_version ) || '' === $new_version ) {
|
||||||
|
// If the new version is not available, we consider the current version to be the latest.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current version is the latest, we don't want to log errors.
|
||||||
|
return version_compare( WC()->version, $new_version, '>=' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -316,45 +323,34 @@ class RemoteLogger extends \WC_Log_Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the latest WooCommerce version using the WordPress API and cache it.
|
* Fetch the new version of WooCommerce from the WordPress API.
|
||||||
*
|
*
|
||||||
* @return string|null
|
* @return string|null New version if an update is available, null otherwise.
|
||||||
*/
|
*/
|
||||||
private function fetch_latest_woocommerce_version() {
|
private function fetch_new_woocommerce_version() {
|
||||||
$cached_version = get_transient( self::WC_LATEST_VERSION_TRANSIENT );
|
if ( ! function_exists( 'get_plugins' ) ) {
|
||||||
if ( $cached_version ) {
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
return $cached_version;
|
}
|
||||||
|
if ( ! function_exists( 'get_plugin_updates' ) ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/update.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
$retry_count = get_transient( self::FETCH_LATEST_VERSION_RETRY );
|
$plugin_updates = get_plugin_updates();
|
||||||
if ( false === $retry_count || ! is_numeric( $retry_count ) ) {
|
|
||||||
$retry_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $retry_count >= 3 ) {
|
// Check if WooCommerce plugin update information is available.
|
||||||
|
if ( ! is_array( $plugin_updates ) || ! isset( $plugin_updates[ WC_PLUGIN_BASENAME ] ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! function_exists( 'plugins_api' ) ) {
|
$wc_plugin_update = $plugin_updates[ WC_PLUGIN_BASENAME ];
|
||||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
|
||||||
}
|
|
||||||
// Fetch the latest version from the WordPress API.
|
|
||||||
$plugin_info = plugins_api( 'plugin_information', array( 'slug' => 'woocommerce' ) );
|
|
||||||
|
|
||||||
if ( is_wp_error( $plugin_info ) ) {
|
// Ensure the update object exists and has the required information.
|
||||||
++$retry_count;
|
if ( ! $wc_plugin_update || ! isset( $wc_plugin_update->update->new_version ) ) {
|
||||||
set_transient( self::FETCH_LATEST_VERSION_RETRY, $retry_count, HOUR_IN_SECONDS );
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! empty( $plugin_info->version ) ) {
|
$new_version = $wc_plugin_update->update->new_version;
|
||||||
$latest_version = $plugin_info->version;
|
return is_string( $new_version ) ? $new_version : null;
|
||||||
set_transient( self::WC_LATEST_VERSION_TRANSIENT, $latest_version, WEEK_IN_SECONDS );
|
|
||||||
delete_transient( self::FETCH_LATEST_VERSION_RETRY );
|
|
||||||
return $latest_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,8 +35,7 @@ class RemoteLoggerTest extends \WC_Unit_Test_Case {
|
||||||
public function tearDown(): void {
|
public function tearDown(): void {
|
||||||
$this->cleanup_filters();
|
$this->cleanup_filters();
|
||||||
delete_option( 'woocommerce_feature_remote_logging_enabled' );
|
delete_option( 'woocommerce_feature_remote_logging_enabled' );
|
||||||
delete_transient( RemoteLogger::WC_LATEST_VERSION_TRANSIENT );
|
delete_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT );
|
||||||
delete_transient( RemoteLogger::FETCH_LATEST_VERSION_RETRY );
|
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" );
|
$wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" );
|
||||||
WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP );
|
WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP );
|
||||||
|
@ -56,6 +55,7 @@ class RemoteLoggerTest extends \WC_Unit_Test_Case {
|
||||||
'plugins_api',
|
'plugins_api',
|
||||||
'pre_http_request',
|
'pre_http_request',
|
||||||
'woocommerce_remote_logger_formatted_log_data',
|
'woocommerce_remote_logger_formatted_log_data',
|
||||||
|
'pre_site_transient_update_plugins',
|
||||||
);
|
);
|
||||||
foreach ( $filters as $filter ) {
|
foreach ( $filters as $filter ) {
|
||||||
remove_all_filters( $filter );
|
remove_all_filters( $filter );
|
||||||
|
@ -98,10 +98,15 @@ class RemoteLoggerTest extends \WC_Unit_Test_Case {
|
||||||
'condition' => 'tracking opted out',
|
'condition' => 'tracking opted out',
|
||||||
'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ),
|
'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ),
|
||||||
),
|
),
|
||||||
|
'high variant assignment' => array(
|
||||||
|
'condition' => 'high variant assignment',
|
||||||
|
'setup' => fn() => add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 15 ),
|
||||||
|
),
|
||||||
'outdated version' => array(
|
'outdated version' => array(
|
||||||
'condition' => 'outdated version',
|
'condition' => 'outdated version',
|
||||||
'setup' => function () {
|
'setup' => function () {
|
||||||
$version = WC()->version;
|
$version = WC()->version;
|
||||||
|
// Next major version. (e.g. 9.0.1 -> 10.0.0).
|
||||||
$next_version = implode(
|
$next_version = implode(
|
||||||
'.',
|
'.',
|
||||||
array_map(
|
array_map(
|
||||||
|
@ -112,28 +117,79 @@ class RemoteLoggerTest extends \WC_Unit_Test_Case {
|
||||||
array_keys( explode( '.', $version ) )
|
array_keys( explode( '.', $version ) )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
set_transient( RemoteLogger::WC_LATEST_VERSION_TRANSIENT, $next_version );
|
|
||||||
|
set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $next_version, WEEK_IN_SECONDS );
|
||||||
},
|
},
|
||||||
'high variant assignment' => array(
|
|
||||||
'condition' => 'high variant assignment',
|
|
||||||
'setup' => fn() => add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 15 ),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox Fetch latest WooCommerce version retries on API failure
|
|
||||||
*/
|
|
||||||
public function test_fetch_latest_woocommerce_version_retry() {
|
|
||||||
$this->setup_remote_logging_conditions( true );
|
|
||||||
add_filter( 'plugins_api', fn() => new \WP_Error(), 10, 3 );
|
|
||||||
|
|
||||||
for ( $i = 1; $i <= 4; $i++ ) {
|
/**
|
||||||
$this->sut->is_remote_logging_allowed();
|
* @testdox should_current_version_be_logged method behaves correctly
|
||||||
$retry_count = get_transient( RemoteLogger::FETCH_LATEST_VERSION_RETRY );
|
* @dataProvider should_current_version_be_logged_provider
|
||||||
$this->assertEquals( min( $i, 3 ), $retry_count );
|
*
|
||||||
|
* @param string $current_version The current WooCommerce version.
|
||||||
|
* @param string $new_version The new WooCommerce version.
|
||||||
|
* @param string $transient_value The value of the transient.
|
||||||
|
* @param bool $expected The expected result.
|
||||||
|
*/
|
||||||
|
public function test_should_current_version_be_logged( $current_version, $new_version, $transient_value, $expected ) {
|
||||||
|
$wc_version = WC()->version;
|
||||||
|
WC()->version = $current_version;
|
||||||
|
|
||||||
|
// Set up the transient.
|
||||||
|
if ( null !== $transient_value ) {
|
||||||
|
set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $transient_value, WEEK_IN_SECONDS );
|
||||||
|
} else {
|
||||||
|
delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT );
|
||||||
|
|
||||||
|
$this->setup_mock_plugin_updates( $new_version );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$result = $this->invoke_private_method( $this->sut, 'should_current_version_be_logged', array() );
|
||||||
|
$this->assertEquals( $expected, $result );
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT );
|
||||||
|
|
||||||
|
WC()->version = $wc_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_should_current_version_be_logged.
|
||||||
|
*/
|
||||||
|
public function should_current_version_be_logged_provider() {
|
||||||
|
return array(
|
||||||
|
'current version is latest (transient set)' => array( '9.2.0', '9.2.0', '9.2.0', true ),
|
||||||
|
'current version is newer (transient set)' => array( '9.3.0', '9.2.0', '9.2.0', true ),
|
||||||
|
'current version is older (transient set)' => array( '9.1.0', '9.2.0', '9.2.0', false ),
|
||||||
|
'new version is null (transient set)' => array( '9.2.0', null, null, true ),
|
||||||
|
'transient not set, current version is latest' => array( '9.2.0', '9.2.0', null, true ),
|
||||||
|
'transient not set, current version is newer' => array( '9.3.0', '9.2.0', null, true ),
|
||||||
|
'transient not set, current version is older' => array( '9.1.0', '9.2.0', null, false ),
|
||||||
|
'transient not set, new version is null' => array( '9.2.0', null, null, true ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox fetch_new_woocommerce_version method returns correct version
|
||||||
|
*/
|
||||||
|
public function test_fetch_new_woocommerce_version() {
|
||||||
|
$this->setup_mock_plugin_updates( '9.3.0' );
|
||||||
|
|
||||||
|
$result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() );
|
||||||
|
$this->assertEquals( '9.3.0', $result, 'The result should be the latest version when an update is available.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox fetch_new_woocommerce_version method returns null when no update is available
|
||||||
|
*/
|
||||||
|
public function test_fetch_new_woocommerce_version_no_update() {
|
||||||
|
add_filter( 'pre_site_transient_update_plugins', fn() => array() );
|
||||||
|
|
||||||
|
$result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() );
|
||||||
|
$this->assertNull( $result, 'The result should be null when no update is available.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -421,17 +477,26 @@ class RemoteLoggerTest extends \WC_Unit_Test_Case {
|
||||||
update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' );
|
update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' );
|
||||||
add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' );
|
add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' );
|
||||||
add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 );
|
add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 );
|
||||||
add_filter(
|
$this->setup_mock_plugin_updates( $enabled ? WC()->version : '9.0.0' );
|
||||||
'plugins_api',
|
|
||||||
function ( $result, $action, $args ) use ( $enabled ) {
|
|
||||||
if ( 'plugin_information' === $action && 'woocommerce' === $args->slug ) {
|
|
||||||
return (object) array( 'version' => $enabled ? WC()->version : '9.0.0' );
|
|
||||||
}
|
}
|
||||||
return $result;
|
|
||||||
},
|
|
||||||
10,
|
/**
|
||||||
3
|
* Set up mock plugin updates.
|
||||||
|
*
|
||||||
|
* @param string $new_version The new version of WooCommerce to simulate.
|
||||||
|
*/
|
||||||
|
private function setup_mock_plugin_updates( $new_version ) {
|
||||||
|
$update_plugins = (object) array(
|
||||||
|
'response' => array(
|
||||||
|
WC_PLUGIN_BASENAME => (object) array(
|
||||||
|
'new_version' => $new_version,
|
||||||
|
'package' => 'https://downloads.wordpress.org/plugin/woocommerce.zip',
|
||||||
|
'slug' => 'woocommerce',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
add_filter( 'pre_site_transient_update_plugins', fn() => $update_plugins );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue