PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php +201 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,201 @@
1 +<?php
2 +/**
3 + * The Package_Version_Tracker class.
4 + *
5 + * @package automattic/jetpack-connection
6 + */
7 +
8 +namespace Automattic\Jetpack\Connection;
9 +
10 +use Jetpack_Options;
11 +
12 +/**
13 + * The Package_Version_Tracker class.
14 + */
15 +class Package_Version_Tracker {
16 +
17 + const PACKAGE_VERSION_OPTION = 'jetpack_package_versions';
18 +
19 + /**
20 + * The cache key for storing a failed request to update remote package versions.
21 + * The caching logic is that when a failed request occurs, we cache it temporarily
22 + * with a set expiration time.
23 + * Only after the key has expired, we'll be able to repeat a remote request.
24 + * This also implies that the cached value is redundant, however we chose the datetime
25 + * of the failed request to avoid using booleans.
26 + */
27 + const CACHED_FAILED_REQUEST_KEY = 'jetpack_failed_update_remote_package_versions';
28 +
29 + /**
30 + * The min time difference in seconds for attempting to
31 + * update remote tracked package versions after a failed remote request.
32 + */
33 + const CACHED_FAILED_REQUEST_EXPIRATION = 1 * HOUR_IN_SECONDS;
34 +
35 + /**
36 + * Transient key for rate limiting the package version requests;
37 + */
38 + const RATE_LIMITER_KEY = 'jetpack_update_remote_package_last_query';
39 +
40 + /**
41 + * Only allow one versions check (and request) per minute.
42 + */
43 + const RATE_LIMITER_TIMEOUT = MINUTE_IN_SECONDS;
44 +
45 + /**
46 + * Runs the package version check on the `shutdown` hook.
47 + *
48 + * Static so the callback can be unregistered, and so the tracker is only
49 + * constructed when the hook fires rather than during configuration.
50 + */
51 + public static function update_on_shutdown() {
52 + ( new self() )->maybe_update_package_versions();
53 + }
54 +
55 + /**
56 + * Uses the jetpack_package_versions filter to obtain the package versions from packages that need
57 + * version tracking. If the package versions have changed, updates the option and notifies WPCOM.
58 + */
59 + public function maybe_update_package_versions() {
60 + // Do not run too early or all the modules may not be loaded.
61 + if ( ! did_action( 'init' ) ) {
62 + return;
63 + }
64 +
65 + // Only attempt to update the option on POST requests.
66 + // This will prevent the option from being updated multiple times due to concurrent requests.
67 + if ( ! ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) ) {
68 + return;
69 + }
70 +
71 + // The version check is being rate limited.
72 + if ( $this->is_rate_limiting() ) {
73 + return;
74 + }
75 +
76 + /**
77 + * Obtains the package versions.
78 + *
79 + * @since 1.30.2
80 + *
81 + * @param array An associative array of Jetpack package slugs and their corresponding versions as key/value pairs.
82 + */
83 + $filter_versions = apply_filters( 'jetpack_package_versions', array() );
84 +
85 + if ( ! is_array( $filter_versions ) ) {
86 + return;
87 + }
88 +
89 + $option_versions = get_option( self::PACKAGE_VERSION_OPTION, array() );
90 +
91 + foreach ( $filter_versions as $package => $version ) {
92 + if ( ! is_string( $package ) || ! is_string( $version ) ) {
93 + unset( $filter_versions[ $package ] );
94 + }
95 + }
96 +
97 + if ( ! is_array( $option_versions )
98 + || count( array_diff_assoc( $filter_versions, $option_versions ) )
99 + || count( array_diff_assoc( $option_versions, $filter_versions ) )
100 + ) {
101 + $this->update_package_versions_option( $filter_versions );
102 + }
103 + }
104 +
105 + /**
106 + * Updates the package versions option.
107 + *
108 + * @param array $package_versions The package versions.
109 + */
110 + protected function update_package_versions_option( $package_versions ) {
111 + if ( ! $this->is_sync_enabled() ) {
112 + $this->update_package_versions_via_remote_request( $package_versions );
113 + // Remove the checksum for package versions, so it gets recalculated when sync gets activated.
114 + $jetpack_callables_sync_checksum = Jetpack_Options::get_raw_option( 'jetpack_callables_sync_checksum' );
115 + if ( isset( $jetpack_callables_sync_checksum['jetpack_package_versions'] ) ) {
116 + unset( $jetpack_callables_sync_checksum['jetpack_package_versions'] );
117 + Jetpack_Options::update_raw_option( 'jetpack_callables_sync_checksum', $jetpack_callables_sync_checksum );
118 + }
119 + return;
120 + }
121 +
122 + update_option( self::PACKAGE_VERSION_OPTION, $package_versions );
123 + }
124 +
125 + /**
126 + * Whether Jetpack Sync is enabled.
127 + *
128 + * @return boolean true if Sync is present and enabled, false otherwise
129 + */
130 + protected function is_sync_enabled() {
131 + if ( class_exists( 'Automattic\Jetpack\Sync\Settings' ) && \Automattic\Jetpack\Sync\Settings::is_sync_enabled() ) {
132 +
133 + return true;
134 + }
135 +
136 + return false;
137 + }
138 +
139 + /**
140 + * Fallback for updating the package versions via a remote request when Sync is not present.
141 + *
142 + * Updates the package versions as follows:
143 + * - Sends the updated package versions to wpcom.
144 + * - Updates the 'jetpack_package_versions' option.
145 + *
146 + * @param array $package_versions The package versions.
147 + */
148 + protected function update_package_versions_via_remote_request( $package_versions ) {
149 + $connection = new Manager();
150 + if ( ! $connection->is_connected() ) {
151 + return;
152 + }
153 +
154 + $site_id = \Jetpack_Options::get_option( 'id' );
155 +
156 + $last_failed_attempt_within_hour = get_transient( self::CACHED_FAILED_REQUEST_KEY );
157 +
158 + if ( $last_failed_attempt_within_hour ) {
159 + return;
160 + }
161 +
162 + $body = wp_json_encode(
163 + array(
164 + 'package_versions' => $package_versions,
165 + ),
166 + JSON_UNESCAPED_SLASHES
167 + );
168 +
169 + $response = Client::wpcom_json_api_request_as_blog(
170 + sprintf( '/sites/%d/jetpack-package-versions', $site_id ),
171 + '2',
172 + array(
173 + 'headers' => array( 'content-type' => 'application/json' ),
174 + 'method' => 'POST',
175 + ),
176 + $body,
177 + 'wpcom'
178 + );
179 +
180 + if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
181 + update_option( self::PACKAGE_VERSION_OPTION, $package_versions );
182 + } else {
183 + set_transient( self::CACHED_FAILED_REQUEST_KEY, time(), self::CACHED_FAILED_REQUEST_EXPIRATION );
184 + }
185 + }
186 +
187 + /**
188 + * Check if version check is being rate limited, and update the rate limiting transient if needed.
189 + *
190 + * @return bool
191 + */
192 + private function is_rate_limiting() {
193 + if ( get_transient( static::RATE_LIMITER_KEY ) ) {
194 + return true;
195 + }
196 +
197 + set_transient( static::RATE_LIMITER_KEY, time(), static::RATE_LIMITER_TIMEOUT );
198 +
199 + return false;
200 + }
201 +}