PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.3.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.3.3
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / classes / WpMatomo / AjaxTracker.php
matomo / classes / WpMatomo Last commit date
Admin 1 year ago Commands 2 years ago Db 1 year ago Ecommerce 1 year ago Report 1 year ago Site 2 years ago TrackingCode 1 year ago Updater 4 years ago User 1 year ago Workarounds 2 years ago WpStatistics 1 year ago views 4 years ago API.php 1 year ago Access.php 4 years ago AjaxTracker.php 1 year ago Annotations.php 4 years ago Bootstrap.php 1 year ago Capabilities.php 4 years ago Compatibility.php 2 years ago Email.php 2 years ago ErrorNotice.php 2 years ago Installer.php 1 year ago Logger.php 1 year ago OptOut.php 1 year ago Paths.php 2 years ago PluginAdminOverrides.php 2 years ago PrivacyBadge.php 4 years ago RedirectOnActivation.php 4 years ago Referral.php 2 years ago Roles.php 1 year ago ScheduledTasks.php 1 year ago Settings.php 1 year ago Site.php 3 years ago TrackingCode.php 1 year ago Uninstaller.php 1 year ago Updater.php 1 year ago User.php 4 years ago
AjaxTracker.php
220 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 * @package matomo
8 */
9
10 namespace WpMatomo;
11
12 use WpMatomo\Ecommerce\ServerSideVisitorId;
13 use WpMatomo\TrackingCode\GeneratorOptions;
14 use WpMatomo\TrackingCode\TrackingCodeGenerator;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // if accessed directly
18 }
19
20 if ( ! class_exists( '\PiwikTracker' ) ) {
21 include_once plugin_dir_path( MATOMO_ANALYTICS_FILE ) . 'app/vendor/matomo/matomo-php-tracker/MatomoTracker.php';
22 }
23
24 class AjaxTracker extends \MatomoTracker {
25
26 const IP_ADDRESS_FORWARDING_HEADER = 'X-Matomo-Forwarded-Ip';
27 const IP_ADDRESS_FORWARDING_HEADER_SERVER_NAME = 'HTTP_X_MATOMO_FORWARDED_IP';
28 const IP_ADDRESS_FORWARDING_NONCE_NAME = 'matomo-track-forward-ip';
29
30 private $has_cookie = false;
31 private $logger;
32
33 public function __construct( Settings $settings ) {
34 $this->logger = new Logger();
35
36 $site = new Site();
37 $idsite = $site->get_current_matomo_site_id();
38
39 if ( ! $idsite ) {
40 return;
41 }
42
43 $paths = new Paths();
44
45 if ( $settings->get_global_option( 'track_api_endpoint' ) === 'restapi' ) {
46 $api_endpoint = $paths->get_tracker_api_rest_api_endpoint();
47 } else {
48 $api_endpoint = $paths->get_tracker_api_url_in_matomo_dir();
49 }
50
51 parent::__construct( $idsite, $api_endpoint );
52
53 $this->ip = false;
54
55 // we are using the tracker only in ajax so the referer contains the actual url
56 $this->urlReferrer = false;
57 $this->pageUrl = ! empty( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : false;
58
59 if ( ! $settings->get_global_option( 'disable_cookies' ) ) {
60 $tracking_code_generator = new TrackingCodeGenerator( $settings, new GeneratorOptions( $settings ) );
61 $cookie_domain = $tracking_code_generator->get_tracking_cookie_domain();
62 $this->enableCookies( $cookie_domain );
63 } else {
64 $this->disableCookieSupport();
65 }
66
67 if ( $this->loadVisitorIdCookie() ) {
68 if ( ! empty( $this->cookieVisitorId ) ) {
69 $this->has_cookie = true;
70 $this->set_visitor_id_safe( $this->cookieVisitorId );
71 }
72 } else if ( function_exists( 'WC' ) && isset( WC()->session ) ) {
73 $visitor_id = WC()->session->get( ServerSideVisitorId::VISITOR_ID_SESSION_VAR_NAME );
74 if ( ! empty( $visitor_id ) ) {
75 $this->hasCookie = true; // do not set cookies for this visitor, since it would have no effect anyway
76 $this->set_visitor_id_safe( $visitor_id );
77 }
78 }
79 }
80
81 public function set_visitor_id_safe( $visitor_id ) {
82 try {
83 $this->setVisitorId( $visitor_id );
84 } catch ( \Exception $ex ) {
85 // do not fatal if the visitor ID is invalid for some reason
86 if ( ! $this->is_invalid_visitor_id_error( $ex ) ) {
87 throw $ex;
88 }
89 }
90 }
91
92 public function is_success_response( $response ) {
93 $gif_response = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
94 return $response === base64_decode( $gif_response );
95 }
96
97 protected function setCookie( $cookieName, $cookieValue, $cookieTTL ) {
98 if ( ! $this->has_cookie ) {
99 // we only set / overwrite cookies if it is a visitor that has eg no JS enabled or ad blocker enabled etc.
100 // this way we will track all cart updates and orders into the same visitor on following requests.
101 // If we recognized the visitor before via cookie we want in our case to make sure to not overwrite
102 // any cookie
103 parent::setCookie( $cookieName, $cookieValue, $cookieTTL );
104 }
105 }
106
107 protected function sendRequest( $url, $method = 'GET', $data = null, $force = false ) {
108 if ( ! $this->idSite ) {
109 $this->logger->log('ecommerce tracking could not find idSite, cannot send request');
110 return null; // not installed or synced yet
111 }
112
113 if ( $this->is_prerender() ) {
114 // do not track if for some reason we are prerendering
115 return null;
116 }
117
118 $args = array(
119 'method' => $method,
120 );
121 if ( ! empty( $data ) ) {
122 $args['body'] = $data;
123 }
124
125 if ( ! empty( $this->ip ) ) {
126 $args['headers'] = [
127 self::IP_ADDRESS_FORWARDING_HEADER => $this->ip,
128 ];
129
130 $ip_nonce = wp_create_nonce( self::IP_ADDRESS_FORWARDING_NONCE_NAME );
131 $url = $url . '&ip_nonce=' . rawurlencode( $ip_nonce );
132 }
133
134 // todo at some point we could think about including `matomo.php` here instead of doing an http request
135 // however we would need to make sure to set a custom tracker response handler to
136 // 1) Not send any response no matter what happens
137 // 2) Never exit at any point
138
139 $url = $url . '&bots=1';
140
141 $response = $this->wp_remote_request( $url, $args );
142
143 if (is_wp_error($response)) {
144 $this->logger->log_exception('ajax_tracker', new \Exception($response->get_error_message()));
145 }
146
147 return $response;
148 }
149
150 private function is_invalid_visitor_id_error( \Exception $ex ) {
151 return strpos( $ex->getMessage(), 'setVisitorId() expects' ) === 0;
152 }
153
154 /**
155 * See https://developer.chrome.com/docs/web-platform/prerender-pages
156 * @return bool
157 */
158 private function is_prerender() {
159 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
160 $purpose = strtolower( isset( $_SERVER['HTTP_SEC_PURPOSE'] ) ? wp_unslash( $_SERVER['HTTP_SEC_PURPOSE'] ) : '' );
161 return strpos( $purpose, 'prefetch' ) !== false
162 || strpos( $purpose, 'prerender' ) !== false;
163 }
164
165 /**
166 * for tests to override
167 * @param string $url
168 * @param array $args
169 * @return array|\WP_Error
170 */
171 protected function wp_remote_request( $url, $args ) {
172 return wp_remote_request( $url, $args );
173 }
174
175 /**
176 * In Matomo for WordPress we want to rely entirely on JavaScript tracker
177 * for creating cookies.
178 *
179 * @return void
180 */
181 protected function setFirstPartyCookies() {
182 // disabled
183 }
184
185 /**
186 * Enables the handling of the X-Matomo-Forwarded-Ip, if it should be for
187 * the current request.
188 *
189 * Matomo for WordPress uses a custom header to correctly track client IP addresses
190 * when doing server side tracking. We choose to use this approach instead
191 * of the `cip` tracking parameter, since that parameter requires the use of
192 * a token_auth, and creating and storing a token_auth is more complexity than
193 * we want.
194 *
195 * Instead, we use a WP nonce to check whether the current request is authorized
196 * to handle the X-Matomo-Forwarded-Ip header.
197 *
198 * If it should be handled, the X-Matomo-Forwarded-Ip header is added to Matomo's
199 * list of proxy HTTP headers to look at for IP addresses.
200 */
201 public static function add_ip_forward_proxy_header_to_config(\Piwik\Config $config ) {
202 if ( empty( $_REQUEST['ip_nonce'] ) ) {
203 return;
204 }
205
206 $ip_nonce = $_REQUEST['ip_nonce'];
207 if ( ! wp_verify_nonce( $ip_nonce, self::IP_ADDRESS_FORWARDING_NONCE_NAME ) ) {
208 return;
209 }
210
211 $proxy_client_headers = $config->General['proxy_client_headers'];
212 if ( ! is_array( $proxy_client_headers ) ) {
213 $proxy_client_headers = [];
214 }
215 $proxy_client_headers[] = self::IP_ADDRESS_FORWARDING_HEADER_SERVER_NAME;
216
217 $config->General['proxy_client_headers'] = $proxy_client_headers;
218 }
219 }
220