PluginProbe
Parse.ly / 3.16.2
Parse.ly v3.16.2
3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 3.20.3 All 105 releases
wp-parsely / src / Telemetry / Tracks / class-tracks-pixel.php

class-tracks-pixel.php in Parse.ly 3.16.2, at src/Telemetry/Tracks/class-tracks-pixel.php

196 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Telemetry: Tracks Pixel class
4 *
5 * @package Parsely\Telemetry
6 * @since 3.12.0
7 */
8
9 declare(strict_types=1);
10
11 namespace Parsely\Telemetry;
12
13 use WP_Error;
14
15 use function Parsely\Utils\get_timestamp;
16
17 /**
18 * Handles all operations related to the Tracks pixel.
19 *
20 * @since 3.12.0
21 */
22 class Tracks_Pixel {
23 /**
24 * Base URL of the Tracks pixel.
25 */
26 protected const PIXEL_BASE_URL = 'https://pixel.wp.com/t.gif';
27
28 /**
29 * Class singleton instance.
30 *
31 * @var ?Tracks_Pixel
32 */
33 protected static $instance = null;
34
35 /**
36 * Events queued to be sent to the Tracks pixel.
37 *
38 * @var array<Tracks_Event>
39 */
40 protected $events = array();
41
42 /**
43 * Constructor.
44 *
45 * @since 3.12.0
46 */
47 public function __construct() {
48 // Track events asynchronously (inject pixels in the footer).
49 add_action( 'admin_footer', array( $this, 'render_tracking_pixels' ) );
50 // Synchronously track any remaining events that could not be added to
51 // the footer.
52 add_action( 'shutdown', array( $this, 'record_remaining_events' ) );
53 }
54
55 /**
56 * Instantiates the singleton.
57 *
58 * @since 3.12.0
59 *
60 * @return Tracks_Pixel
61 */
62 public static function instance(): Tracks_Pixel {
63 if ( is_null( self::$instance ) ) {
64 self::$instance = new Tracks_Pixel();
65 }
66
67 return self::$instance;
68 }
69
70 /**
71 * Enqueues an event to be recorded asynchronously.
72 *
73 * @since 3.12.0
74 *
75 * @param Tracks_Event $event The event to record.
76 * @return bool|WP_Error True if the event was enqueued for recording.
77 * False if the event is not recordable.
78 * WP_Error if the event is generating an error.
79 */
80 public function record_event_asynchronously( Tracks_Event $event ) {
81 $is_event_recordable = $event->is_recordable();
82
83 if ( true !== $is_event_recordable ) {
84 return $is_event_recordable;
85 }
86
87 self::instance()->events[] = $event;
88
89 return true;
90 }
91
92 /**
93 * Records an event synchronously (using a GET request).
94 *
95 * @since 3.12.0
96 *
97 * @param Tracks_Event $event The event to record.
98 * @return bool|WP_Error True if recording the event succeeded.
99 * False if the event is not recordable.
100 * WP_Error if any error occurred.
101 */
102 public function record_event_synchronously( Tracks_Event $event ) {
103 $is_event_recordable = $event->is_recordable();
104
105 if ( true !== $is_event_recordable ) {
106 return $is_event_recordable;
107 }
108
109 $pixel_url = self::instance()->generate_pixel_url( $event );
110
111 if ( null === $pixel_url ) {
112 return new WP_Error(
113 'invalid_pixel',
114 'cannot generate tracks pixel for given input',
115 array( 'status' => 400 )
116 );
117 }
118
119 // Add the Request Timestamp and URL terminator just before the HTTP
120 // request.
121 $pixel_url .= '&_rt=' . get_timestamp() . '&_=_';
122
123 $request = wp_safe_remote_get(
124 $pixel_url,
125 array(
126 'blocking' => false,
127 'redirection' => 2,
128 'httpversion' => '1.1',
129 'timeout' => 1,
130 )
131 );
132
133 if ( is_wp_error( $request ) ) {
134 return $request;
135 }
136
137 return true;
138 }
139
140 /**
141 * Outputs a Tracks pixel for every registered event.
142 *
143 * @since 3.12.0
144 */
145 public function render_tracking_pixels(): void {
146 foreach ( $this->events as $event ) {
147 $pixel_url = $this->generate_pixel_url( $event );
148
149 if ( null === $pixel_url ) {
150 continue;
151 }
152
153 echo '<img style="position: fixed;" src="', esc_url( $pixel_url ), '" />';
154 }
155
156 $this->events = array();
157 }
158
159 /**
160 * Generates a Tracks pixel URL that will record an event when accessed.
161 *
162 * @since 3.12.0
163 *
164 * @param Tracks_Event $event The event for which to generate the pixel URL.
165 * @return ?string The pixel URL or null if an error occurred.
166 */
167 protected function generate_pixel_url( Tracks_Event $event ): ?string {
168 $event_data = $event->get_data();
169
170 if ( is_wp_error( $event_data ) ) {
171 return null;
172 }
173
174 $args = get_object_vars( $event_data );
175
176 // Request Timestamp and URL Terminator must be added just before the
177 // HTTP request or not at all.
178 unset( $args['_rt'], $args['_'] );
179
180 return esc_url_raw(
181 self::PIXEL_BASE_URL . '?' . http_build_query( $args )
182 );
183 }
184
185 /**
186 * Records any remaining events synchronously.
187 *
188 * @since 3.12.0
189 */
190 public function record_remaining_events(): void {
191 foreach ( $this->events as $event ) {
192 self::instance()->record_event_synchronously( $event );
193 }
194 }
195 }
196