Nenhuma Descrição

class-wc-tracks-event.php 3.6KB

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