PluginProbe
Parse.ly / 3.20.3
Parse.ly v3.20.3
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.20.3, at src/Telemetry/Tracks/class-tracks-pixel.php

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