PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.3
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 / woocommerce-analytics / src / class-woocommerce-analytics.php

class-woocommerce-analytics.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.3, at jetpack_vendor/automattic/woocommerce-analytics/src/class-woocommerce-analytics.php

389 lines 12.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main class for the WooCommerce Analytics package.
4 * Originally ported from the Jetpack_Google_Analytics code.
5 *
6 * @package automattic/woocommerce-analytics
7 */
8
9 namespace Automattic;
10
11 use Automattic\Jetpack\Assets;
12 use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
13 use Automattic\Woocommerce_Analytics\My_Account;
14 use Automattic\Woocommerce_Analytics\Universal;
15 use Automattic\Woocommerce_Analytics\WC_Analytics_Tracking_Proxy;
16 use Composer\InstalledVersions;
17
18 /**
19 * Instantiate WooCommerce Analytics
20 */
21 class Woocommerce_Analytics {
22 /**
23 * Package version.
24 */
25 const PACKAGE_VERSION = '0.16.3';
26
27 /**
28 * Proxy speed module version option.
29 *
30 * @var string
31 */
32 const PROXY_SPEED_MODULE_VERSION_OPTION = 'woocommerce_analytics_proxy_speed_module_version';
33
34 /**
35 * Proxy speed module version check transient.
36 *
37 * @var string
38 */
39 const PROXY_SPEED_MODULE_VERSION_CHECK_TRANSIENT = 'woocommerce_analytics_proxy_speed_module_version_check';
40
41 /**
42 * Initializer.
43 * Used to configure the WooCommerce Analytics package.
44 *
45 * @return void
46 */
47 public static function init() {
48 if ( ! self::should_track_store() || did_action( 'woocommerce_analytics_init' ) ) {
49 return;
50 }
51
52 // loading _wca.
53 add_action( 'wp_head', array( __CLASS__, 'wp_head_top' ), 1 );
54
55 // loading s.js.
56 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_tracking_script' ) );
57
58 // loading client-side analytics script.
59 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_client_script' ) );
60
61 // Initialize general store tracking actions.
62 add_action( 'init', array( new Universal(), 'init_hooks' ) );
63 add_action( 'init', array( new My_Account(), 'init_hooks' ) );
64
65 // Initialize REST API endpoints.
66 add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
67
68 /**
69 * Fires after the WooCommerce Analytics package is initialized
70 *
71 * @since 0.1.5
72 */
73 do_action( 'woocommerce_analytics_init' );
74 }
75
76 /**
77 * WooCommerce Analytics is only available to Jetpack connected WooCommerce stores
78 * with WooCommerce version 3.0 or higher
79 *
80 * @return bool
81 */
82 public static function should_track_store() {
83 // Ensure this is available, even with mu-plugins.
84 if ( ! function_exists( 'is_plugin_active' ) ) {
85 require_once ABSPATH . 'wp-admin/includes/plugin.php';
86 }
87
88 /**
89 * Make sure WooCommerce is installed and active
90 *
91 * This action is documented in https://docs.woocommerce.com/document/create-a-plugin
92 */
93 if ( ! defined( 'WC_PLUGIN_FILE' ) || ! is_plugin_active( plugin_basename( WC_PLUGIN_FILE ) ) ) {
94 return false;
95 }
96 // Ensure the WooCommerce class exists and is a valid version.
97 $minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( \WC_VERSION, '3.0', '>=' );
98 if ( ! $minimum_woocommerce_active ) {
99 return false;
100 }
101
102 // Ensure the WC Tracks classes exist.
103 if ( ! class_exists( 'WC_Tracks' ) ) {
104 if ( ! defined( 'WC_ABSPATH' ) || ! file_exists( WC_ABSPATH . 'includes/tracks/class-wc-tracks.php' ) ) {
105 return false;
106 }
107
108 include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks.php';
109 include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
110 include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-client.php';
111 }
112
113 add_action( 'admin_init', array( __CLASS__, 'maybe_update_proxy_speed_module' ) );
114
115 // Tracking only Site pages.
116 if ( is_admin() || wp_doing_ajax() || wp_is_xml_request() || is_login() || is_feed() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
117 return false;
118 }
119
120 // Make sure the site is connected to WordPress.com.
121 if ( ! ( new Jetpack_Connection() )->is_connected() ) {
122 return false;
123 }
124
125 return true;
126 }
127
128 /**
129 * Make _wca available to queue events
130 */
131 public static function wp_head_top() {
132 if ( is_cart() || is_checkout() || is_checkout_pay_page() || is_order_received_page() || is_add_payment_method_page() ) {
133 echo '<script>window._wca_prevent_referrer = true;</script>' . "\r\n";
134 }
135 echo '<script>window._wca = window._wca || [];</script>' . "\r\n";
136 }
137
138 /**
139 * Place script to call s.js, Store Analytics.
140 */
141 public static function enqueue_tracking_script() {
142 $url = sprintf(
143 'https://stats.wp.com/s-%d.js',
144 gmdate( 'YW' )
145 );
146
147 wp_enqueue_script(
148 'woocommerce-analytics',
149 $url,
150 array(),
151 null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- The version is set in the URL.
152 array(
153 'in_footer' => false,
154 'strategy' => 'defer',
155 )
156 );
157 }
158
159 /**
160 * Enqueue client-side analytics script.
161 */
162 public static function enqueue_client_script() {
163 Assets::register_script(
164 'woocommerce-analytics-client',
165 '../build/woocommerce-analytics-client.js',
166 __FILE__,
167 array(
168 'in_footer' => true,
169 'strategy' => 'defer',
170 'enqueue' => true,
171 )
172 );
173 }
174
175 /**
176 * Register REST API routes.
177 */
178 public static function register_rest_routes() {
179 $controller = new WC_Analytics_Tracking_Proxy();
180 $controller->register_routes();
181 }
182
183 /**
184 * Maybe update proxy speed module.
185 */
186 public static function maybe_update_proxy_speed_module() {
187 // Skip if we've already checked recently.
188 if ( get_transient( self::PROXY_SPEED_MODULE_VERSION_CHECK_TRANSIENT ) ) {
189 return;
190 }
191
192 $version = get_option( self::PROXY_SPEED_MODULE_VERSION_OPTION, false );
193
194 if ( \Automattic\Woocommerce_Analytics\Features::is_proxy_speed_module_enabled() ) {
195 if ( $version !== self::PACKAGE_VERSION ) {
196 self::maybe_add_proxy_speed_module();
197 }
198 } elseif ( $version !== false ) {
199 self::maybe_remove_proxy_speed_module();
200 }
201
202 // Set the transient after the update attempt to prevent checking on every admin_init.
203 // If the update failed, it will be retried after the transient expires (1 day).
204 set_transient( self::PROXY_SPEED_MODULE_VERSION_CHECK_TRANSIENT, 1, DAY_IN_SECONDS );
205 }
206
207 /**
208 * Maybe add proxy speed module.
209 */
210 public static function maybe_add_proxy_speed_module() {
211 if ( ! \Automattic\Woocommerce_Analytics\Features::is_proxy_speed_module_enabled() ) {
212 return;
213 }
214
215 if ( ! self::init_filesystem() ) {
216 return;
217 }
218
219 global $wp_filesystem;
220
221 // Create the mu-plugin directory if it doesn't exist.
222 if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
223 wp_mkdir_p( WPMU_PLUGIN_DIR );
224 }
225
226 // If the mu-plugin directory doesn't exist, we can't copy the files.
227 if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
228 return;
229 }
230
231 // Check if the mu-plugin directory is writable.
232 if ( ! $wp_filesystem->is_writable( WPMU_PLUGIN_DIR ) ) {
233 if ( function_exists( 'wc_get_logger' ) ) {
234 wc_get_logger()->debug( 'WooCommerce Analytics proxy speed module not installed: mu-plugins directory is not writable.', array( 'source' => 'woocommerce-analytics' ) );
235 }
236 return;
237 }
238
239 if ( get_option( self::PROXY_SPEED_MODULE_VERSION_OPTION ) === self::PACKAGE_VERSION ) {
240 // No need to copy the files again.
241 return;
242 }
243
244 $mu_plugin_src_file = __DIR__ . '/mu-plugin/woocommerce-analytics-proxy-speed-module-template.php';
245 $mu_plugin_dest_file = trailingslashit( WPMU_PLUGIN_DIR ) . 'woocommerce-analytics-proxy-speed-module.php';
246
247 // Verify source file exists before attempting to copy.
248 if ( ! file_exists( $mu_plugin_src_file ) ) {
249 if ( function_exists( 'wc_get_logger' ) ) {
250 wc_get_logger()->error( 'WooCommerce Analytics proxy speed module source file not found.', array( 'source' => 'woocommerce-analytics' ) );
251 }
252 return;
253 }
254
255 $content = $wp_filesystem->get_contents( $mu_plugin_src_file );
256 if ( false === $content ) {
257 if ( function_exists( 'wc_get_logger' ) ) {
258 wc_get_logger()->error( 'Failed to read the WooCommerce Analytics proxy speed module source file.', array( 'source' => 'woocommerce-analytics' ) );
259 }
260 return;
261 }
262
263 // Get the autoloader path from the current plugin location.
264 $autoloader_path = self::locate_autoloader_file();
265 if ( null === $autoloader_path ) {
266 if ( function_exists( 'wc_get_logger' ) ) {
267 wc_get_logger()->error( 'WooCommerce Analytics proxy speed module not installed: could not locate autoloader.', array( 'source' => 'woocommerce-analytics' ) );
268 }
269 return;
270 }
271
272 // Replace placeholders with actual values.
273 $content = str_replace(
274 array( '{{AUTOLOADER_PATH}}', '{{VERSION}}' ),
275 array( $autoloader_path, self::PACKAGE_VERSION ),
276 $content
277 );
278
279 if ( ! $wp_filesystem->put_contents( $mu_plugin_dest_file, $content ) ) {
280 if ( function_exists( 'wc_get_logger' ) ) {
281 wc_get_logger()->error( 'Failed to write the WooCommerce Analytics proxy speed module file.', array( 'source' => 'woocommerce-analytics' ) );
282 }
283 return;
284 }
285
286 update_option( self::PROXY_SPEED_MODULE_VERSION_OPTION, self::PACKAGE_VERSION );
287 }
288
289 /**
290 * Maybe removes the proxy speed module. This should be invoked when the plugin is deactivated.
291 */
292 public static function maybe_remove_proxy_speed_module() {
293 if ( ! self::init_filesystem() ) {
294 return;
295 }
296
297 global $wp_filesystem;
298
299 /**
300 * Clean up MU plugin.
301 */
302 $file_path = trailingslashit( WPMU_PLUGIN_DIR ) . 'woocommerce-analytics-proxy-speed-module.php';
303
304 if ( $wp_filesystem->exists( $file_path ) && $wp_filesystem->is_writable( $file_path ) ) {
305 $deleted = $wp_filesystem->delete( $file_path );
306 if ( ! $deleted && function_exists( 'wc_get_logger' ) ) {
307 wc_get_logger()->error( 'Failed to delete WooCommerce Analytics proxy speed module file. The MU-plugin may continue running.', array( 'source' => 'woocommerce-analytics' ) );
308 }
309 }
310
311 delete_option( self::PROXY_SPEED_MODULE_VERSION_OPTION );
312 delete_transient( self::PROXY_SPEED_MODULE_VERSION_CHECK_TRANSIENT );
313 }
314
315 /**
316 * Finds the path to the autoloader file.
317 *
318 * Uses multiple strategies to locate the autoloader, since this package
319 * can be included in different plugins (Jetpack, WooCommerce Analytics, etc.):
320 * 1. Jetpack autoloader global (if available)
321 * 2. Composer's InstalledVersions API
322 * 3. Directory-based guessing as a fallback
323 *
324 * @return string|null The path to the autoloader file, or null if not found.
325 */
326 private static function locate_autoloader_file() {
327 global $jetpack_autoloader_loader;
328
329 $autoload_file = null;
330
331 // Try the Jetpack autoloader.
332 if ( isset( $jetpack_autoloader_loader ) ) {
333 $class_file = $jetpack_autoloader_loader->find_class_file( self::class );
334 if ( $class_file ) {
335 // Walk up 5 levels: src/ → woocommerce-analytics/ → automattic/ → jetpack_vendor/ → plugin root.
336 $autoload_file = dirname( $class_file, 5 ) . '/vendor/autoload.php';
337 }
338 }
339
340 // Try Composer's InstalledVersions API.
341 if ( null === $autoload_file
342 && is_callable( array( InstalledVersions::class, 'getInstallPath' ) )
343 && InstalledVersions::isInstalled( 'automattic/woocommerce-analytics' )
344 ) {
345 $package_file = InstalledVersions::getInstallPath( 'automattic/woocommerce-analytics' );
346 $expected_suffix = '/automattic/woocommerce-analytics';
347 if ( substr( $package_file, -strlen( $expected_suffix ) ) === $expected_suffix ) {
348 // Walk up 3 levels: woocommerce-analytics/ → automattic/ → jetpack_vendor/ → plugin root.
349 $autoload_file = dirname( $package_file, 3 ) . '/vendor/autoload.php';
350 }
351 }
352
353 // Guess based on directory structure.
354 // First try standard vendor layout (vendor/automattic/woocommerce-analytics/src/),
355 // then try standalone package with its own vendor dir.
356 if ( null === $autoload_file ) {
357 // Walk up 4 levels from src/: woocommerce-analytics/ → automattic/ → vendor/ → project root.
358 $autoload_file = dirname( __DIR__, 4 ) . '/vendor/autoload.php';
359 if ( ! file_exists( $autoload_file ) ) {
360 $autoload_file = dirname( __DIR__ ) . '/vendor/autoload.php';
361 }
362 }
363
364 if ( ! file_exists( $autoload_file ) ) {
365 return null;
366 }
367
368 return $autoload_file;
369 }
370
371 /**
372 * Initialize the WP filesystem.
373 *
374 * @return bool True if filesystem is initialized, false otherwise.
375 */
376 private static function init_filesystem() {
377 if ( ! function_exists( 'WP_Filesystem' ) ) {
378 require_once ABSPATH . 'wp-admin/includes/file.php';
379 }
380
381 // Initialize the WP filesystem.
382 ob_start();
383 $initialized = WP_Filesystem();
384 ob_end_clean();
385
386 return $initialized;
387 }
388 }
389