Completed
Push — update/tracking-in-dev-mode ( 5ec1b0...8a14c1 )
by Jeremy
62:31 queued 54:24
created

Jetpack_Tracks_Client::build_timestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Legacy Jetpack Tracks Client
4
 *
5
 * @package Jetpack
6
 */
7
8
/**
9
 * Jetpack_Tracks_Client
10
 *
11
 * @autounit nosara tracks-client
12
 *
13
 * Send Tracks events on behalf of a user
14
 *
15
 * Example Usage:
16
```php
17
	require( dirname(__FILE__).'path/to/tracks/class.tracks-client' );
18
19
	$result = Jetpack_Tracks_Client::record_event( array(
20
		'_en'        => $event_name,       // required
21
		'_ui'        => $user_id,          // required unless _ul is provided
22
		'_ul'        => $user_login,       // required unless _ui is provided
23
24
		// Optional, but recommended
25
		'_ts'        => $ts_in_ms,         // Default: now
26
		'_via_ip'    => $client_ip,        // we use it for geo, etc.
27
28
		// Possibly useful to set some context for the event
29
		'_via_ua'    => $client_user_agent,
30
		'_via_url'   => $client_url,
31
		'_via_ref'   => $client_referrer,
32
33
		// For user-targeted tests
34
		'abtest_name'        => $abtest_name,
35
		'abtest_variation'   => $abtest_variation,
36
37
		// Your application-specific properties
38
		'custom_property'    => $some_value,
39
	) );
40
41
	if ( is_wp_error( $result ) ) {
42
		// Handle the error in your app
43
	}
44
```
45
 */
46
class Jetpack_Tracks_Client {
47
	const PIXEL           = 'https://pixel.wp.com/t.gif';
48
	const BROWSER_TYPE    = 'php-agent';
49
	const USER_AGENT_SLUG = 'tracks-client';
50
	const VERSION         = '0.3';
51
52
	/**
53
	 * Record an event.
54
	 *
55
	 * @param  mixed $event Event object to send to Tracks. An array will be cast to object. Required.
56
	 *                      Properties are included directly in the pixel query string after light validation.
57
	 * @return mixed         True on success, WP_Error on failure
58
	 */
59
	public static function record_event( $event ) {
60
		if ( ! Jetpack::jetpack_tos_agreed() || ! empty( $_COOKIE['tk_opt-out'] ) ) {
61
			return false;
62
		}
63
64
		if ( ! $event instanceof Jetpack_Tracks_Event ) {
65
			$event = new Jetpack_Tracks_Event( $event );
66
		}
67
		if ( is_wp_error( $event ) ) {
68
			return $event;
69
		}
70
71
		$pixel = $event->build_pixel_url( $event );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Tracks_Event::build_pixel_url() has too many arguments starting with $event.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
72
73
		if ( ! $pixel ) {
74
			return new WP_Error( 'invalid_pixel', 'cannot generate tracks pixel for given input', 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_pixel'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
75
		}
76
77
		return self::record_pixel( $pixel );
78
	}
79
80
	/**
81
	 * Synchronously request the pixel.
82
	 *
83
	 * @param string $pixel The wp.com tracking pixel.
84
	 * @return array|bool|WP_Error True if successful. wp_remote_get response or WP_Error if not.
85
	 */
86
	public static function record_pixel( $pixel ) {
87
		// Add the Request Timestamp and URL terminator just before the HTTP request.
88
		$pixel .= '&_rt=' . self::build_timestamp() . '&_=_';
89
90
		$response = wp_remote_get(
91
			$pixel,
92
			array(
93
				'blocking'    => true, // The default, but being explicit here :).
94
				'timeout'     => 1,
95
				'redirection' => 2,
96
				'httpversion' => '1.1',
97
				'user-agent'  => self::get_user_agent(),
98
			)
99
		);
100
101
		if ( is_wp_error( $response ) ) {
102
			return $response;
103
		}
104
105
		$code = isset( $response['response']['code'] ) ? $response['response']['code'] : 0;
106
107
		if ( 200 !== $code ) {
108
			return new WP_Error( 'request_failed', 'Tracks pixel request failed', $code );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'request_failed'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
109
		}
110
111
		return true;
112
	}
113
114
	/**
115
	 * Get the user agent.
116
	 *
117
	 * @return string The user agent.
118
	 */
119
	public static function get_user_agent() {
120
		return self::USER_AGENT_SLUG . '-v' . self::VERSION;
121
	}
122
123
	/**
124
	 * Build an event and return its tracking URL
125
	 *
126
	 * @deprecated          Call the `build_pixel_url` method on a Jetpack_Tracks_Event object instead.
127
	 * @param  array $event Event keys and values.
128
	 * @return string       URL of a tracking pixel.
129
	 */
130
	public static function build_pixel_url( $event ) {
131
		$_event = new Jetpack_Tracks_Event( $event );
132
		return $_event->build_pixel_url();
133
	}
134
135
	/**
136
	 * Validate input for a tracks event.
137
	 *
138
	 * @deprecated          Instantiate a Jetpack_Tracks_Event object instead
139
	 * @param  array $event Event keys and values.
140
	 * @return mixed        Validated keys and values or WP_Error on failure
141
	 */
142
	private static function validate_and_sanitize( $event ) {
143
		$_event = new Jetpack_Tracks_Event( $event );
144
		if ( is_wp_error( $_event ) ) {
145
			return $_event;
146
		}
147
		return get_object_vars( $_event );
148
	}
149
150
	/**
151
	 * Builds a timestamp.
152
	 *
153
	 * Milliseconds since 1970-01-01.
154
	 *
155
	 * @return string
156
	 */
157
	public static function build_timestamp() {
158
		$ts = round( microtime( true ) * 1000 );
159
		return number_format( $ts, 0, '', '' );
160
	}
161
162
	/**
163
	 * Grabs the user's anon id from cookies, or generates and sets a new one
164
	 *
165
	 * @return string An anon id for the user
166
	 */
167
	public static function get_anon_id() {
168
		static $anon_id = null;
169
170
		if ( ! isset( $anon_id ) ) {
171
172
			// Did the browser send us a cookie?
173
			if ( isset( $_COOKIE['tk_ai'] ) && preg_match( '#^[A-Za-z0-9+/=]{24}$#', $_COOKIE['tk_ai'] ) ) {
174
				$anon_id = $_COOKIE['tk_ai'];
175
			} else {
176
177
				$binary = '';
178
179
				// Generate a new anonId and try to save it in the browser's cookies.
180
				// Note that base64-encoding an 18 character string generates a 24-character anon id.
181
				for ( $i = 0; $i < 18; ++$i ) {
182
					$binary .= chr( wp_rand( 0, 255 ) );
183
				}
184
185
				$anon_id = 'jetpack:' . base64_encode( $binary ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
186
187
				if ( ! headers_sent()
188
					&& ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
189
					&& ! ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
190
				) {
191
					setcookie( 'tk_ai', $anon_id );
192
				}
193
			}
194
		}
195
196
		return $anon_id;
197
	}
198
199
	/**
200
	 * Gets the WordPress.com user's Tracks identity, if connected.
201
	 *
202
	 * @return array|bool
203
	 */
204
	public static function get_connected_user_tracks_identity() {
205
		$user_data = Jetpack::get_connected_user_data();
206
		if ( ! $user_data ) {
207
			return false;
208
		}
209
210
		return array(
211
			'blogid'   => Jetpack_Options::get_option( 'id', 0 ),
212
			'userid'   => $user_data['ID'],
213
			'username' => $user_data['login'],
214
		);
215
	}
216
}
217