Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
31.11% covered (danger)
31.11%
14 / 45
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
A8c_Mc_Stats
31.11% covered (danger)
31.11%
14 / 45
44.44% covered (danger)
44.44%
4 / 9
180.23
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 get_current_stats
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_group_query_args
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 get_stats_urls
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 do_stats
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 do_server_side_stats
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 do_server_side_stat
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 build_stats_url
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Jetpack MC Stats package.
4 *
5 * @package  automattic/jetpack-mc-stats
6 */
7
8namespace Automattic\Jetpack;
9
10/**
11 * Class MC Stats, used to record stats using https://pixel.wp.com/g.gif
12 */
13class A8c_Mc_Stats {
14
15    /**
16     * Holds the stats to be processed
17     *
18     * @var array
19     */
20    private $stats = array();
21
22    /**
23     * Indicates whether to use the transparent pixel (b.gif) instead of the regular smiley (g.gif)
24     *
25     * @var boolean
26     */
27    public $use_transparent_pixel = true;
28
29    /**
30     * Class Constructor
31     *
32     * @param boolean $use_transparent_pixel Use the transparent pixel instead of the smiley.
33     */
34    public function __construct( $use_transparent_pixel = true ) {
35        $this->use_transparent_pixel = $use_transparent_pixel;
36    }
37
38    /**
39     * Store a stat for later output.
40     *
41     * @param string $group The stat group.
42     * @param string $name The stat name to bump.
43     *
44     * @return boolean true if stat successfully added
45     */
46    public function add( $group, $name ) {
47
48        if ( ! \is_string( $group ) || ! \is_string( $name ) ) {
49            return false;
50        }
51
52        if ( ! isset( $this->stats[ $group ] ) ) {
53            $this->stats[ $group ] = array();
54        }
55
56        if ( \in_array( $name, $this->stats[ $group ], true ) ) {
57            return false;
58        }
59
60        $this->stats[ $group ][] = $name;
61
62        return true;
63    }
64
65    /**
66     * Gets current stats stored to be processed
67     *
68     * @return array $stats
69     */
70    public function get_current_stats() {
71        return $this->stats;
72    }
73
74    /**
75     * Return the stats from a group in an array ready to be added as parameters in a query string
76     *
77     * @param string $group_name The name of the group to retrieve.
78     * @return array Array with one item, where the key is the prefixed group and the value are all stats concatenated with a comma. If group not found, an empty array will be returned
79     */
80    public function get_group_query_args( $group_name ) {
81        $stats = $this->get_current_stats();
82        if ( isset( $stats[ $group_name ] ) && ! empty( $stats[ $group_name ] ) ) {
83            return array( "x_jetpack-{$group_name}" => implode( ',', $stats[ $group_name ] ) );
84        }
85        return array();
86    }
87
88    /**
89     * Gets a list of trac URLs for every stored URL
90     *
91     * @return array An array of URLs
92     */
93    public function get_stats_urls() {
94
95        $urls = array();
96
97        foreach ( $this->get_current_stats() as $group => $stat ) {
98            $group_query_string = $this->get_group_query_args( $group );
99            $urls[]             = $this->build_stats_url( $group_query_string );
100        }
101
102        return $urls;
103    }
104
105    /**
106     * Outputs the tracking pixels for the current stats and empty the stored stats from the object
107     *
108     * @return void
109     */
110    public function do_stats() {
111        $urls = $this->get_stats_urls();
112        foreach ( $urls as $url ) {
113            echo '<img src="' . esc_url( $url ) . '" width="1" height="1" style="display:none;" />';
114        }
115        $this->stats = array();
116    }
117
118    /**
119     * Pings the stats server for the current stats and empty the stored stats from the object
120     *
121     * @return void
122     */
123    public function do_server_side_stats() {
124        $urls = $this->get_stats_urls();
125        foreach ( $urls as $url ) {
126            $this->do_server_side_stat( $url );
127        }
128        $this->stats = array();
129    }
130
131    /**
132     * Runs stats code for a one-off, server-side.
133     *
134     * @param string $url string The URL to be pinged. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
135     *
136     * @return bool If it worked.
137     */
138    public function do_server_side_stat( $url ) {
139        $response = wp_remote_get( esc_url_raw( $url ) );
140        if ( is_wp_error( $response ) ) {
141            return false;
142        }
143
144        if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
145            return false;
146        }
147
148        return true;
149    }
150
151    /**
152     * Builds the stats url.
153     *
154     * @param array $args array|string The arguments to append to the URL.
155     *
156     * @return string The URL to be pinged.
157     */
158    public function build_stats_url( $args ) {
159        $defaults = array(
160            'v'    => 'wpcom2',
161            // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand -- There can be a case where pluggables are not yet loaded.
162            'rand' => md5( ( function_exists( 'wp_rand' ) ? wp_rand( 0, 999 ) : rand( 0, 999 ) ) . time() ),
163        );
164        $args     = wp_parse_args( $args, $defaults );
165        $gifname  = true === $this->use_transparent_pixel ? 'b.gif' : 'g.gif';
166
167        /**
168         * Filter the URL used as the Stats tracking pixel.
169         *
170         * @since-jetpack 2.3.2
171         * @since 1.0.0
172         *
173         * @param string $url Base URL used as the Stats tracking pixel.
174         */
175        $base_url = apply_filters(
176            'jetpack_stats_base_url',
177            'https://pixel.wp.com/' . $gifname
178        );
179        $url      = add_query_arg( $args, $base_url );
180        return $url;
181    }
182}