PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / src / class-package-version-tracker.php

class-package-version-tracker.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php

202 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
202