1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
<?php /** * This class represents an event used to record a Tracks event * * @package WooCommerce\Tracks */
use Automattic\Jetpack\Constants;
defined( 'ABSPATH' ) || exit;
/** * WC_Tracks_Event class. */ #[AllowDynamicProperties] class WC_Tracks_Event {
/** * Event name regex. */ public const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){1}([a-z0-9_]+)$/';
/** * Property name regex. */ public const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';
/** * Error message as WP_Error. * * @var WP_Error */ public $error;
/** * WC_Tracks_Event constructor. * * @param array $event Event properties. */ public function __construct( $event ) { $_event = self::validate_and_sanitize( $event ); if ( is_wp_error( $_event ) ) { $this->error = $_event; return; }
foreach ( $_event as $key => $value ) { $this->{$key} = $value; } }
/** * Record Tracks event * * @return bool Always returns true. */ public function record() { if ( wp_doing_ajax() || Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'WP_CLI' ) || wp_doing_cron() ) { return WC_Tracks_Client::record_event( $this ); }
return WC_Tracks_Footer_Pixel::record_event( $this ); }
/** * Annotate the event with all relevant info. * * @param array $event Event arguments. * @return bool|WP_Error True on success, WP_Error on failure. */ public static function validate_and_sanitize( $event ) { $event = (object) $event;
// Required. if ( ! $event->_en ) { return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 ); }
// Delete non-routable addresses otherwise geoip will discard the record entirely. if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) { unset( $event->_via_ip ); }
$validated = array( 'browser_type' => WC_Tracks_Client::BROWSER_TYPE, );
$_event = (object) array_merge( (array) $event, $validated );
// If you want to block property names, do it here. // Make sure we have an event timestamp. if ( ! isset( $_event->_ts ) ) { $_event->_ts = WC_Tracks_Client::build_timestamp(); }
if ( ! self::event_name_is_valid( $_event->_en ) ) { return new WP_Error( 'invalid_event_name', __( 'A valid event name must be specified.', 'woocommerce' ) ); }
foreach ( array_keys( (array) $_event ) as $key ) { if ( ! self::prop_name_is_valid( $key ) && '_en' !== $key ) { return new WP_Error( 'invalid_prop_name', __( 'A valid prop name must be specified', 'woocommerce' ) ); } }
return $_event; }
/** * Build a pixel URL that will send a Tracks event when fired. * On error, returns an empty string (''). * * @return string A pixel URL or empty string ('') if there were invalid args. */ public function build_pixel_url() { if ( $this->error ) { return ''; }
$args = get_object_vars( $this );
// Request Timestamp and URL Terminator must be added just before the HTTP request or not at all. unset( $args['_rt'], $args['_'] );
$validated = self::validate_and_sanitize( $args );
if ( is_wp_error( $validated ) ) { return ''; }
return esc_url_raw( WC_Tracks_Client::PIXEL . '?' . http_build_query( $validated ) ); }
/** * Check if event name is valid. * * @param string $name Event name. * @return false|int */ public static function event_name_is_valid( $name ) { return preg_match( self::EVENT_NAME_REGEX, $name ); }
/** * Check if a property name is valid. * * @param string $name Event property. * @return false|int */ public static function prop_name_is_valid( $name ) { return preg_match( self::PROP_NAME_REGEX, $name ); }
/** * Check event names * * @param object $event An event object. */ public static function scrutinize_event_names( $event ) { if ( ! self::event_name_is_valid( $event->_en ) ) { return; }
$allowed_key_names = array( 'anonId', 'Browser_Type', );
foreach ( array_keys( (array) $event ) as $key ) { if ( in_array( $key, $allowed_key_names, true ) ) { continue; } if ( ! self::prop_name_is_valid( $key ) ) { return; } } } }
|