Skip to content

Commit 71be042

Browse files
authored
Merge pull request #492 from jacobarriola/bugfix/session-transaction-manager
Session Transaction Manager bugfixes and enhancements
2 parents da02e43 + b4a158b commit 71be042

File tree

1 file changed

+73
-9
lines changed

1 file changed

+73
-9
lines changed

includes/utils/class-session-transaction-manager.php

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,15 @@ public function update_transaction_queue( $source, $args, $context, $info ) {
124124
return;
125125
}
126126

127-
// Initialize transaction ID.
128-
if ( is_null( $this->transaction_id ) ) {
127+
128+
// Bail if transaction has already been completed. There are times when the underlying action runs twice.
129+
if ( ! is_null( $this->transaction_id ) ) {
130+
$transaction_queue = get_transient( "woo_session_transactions_queue_{$this->session_handler->get_customer_id()}" );
131+
if ( in_array( $this->transaction_id, array_column( $transaction_queue, 'transaction_id' ), true ) ) {
132+
return;
133+
}
134+
} else {
135+
// Initialize transaction ID.
129136
$mutation = $info->fieldName; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
130137
$this->transaction_id = \uniqid( "wooSession_{$mutation}_" );
131138
}
@@ -136,6 +143,9 @@ public function update_transaction_queue( $source, $args, $context, $info ) {
136143
$this->update_transaction_queue( $source, $args, $context, $info );
137144
} else {
138145
$this->session_handler->reload_data();
146+
147+
// Set a timestamp on the transaction, which will allow us to check for any stale transactions that accidentally get left behind.
148+
$this->set_timestamp();
139149
}
140150
}
141151

@@ -156,6 +166,10 @@ public function next_transaction() {
156166
// If current transaction is the lead exit loop.
157167
} elseif ( $this->transaction_id === $transaction_queue[0]['transaction_id'] ) {
158168
return true;
169+
} elseif ( true === $this->did_transaction_expire( $transaction_queue ) ) {
170+
// If transaction has expired, remove it from the queue array and continue loop
171+
array_shift( $transaction_queue );
172+
$this->save_transaction_queue( $transaction_queue );
159173
}
160174

161175
return false;
@@ -169,15 +183,16 @@ public function next_transaction() {
169183
*/
170184
public function get_transaction_queue() {
171185
// Get transaction queue.
172-
$transaction_queue = get_transient( "woo_session_transactions_queue_{$this->_customer_id}" );
186+
$transaction_queue = get_transient( "woo_session_transactions_queue_{$this->session_handler->get_customer_id()}" );
173187
if ( ! $transaction_queue ) {
174188
$transaction_queue = array();
175189
}
176190

177191
// If transaction ID not in queue, add it, and start transaction.
178-
if ( false === array_search( $this->transaction_id, array_column( $transaction_queue, 'transaction_id' ), true ) ) {
179-
$transaction_id = $this->transaction_id;
180-
$snapshot = $this->_data;
192+
if ( ! in_array( $this->transaction_id, array_column( $transaction_queue, 'transaction_id' ), true ) ) {
193+
$transaction_id = $this->transaction_id;
194+
$snapshot = $this->session_handler->get_session_data();
195+
181196
$transaction_queue[] = compact( 'transaction_id', 'snapshot' );
182197

183198
// Update queue.
@@ -199,7 +214,7 @@ public function pop_transaction_id() {
199214
}
200215

201216
// Get transaction queue.
202-
$transaction_queue = get_transient( "woo_session_transactions_queue_{$this->_customer_id}" );
217+
$transaction_queue = get_transient( "woo_session_transactions_queue_{$this->session_handler->get_customer_id()}" );
203218

204219
// Throw if transaction ID not on top.
205220
if ( $this->transaction_id !== $transaction_queue[0]['transaction_id'] ) {
@@ -221,11 +236,60 @@ public function pop_transaction_id() {
221236
public function save_transaction_queue( $queue = array() ) {
222237
// If queue empty delete transient and bail.
223238
if ( empty( $queue ) ) {
224-
delete_transient( "woo_session_transactions_queue_{$this->_customer_id}" );
239+
delete_transient( "woo_session_transactions_queue_{$this->session_handler->get_customer_id()}" );
225240
return;
226241
}
227242

228243
// Save transaction queue.
229-
set_transient( "woo_session_transactions_queue_{$this->_customer_id}", $queue );
244+
set_transient( "woo_session_transactions_queue_{$this->session_handler->get_customer_id()}", $queue );
245+
}
246+
247+
public function set_timestamp() {
248+
$transaction_queue = $this->get_transaction_queue();
249+
250+
// Bail if we don't have a queue to add a timestamp against.
251+
if ( empty( $transaction_queue[0] ) ) {
252+
return;
253+
}
254+
255+
$transaction_queue[0]['timestamp'] = time();
256+
257+
$this->save_transaction_queue( $transaction_queue );
258+
}
259+
260+
/**
261+
* The length of time in seconds a transaction should stay in the queue
262+
*
263+
* @return mixed|void
264+
*/
265+
public function get_timestamp_threshold() {
266+
return apply_filters( 'woographql_session_transaction_timeout', 30 );
267+
}
268+
269+
/**
270+
* Whether the transaction has expired. This helps prevent infinite loops while searching through the transaction
271+
* queue.
272+
*
273+
* @param $transaction_queue
274+
*
275+
* @return bool
276+
*/
277+
public function did_transaction_expire( $transaction_queue ) {
278+
// Guard against empty transaction queue. We assume that it is invalid since we cannot calculate.
279+
if ( empty( $transaction_queue ) ) {
280+
return true;
281+
}
282+
283+
// Guard against empty timestamp. We assume that it is invalid since we cannot calculate.
284+
if ( empty( $transaction_queue[0] ) || empty( $transaction_queue[0]['timestamp'] ) ) {
285+
return true;
286+
}
287+
288+
$now = time();
289+
$stamp = $transaction_queue[0]['timestamp'];
290+
$threshold = $this->get_timestamp_threshold();
291+
$difference = $now - $stamp;
292+
293+
return $difference > $threshold;
230294
}
231295
}

0 commit comments

Comments
 (0)