PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-stats / src / class-tracking-pixel.php
jetpack / jetpack_vendor / automattic / jetpack-stats / src Last commit date
abilities 1 week ago class-main.php 3 days ago class-options.php 3 weeks ago class-package-version.php 3 days ago class-rest-provider.php 2 years ago class-tracking-pixel.php 3 weeks ago class-transient-cleanup.php 1 week ago class-wpcom-stats.php 3 days ago class-xmlrpc-provider.php 3 days ago
class-tracking-pixel.php
502 lines
1 <?php
2 /**
3 * Stats Tracking_Pixel
4 *
5 * @package automattic/jetpack-stats
6 */
7
8 namespace Automattic\Jetpack\Stats;
9
10 use Jetpack_Options;
11 use WP_Post;
12
13 /**
14 * Stats Tracking_Pixel class.
15 *
16 * Responsible for embedding the Stats tracking pixel.
17 *
18 * @since 0.1.0
19 */
20 class Tracking_Pixel {
21
22 /**
23 * Array name.
24 *
25 * @var string $array_name The 'stats' array name
26 */
27 const STATS_ARRAY_TO_STRING_FILTER = 'stats_array';
28
29 const TRACKED_UTM_PARAMETERS = array(
30 'utm_id',
31 'utm_source',
32 'utm_medium',
33 'utm_campaign',
34 'utm_term',
35 'utm_content',
36 'utm_source_platform',
37 'utm_creative_format',
38 'utm_marketing_tactic',
39 );
40
41 /**
42 * Stats Build View Data.
43 *
44 * @access public
45 * @return array
46 */
47 public static function build_view_data() {
48 global $wp_the_query;
49
50 $blog = Jetpack_Options::get_option( 'id' );
51 $tz = get_option( 'gmt_offset' );
52 $v = 'ext';
53 $blog_url = wp_parse_url( site_url() );
54 $srv = $blog_url['host'];
55 $is_not_post = false;
56 if ( $wp_the_query->is_single || $wp_the_query->is_page || $wp_the_query->is_posts_page ) {
57 // Store and reset the queried_object and queried_object_id
58 // Otherwise, redirect_canonical() will redirect to home_url( '/' ) for show_on_front = page sites where home_url() is not all lowercase.
59 // Repro:
60 // 1. Set home_url = https://ExamPle.com/
61 // 2. Set show_on_front = page
62 // 3. Set page_on_front = something
63 // 4. Visit https://example.com/ !
64 $queried_object = $wp_the_query->queried_object ?? null;
65 $queried_object_id = $wp_the_query->queried_object_id ?? null;
66 try {
67 $post_obj = $wp_the_query->get_queried_object();
68 $post = $post_obj instanceof WP_Post ? $post_obj->ID : '0';
69 } finally {
70 $wp_the_query->queried_object = $queried_object;
71 $wp_the_query->queried_object_id = $queried_object_id;
72 }
73 } else {
74 $post = '0';
75 $is_not_post = true;
76 }
77 $view_data = compact( 'v', 'blog', 'post', 'tz', 'srv' );
78 // Batcache removes some of the UTM params from $_GET, we need to extract them from uri directly instead.
79 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We're sanitizing individual params in the loop.
80 $url_query = wp_parse_url( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ), PHP_URL_QUERY );
81 parse_str( (string) $url_query, $url_params );
82 foreach ( self::TRACKED_UTM_PARAMETERS as $utm_parameter ) {
83 if ( isset( $url_params[ $utm_parameter ] ) && is_scalar( $url_params[ $utm_parameter ] ) ) {
84 $view_data[ $utm_parameter ] = substr( sanitize_textarea_field( wp_unslash( $url_params[ $utm_parameter ] ) ), 0, 255 );
85 }
86 }
87
88 if ( $is_not_post ) {
89 if ( $wp_the_query->is_home() ) {
90 $view_data['arch_home'] = '1';
91 } elseif ( $wp_the_query->is_search() ) {
92 $search_term = $wp_the_query->query['s'] ?? $wp_the_query->query_vars['s'] ?? '';
93 $view_data['arch_search'] = sanitize_text_field( $search_term );
94 $view_data['arch_filters'] = sanitize_text_field( self::build_search_filters( $wp_the_query ) );
95 $view_data['arch_results'] = $wp_the_query->posts ? $wp_the_query->post_count : 0;
96 } elseif ( $wp_the_query->is_archive() ) {
97 if ( $wp_the_query->is_date ) {
98 $query = $wp_the_query->query;
99 $date_parts = array_filter( array( $query['year'] ?? null, $query['monthnum'] ?? null, $query['day'] ?? null ) );
100 $date = implode( '/', $date_parts );
101 $view_data['arch_date'] = $date;
102 }
103 if ( $wp_the_query->is_category ) {
104 $view_data['arch_cat'] = $wp_the_query->query['category_name'] ?? $wp_the_query->query_vars['category_name'] ?? '';
105 }
106 if ( $wp_the_query->is_tag ) {
107 $view_data['arch_tag'] = $wp_the_query->query['tag'] ?? $wp_the_query->query_vars['tag'] ?? '';
108 }
109 if ( $wp_the_query->is_author ) {
110 $view_data['arch_author'] = $wp_the_query->query['author_name'] ?? '';
111 }
112 if ( $wp_the_query->is_tax ) {
113 $query = $wp_the_query->query;
114 if ( is_array( $query ) && count( $query ) === 1 ) {
115 $view_data[ 'arch_tax_' . array_keys( $query )[0] ] = array_values( $query )[0];
116 }
117 }
118 $view_data['arch_results'] = $wp_the_query->posts ? $wp_the_query->post_count : 0;
119 } elseif ( $wp_the_query->is_404() ) {
120 $view_data['arch_err'] = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) );
121 } else {
122 $view_data['arch_other'] = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) );
123 }
124 }
125 return $view_data;
126 }
127
128 /**
129 * Collect the tracking data for a search page.
130 *
131 * @access private
132 * @param \WP_Query $query The WP_Query object to parse all the filters from.
133 * @return string The search filters in a URL query string format.
134 */
135 private static function build_search_filters( $query ) {
136 $data = array(
137 'posts_per_page' => $query->get( 'posts_per_page' ),
138 'paged' => ( $query->get( 'paged' ) ) ? absint( $query->get( 'paged' ) ) : 1,
139 'orderby' => $query->get( 'orderby' ),
140 'order' => $query->get( 'order' ),
141 );
142
143 if ( $query->get( 'author_name' ) ) {
144 $data['author_name'] = $query->get( 'author_name' );
145 }
146 $filters = http_build_query( $data );
147
148 $the_tax_query = $query->tax_query;
149 $terms = array();
150 if ( ! empty( $the_tax_query->queried_terms ) && is_array( $the_tax_query->queried_terms ) ) {
151 foreach ( $the_tax_query->queries as $tax_query ) {
152 if ( ! is_array( $tax_query ) || ! isset( $tax_query['taxonomy'] ) ) {
153 continue;
154 }
155 $taxonomy = $tax_query['taxonomy'];
156 if ( ! isset( $terms[ $taxonomy ] ) || ! is_array( $terms[ $taxonomy ] ) ) {
157 $terms[ $taxonomy ] = array();
158 }
159 $terms[ $taxonomy ] = array_merge( $terms[ $taxonomy ], $tax_query['terms'] );
160 }
161 }
162 if ( ! empty( $terms ) ) {
163 $filters .= '&terms=' . wp_json_encode( $terms, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
164 }
165 return $filters;
166 }
167
168 /**
169 * Build the Stats tracking details.
170 *
171 * @since 0.6.0
172 *
173 * @access private
174 * @param array $data Array of options about the site and page for the inline (non-AMP) tracker.
175 * @return string
176 */
177 private static function build_stats_details( $data ) {
178 $data_stats_array = self::stats_array_to_string( $data );
179
180 $pushes = sprintf(
181 '_stq.push([ "view", %1$s ]);
182 _stq.push([ "clickTrackerInit", "%2$s", "%3$s" ]);',
183 $data_stats_array,
184 $data['blog'],
185 $data['post']
186 );
187
188 // OFF (default): byte-for-byte identical to the historical output.
189 if ( ! Options::get_option( 'honor_cookie_consent' ) ) {
190 return "_stq = window._stq || [];\n" . $pushes;
191 }
192
193 // Fail closed when the WP Consent API plugin is active (an unavailable client-side API
194 // means "wait", not "fire"); fail open otherwise to preserve historical tracking.
195 return self::build_consent_gate( $pushes, ! function_exists( 'wp_has_consent' ) );
196 }
197
198 /**
199 * Wrap the tracking pushes in a WP Consent API gate.
200 *
201 * The check runs in the browser because cached HTML is shared across visitors, deferred to
202 * DOMContentLoaded (and re-run on the `wp_consent_type_defined` readiness event) so a
203 * late-loading consent plugin is still honored. The check is idempotent.
204 *
205 * `_jpStatsFire.done` is set before the pushes, not after, so the gate is at-most-once even
206 * if a push throws. Retrying can't recover: the stats sender assigns the beacon `src` before
207 * any of its fallible DOM work, so a later exception means the view was already counted and
208 * a replay would double-count it.
209 *
210 * @access private
211 * @param string $pushes The `_stq.push(...)` statements to gate.
212 * @param bool $fail_open Whether to fire when the client-side WP Consent API is unavailable.
213 * @return string
214 */
215 private static function build_consent_gate( $pushes, $fail_open ) {
216 $fail_open_literal = $fail_open ? 'true' : 'false';
217
218 return sprintf(
219 '_stq = window._stq || [];
220 function _jpStatsFire() {
221 if ( _jpStatsFire.done ) { return; }
222 _jpStatsFire.done = true;
223 %1$s
224 }
225 function _jpStatsCheck() {
226 if ( typeof window.wp_has_consent === "function" ) {
227 var consented;
228 try {
229 consented = window.wp_has_consent( "statistics" );
230 } catch ( e ) {
231 consented = %2$s;
232 }
233 if ( consented ) { _jpStatsFire(); }
234 return;
235 }
236 if ( %2$s ) { _jpStatsFire(); }
237 }
238 document.addEventListener( "wp_listen_for_consent_change", function ( event ) {
239 if ( event && event.detail && event.detail.statistics === "allow" ) { _jpStatsFire(); }
240 } );
241 document.addEventListener( "wp_consent_type_defined", _jpStatsCheck );
242 window.addEventListener( "wp_consent_type_defined", _jpStatsCheck );
243 if ( document.readyState === "loading" ) {
244 document.addEventListener( "DOMContentLoaded", _jpStatsCheck, { once: true } );
245 } else {
246 _jpStatsCheck();
247 }',
248 $pushes,
249 $fail_open_literal
250 );
251 }
252
253 /**
254 * Add fetchpriority="low" to the Stats script attributes.
255 *
256 * Reduces network contention with resources in the critical rendering path (e.g., the LCP
257 * element image). This benefits Safari and Firefox, which don't automatically assign low
258 * priority to async/defer scripts (unlike Chrome).
259 *
260 * @since 0.19.5
261 *
262 * @param array $attributes Script tag attributes.
263 * @return array Modified attributes.
264 */
265 public static function add_low_fetchpriority( $attributes ) {
266 // WordPress derives the tag id from the enqueue handle as "{handle}-js", so the
267 // 'jetpack-stats' script (registered in enqueue_stats_script()) prints as
268 // 'jetpack-stats-js'. Keep this in sync if the handle is ever renamed.
269 if ( isset( $attributes['id'] ) && 'jetpack-stats-js' === $attributes['id'] ) {
270 $attributes['fetchpriority'] = 'low';
271 }
272 return $attributes;
273 }
274
275 /**
276 * Remove the dns-prefetch resource hint for stats.wp.com.
277 *
278 * WordPress automatically adds dns-prefetch hints for enqueued script hosts via
279 * wp_dependencies_unique_hosts(). Since we're deprioritizing the stats script,
280 * the dns-prefetch is counterproductive — it front-loads DNS resolution for a
281 * resource we're intentionally delaying.
282 *
283 * @since 0.19.5
284 *
285 * @param array $urls Array of resource hint URLs.
286 * @param string $relation_type The relation type (dns-prefetch, preconnect, etc.).
287 * @return array Filtered URLs.
288 */
289 public static function remove_stats_dns_prefetch( $urls, $relation_type ) {
290 if ( 'dns-prefetch' !== $relation_type ) {
291 return $urls;
292 }
293
294 return array_filter(
295 $urls,
296 static function ( $url ) {
297 // Resource hints can be arrays that carry the URL under an 'href' key.
298 if ( is_array( $url ) ) {
299 $candidate = ( isset( $url['href'] ) && is_string( $url['href'] ) ) ? $url['href'] : '';
300 } elseif ( is_string( $url ) ) {
301 $candidate = $url;
302 } else {
303 return true; // Unknown entry shape; leave it untouched.
304 }
305
306 // dns-prefetch entries arrive in several shapes: WordPress core emits bare
307 // hosts ('stats.wp.com') via wp_dependencies_unique_hosts(), while other
308 // filters may add scheme-relative ('//stats.wp.com') or full URLs. Normalize
309 // each to a host so we drop stats.wp.com exactly without removing look-alike
310 // hosts such as 'mystats.wp.com' or 'stats.wp.com.evil.tld'.
311 if ( str_starts_with( $candidate, '//' ) ) {
312 $host = wp_parse_url( 'https:' . $candidate, PHP_URL_HOST );
313 } elseif ( str_contains( $candidate, '://' ) ) {
314 $host = wp_parse_url( $candidate, PHP_URL_HOST );
315 } else {
316 $host = $candidate; // Bare host form, e.g. 'stats.wp.com'.
317 }
318
319 return ! is_string( $host ) || 'stats.wp.com' !== strtolower( $host );
320 }
321 );
322 }
323
324 /**
325 * Enqueue the Stats pixel.
326 * Do not use this function directly, it is hooked into `wp_enqueue_scripts`.
327 *
328 * @access public
329 * @return void
330 */
331 public static function enqueue_stats_script() {
332 if ( self::is_amp_request() ) {
333 return;
334 }
335
336 wp_enqueue_script(
337 'jetpack-stats',
338 'https://stats.wp.com/e-' . gmdate( 'YW' ) . '.js',
339 array(),
340 null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- The version is set in the URL.
341 array(
342 'in_footer' => true,
343 'strategy' => 'defer',
344 )
345 );
346 add_filter( 'wp_script_attributes', array( static::class, 'add_low_fetchpriority' ) );
347 add_filter( 'wp_resource_hints', array( static::class, 'remove_stats_dns_prefetch' ), 100, 2 );
348
349 $data = self::build_view_data();
350
351 /**
352 * Filter the parameters added to the JavaScript stats tracking code.
353 *
354 * @module stats
355 *
356 * @since-jetpack 10.9
357 *
358 * @param array $data Array of options about the site and page you're on.
359 */
360 $data = (array) apply_filters( 'jetpack_stats_footer_js_data', $data );
361
362 $triggers = self::build_stats_details( $data );
363 wp_add_inline_script(
364 'jetpack-stats',
365 $triggers,
366 'before'
367 );
368 }
369
370 /**
371 * Gets the stats footer for AMP output.
372 *
373 * @access private
374 * @param array $data Array of data for the AMP pixel tracker.
375 * @return string Returns the footer to add for the Stats tracker in an AMP scenario.
376 */
377 private static function get_amp_footer( $data ) {
378 /**
379 * Filter the parameters added to the AMP pixel tracking code.
380 *
381 * @module stats
382 *
383 * @since-jetpack 10.9
384 *
385 * @param array $data Array of options about the site and page you're on.
386 */
387 $data = (array) apply_filters( 'jetpack_stats_footer_amp_data', $data );
388
389 $data['host'] = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; // input var ok.
390 $data['rand'] = 'RANDOM'; // AMP placeholder.
391 $data['ref'] = 'DOCUMENT_REFERRER'; // AMP placeholder.
392 $data = array_map( 'rawurlencode', $data );
393 $pixel_url = add_query_arg( $data, 'https://pixel.wp.com/g.gif' );
394 return '<amp-pixel src="' . esc_url( $pixel_url ) . '"></amp-pixel>';
395 }
396
397 /**
398 * Build an AMP pixel.
399 * Do not use this function directly, it is hooked into `wp_footer`.
400 *
401 * @access public
402 * @return void
403 */
404 public static function add_amp_pixel() {
405 $data = self::build_view_data();
406 if ( ! self::is_amp_request() ) {
407 return;
408 }
409
410 $pixel = self::get_amp_footer( $data );
411 echo $pixel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
412 }
413
414 /**
415 * Stats Footer.
416 *
417 * @deprecated 0.6.0
418 *
419 * @access public
420 * @return void
421 */
422 public static function add_to_footer() {
423 _deprecated_function( __METHOD__, '0.6.0' );
424 }
425
426 /**
427 * Gets the footer to add for the Stats tracker.
428 *
429 * @deprecated 0.6.0
430 *
431 * @access public
432 * @param array $data Array of data for the JS stats tracker.
433 * @return void
434 */
435 public static function get_footer_to_add( $data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
436 _deprecated_function( __METHOD__, '0.6.0' );
437 }
438
439 /**
440 * Render the stats footer. Kept for backward compatibility on legacy AMF views.
441 *
442 * @deprecated 0.6.0
443 *
444 * @access public
445 * @param array $data Array of data for the JS stats tracker.
446 */
447 public static function render_footer( $data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
448 _deprecated_function( __METHOD__, '0.6.0' );
449 }
450
451 /**
452 * Render the stats footer for AMP output. Kept for backward compatibility.
453 *
454 * @access public
455 * @param array $data Array of data for the AMP pixel tracker.
456 */
457 public static function render_amp_footer( $data ) {
458 print self::get_amp_footer( $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
459 }
460
461 /**
462 * Creates the "array" string used as part of the JS tracker.
463 *
464 * @access private
465 * @param array $kvs Array of options about the site and page you're on.
466 * @return string
467 */
468 private static function stats_array_to_string( $kvs ) {
469 /**
470 * Filters the options added to the JavaScript Stats tracking code.
471 *
472 * @since-jetpack 1.1.0
473 *
474 * @param array $kvs Array of options about the site and page you're on.
475 */
476 $kvs = (array) apply_filters( self::STATS_ARRAY_TO_STRING_FILTER, $kvs );
477 $kvs = array_map( 'strval', $kvs );
478
479 // Encode into JSON object for direct use in JS.
480 return wp_json_encode( $kvs, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
481 }
482
483 /**
484 * Does the page return AMP content.
485 *
486 * @return bool $is_amp_request Are we on AMP view.
487 */
488 private static function is_amp_request() {
489 $is_amp_request = ( function_exists( 'amp_is_request' ) && amp_is_request() );
490 $is_amp_request = $is_amp_request || ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() );
491
492 /**
493 * Returns true if the current request should return valid AMP content.
494 *
495 * @since 6.2.0
496 *
497 * @param boolean $is_amp_request Is this request supposed to return valid AMP content?
498 */
499 return apply_filters( 'jetpack_is_amp_request', $is_amp_request );
500 }
501 }
502