PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.1.1
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.1.1
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / libraries / bsf-analytics / class-bsf-analytics-events.php

class-bsf-analytics-events.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.1.1, at libraries/bsf-analytics/class-bsf-analytics-events.php

202 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * BSF Analytics Events — reusable one-time milestone tracking.
4 *
5 * Tracks events temporarily, sends them once via BSF Analytics,
6 * then cleans up. Only a minimal dedup flag remains.
7 *
8 * @package bsf-analytics
9 * @since 1.1.21
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit; // Exit if accessed directly.
14 }
15
16 if ( ! class_exists( 'BSF_Analytics_Events' ) ) {
17
18 /**
19 * BSF Analytics Events Class.
20 *
21 * @since 1.1.21
22 */
23 class BSF_Analytics_Events {
24
25 /**
26 * Plugin slug used as option key prefix in default storage.
27 *
28 * @var string
29 */
30 private $slug;
31
32 /**
33 * Option resolver callbacks.
34 *
35 * @var array{get: callable|null, update: callable|null}
36 */
37 private $option_resolver;
38
39 /**
40 * Constructor.
41 *
42 * @param string $slug Plugin slug (e.g. 'sureforms', 'astra').
43 * @param array $option_resolver Optional. Custom callbacks for option storage.
44 * 'get' => callable( $key, $default ) — retrieve an option.
45 * 'update' => callable( $key, $value ) — persist an option.
46 * When omitted, uses get_option( '{slug}_{key}' ) / update_option( '{slug}_{key}' ).
47 * @since 1.1.21
48 */
49 public function __construct( $slug, $option_resolver = array() ) {
50 $this->slug = sanitize_key( $slug );
51 $this->option_resolver = wp_parse_args(
52 $option_resolver,
53 array(
54 'get' => null,
55 'update' => null,
56 )
57 );
58 }
59
60 /**
61 * Track a one-time event. Skips if already tracked or pending.
62 * Only stores temporary data — cleaned up after analytics send.
63 *
64 * @param string $event_name Event identifier.
65 * @param string $event_value Primary value (version, form ID, mode, etc.).
66 * @param array<string, mixed> $properties Additional context as key-value pairs.
67 * @since 1.1.21
68 * @return void
69 */
70 public function track( $event_name, $event_value = '', $properties = array() ) {
71 // Sanitize inputs once upfront — ensures dedup comparisons match stored values.
72 $event_name = sanitize_text_field( $event_name );
73 $event_value = sanitize_text_field( (string) $event_value );
74 $properties = is_array( $properties ) ? $properties : array();
75
76 // Check dedup flag — already sent in a previous cycle.
77 $pushed = $this->get_option( 'usage_events_pushed', array() );
78 $pushed = is_array( $pushed ) ? $pushed : array();
79 if ( in_array( $event_name, $pushed, true ) ) {
80 return;
81 }
82
83 // Check if already queued in current cycle.
84 $pending = $this->get_option( 'usage_events_pending', array() );
85 $pending = is_array( $pending ) ? $pending : array();
86 if ( in_array( $event_name, array_column( $pending, 'event_name' ), true ) ) {
87 return;
88 }
89
90 // Add to pending queue.
91 $pending[] = array(
92 'event_name' => $event_name,
93 'event_value' => $event_value,
94 'properties' => $properties,
95 'date' => current_time( 'mysql' ),
96 );
97 $this->update_option( 'usage_events_pending', $pending );
98 }
99
100 /**
101 * Flush pending events: returns them for the payload, then cleans up.
102 *
103 * After this call:
104 * - usage_events_pending is EMPTY (full event data deleted).
105 * - usage_events_pushed has event_name strings added (minimal dedup).
106 *
107 * @since 1.1.21
108 * @return array Pending events to include in payload. Empty if none.
109 */
110 public function flush_pending() {
111 $pending = $this->get_option( 'usage_events_pending', array() );
112 if ( empty( $pending ) || ! is_array( $pending ) ) {
113 return array();
114 }
115
116 // Add event names to dedup flag (minimal — just strings).
117 $pushed = $this->get_option( 'usage_events_pushed', array() );
118 $pushed = is_array( $pushed ) ? $pushed : array();
119 $pushed = array_unique(
120 array_merge( $pushed, array_column( $pending, 'event_name' ) )
121 );
122 $this->update_option( 'usage_events_pushed', $pushed );
123
124 // DELETE all temporary event data.
125 $this->update_option( 'usage_events_pending', array() );
126
127 return $pending;
128 }
129
130 /**
131 * Remove specific event names from the pushed dedup flag, allowing them to be re-tracked.
132 *
133 * Pass an array of event names to remove only those entries.
134 * Pass an empty array (or omit) to clear all pushed events.
135 *
136 * @param array<string> $event_names Event names to remove. Empty = clear all.
137 * @since 1.1.21
138 * @return void
139 */
140 public function flush_pushed( $event_names = array() ) {
141 $pushed = $this->get_option( 'usage_events_pushed', array() );
142 $pushed = is_array( $pushed ) ? $pushed : array();
143
144 if ( empty( $event_names ) ) {
145 $this->update_option( 'usage_events_pushed', array() );
146 return;
147 }
148
149 $pushed = array_values( array_diff( $pushed, $event_names ) );
150 $this->update_option( 'usage_events_pushed', $pushed );
151 }
152
153 /**
154 * Check if an event has already been tracked (sent or pending).
155 *
156 * @param string $event_name Event identifier.
157 * @since 1.1.21
158 * @return bool
159 */
160 public function is_tracked( $event_name ) {
161 $pushed = $this->get_option( 'usage_events_pushed', array() );
162 $pushed = is_array( $pushed ) ? $pushed : array();
163 if ( in_array( $event_name, $pushed, true ) ) {
164 return true;
165 }
166
167 $pending = $this->get_option( 'usage_events_pending', array() );
168 $pending = is_array( $pending ) ? $pending : array();
169 return in_array( $event_name, array_column( $pending, 'event_name' ), true );
170 }
171
172 /**
173 * Get an option value using custom resolver or default WordPress option.
174 *
175 * @param string $key Option key (e.g. 'usage_events_pending').
176 * @param mixed $default Default value.
177 * @return mixed
178 */
179 private function get_option( $key, $default = null ) {
180 if ( is_callable( $this->option_resolver['get'] ) ) {
181 return call_user_func( $this->option_resolver['get'], $key, $default );
182 }
183 return get_option( $this->slug . '_' . $key, $default );
184 }
185
186 /**
187 * Update an option value using custom resolver or default WordPress option.
188 *
189 * @param string $key Option key (e.g. 'usage_events_pending').
190 * @param mixed $value Value to store.
191 * @return void
192 */
193 private function update_option( $key, $value ) {
194 if ( is_callable( $this->option_resolver['update'] ) ) {
195 call_user_func( $this->option_resolver['update'], $key, $value );
196 return;
197 }
198 update_option( $this->slug . '_' . $key, $value );
199 }
200 }
201 }
202