Completed
Push — try/namespacing-all-the-things ( 457764 )
by
unknown
08:24
created

Event::record()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Automattic\Jetpack\Tracks;
3
4
/**
5
 * @autounit nosara tracks-client
6
 *
7
 * Example Usage:
8
```php
9
	require_once( dirname(__FILE__) . 'path/to/tracks/class.tracks-event' );
10
11
	$event = new Jetpack_Tracks_Event( array(
12
		'_en'        => $event_name,       // required
13
		'_ui'        => $user_id,          // required unless _ul is provided
14
		'_ul'        => $user_login,       // required unless _ui is provided
15
16
		// Optional, but recommended
17
		'_via_ip'    => $client_ip,        // for geo, etc.
18
19
		// Possibly useful to set some context for the event
20
		'_via_ua'    => $client_user_agent,
21
		'_via_url'   => $client_url,
22
		'_via_ref'   => $client_referrer,
23
24
		// For user-targeted tests
25
		'abtest_name'        => $abtest_name,
26
		'abtest_variation'   => $abtest_variation,
27
28
		// Your application-specific properties
29
		'custom_property'    => $some_value,
30
	) );
31
32
	if ( is_wp_error( $event->error ) ) {
33
		// Handle the error in your app
34
	}
35
36
	$bump_and_redirect_pixel = $event->build_signed_pixel_url();
37
```
38
 */
39
40
class Event {
41
	const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/';
42
	const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';
43
	public $error;
44
45
	function __construct( $event ) {
46
		$_event = self::validate_and_sanitize( $event );
47
		if ( is_wp_error( $_event ) ) {
48
			$this->error = $_event;
49
			return;
50
		}
51
52
		foreach( $_event as $key => $value ) {
53
			$this->{$key} = $value;
54
		}
55
	}
56
57
	function record() {
58
		return Client::record_event( $this );
59
	}
60
61
	/**
62
	 * Annotate the event with all relevant info.
63
	 * @param  mixed		$event Object or (flat) array
64
	 * @return mixed        The transformed event array or WP_Error on failure.
65
	 */
66
	static function validate_and_sanitize( $event ) {
67
		$event = (object) $event;
68
69
		// Required
70
		if ( ! $event->_en ) {
71
			return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 );
72
		}
73
74
		// delete non-routable addresses otherwise geoip will discard the record entirely
75
		if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) {
76
			unset($event->_via_ip);
77
		}
78
79
		$validated = array(
80
			'browser_type'      => Client::BROWSER_TYPE,
81
			'_aua'              => Client::get_user_agent(),
82
		);
83
84
		$_event = (object) array_merge( (array) $event, $validated );
85
86
		// If you want to blacklist property names, do it here.
87
88
		// Make sure we have an event timestamp.
89
		if ( ! isset( $_event->_ts ) ) {
90
			$_event->_ts = Client::build_timestamp();
91
		}
92
93
		return $_event;
94
	}
95
96
	/**
97
	 * Build a pixel URL that will send a Tracks event when fired.
98
	 * On error, returns an empty string ('').
99
	 *
100
	 * @return string A pixel URL or empty string ('') if there were invalid args.
101
	 */
102
	function build_pixel_url() {
103
		if ( $this->error ) {
104
			return '';
105
		}
106
107
		$args = get_object_vars( $this );
108
109
		// Request Timestamp and URL Terminator must be added just before the HTTP request or not at all.
110
		unset( $args['_rt'] );
111
		unset( $args['_'] );
112
113
		$validated = self::validate_and_sanitize( $args );
114
115
		if ( is_wp_error( $validated ) )
116
			return '';
117
118
		return Client::PIXEL . '?' . http_build_query( $validated );
119
	}
120
121
	static function event_name_is_valid( $name ) {
122
		return preg_match( Event::EVENT_NAME_REGEX, $name );
123
	}
124
125
	static function prop_name_is_valid( $name ) {
126
		return preg_match( Event::PROP_NAME_REGEX, $name );
127
	}
128
129
	static function scrutinize_event_names( $event ) {
130
		if ( ! Event::event_name_is_valid( $event->_en ) ) {
131
			return;
132
		}
133
134
		$whitelisted_key_names = array(
135
			'anonId',
136
			'Browser_Type',
137
		);
138
139
		foreach ( array_keys( (array) $event ) as $key ) {
140
			if ( in_array( $key, $whitelisted_key_names ) ) {
141
				continue;
142
			}
143
			if ( ! Event::prop_name_is_valid( $key ) ) {
144
				return;
145
			}
146
		}
147
	}
148
}
149