PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 All 51 releases
← All changes | api/Resource/FrontendResource/CartResource.php +74 -10 1.5.1 → 1.7.1 View file →
@@ -19,8 +19,29 @@
19 19
20 20 class CartResource extends BaseResourceApi
21 21 {
22 22
23 + /**
24 + * Per-request memo for get(). In production every HTTP request runs in a
25 + * fresh PHP process, so this lives exactly one request. Long-running
26 + * processes that simulate multiple requests (test suites, CLI) must clear
27 + * it between simulated requests via resetCartCache() — as a
28 + * function-static it was unreachable and leaked the first request's cart
29 + * into every subsequent one.
30 + *
31 + * Only a resolved Cart is memoized; a null ("no cart") result is
32 + * deliberately re-queried on the next call — matching the original
33 + * function-static behavior, where isset(null) === false.
34 + *
35 + * @var Cart|null|false false = not resolved yet
36 + */
37 + private static $cartCache = false;
38 +
39 + public static function resetCartCache(): void
40 + {
41 + static::$cartCache = false;
42 + }
43 +
23 44 public static function getQuery(): Builder
24 45 {
25 46 return Cart::query();
26 47 }
@@ -59,8 +80,16 @@
59 80 'variation' => $variation,
60 81 'product' => !$isCustom ? $variation->product : []
61 82 ]);
62 83
84 + // After the filter, not before: this path takes its quantity straight from a
85 + // public URL param, and the filter above can replace it with anything.
86 + $error = CartHelper::validateQuantity($quantity);
87 + if ($error) {
88 + return $error;
89 + }
90 + $quantity = (int)$quantity;
91 +
63 92 if ($variation->payment_type === 'subscription') {
64 93 $quantity = 1;
65 94 }
66 95
@@ -140,11 +169,10 @@
140 169 *
141 170 */
142 171 public static function get(array $params = [])
143 172 {
144 - static $cart;
145 - if (isset($cart)) {
146 - return $cart;
173 + if (static::$cartCache !== false && static::$cartCache !== null) {
174 + return static::$cartCache;
147 175 }
148 176
149 177 $autoCreate = Arr::get($params, 'create', false);
150 178
@@ -159,9 +187,9 @@
159 187 ->where('cart_group', 'instant');
160 188
161 189 $tempCart = $cartQuery->first();
162 190
163 - $cart = $tempCart;
191 + static::$cartCache = $tempCart;
164 192
165 193 if (!$autoCreate) {
166 194 return $tempCart;
167 195 }
@@ -166,11 +194,11 @@
166 194 return $tempCart;
167 195 }
168 196 }
169 197
170 - $cart = static::getOrSetCartForThisDevice($autoCreate);
198 + static::$cartCache = static::getOrSetCartForThisDevice($autoCreate);
171 199
172 - return $cart;
200 + return static::$cartCache;
173 201 }
174 202
175 203 public static function find($id, $params = [])
176 204 {
@@ -195,11 +223,13 @@
195 223 {
196 224 $itemId = Arr::get($data, 'id');
197 225 $quantity = Arr::get($data, 'quantity', 1);
198 226
199 - if ($quantity <= 0) {
227 + // This path writes cart_data directly instead of going through Cart::addItem().
228 + $error = CartHelper::validateQuantity($quantity);
229 + if ($error) {
200 230 return static::makeErrorResponse([
201 - ['code' => 403, 'message' => __('Quantity can not be negative.', 'fluent-cart')]
231 + ['code' => 403, 'message' => $error->get_error_message()]
202 232 ]);
203 233 }
204 234
205 235 $cart = CartResource::get([
@@ -331,9 +361,21 @@
331 361 ]);
332 362 }
333 363
334 364 if (!$variation) {
335 - return $cart->removeItem($itemId);
365 + // An item already in the cart whose variation row has since
366 + // disappeared (product/variation deleted) is dropped gracefully.
367 + // An id that was never in the cart and resolves to nothing is a
368 + // client error — silently answering "Cart updated successfully"
369 + // hid typos and probing as a 200 no-op.
370 + if ($existingItem !== null) {
371 + return $cart->removeItem($itemId);
372 + }
373 +
374 + return new WP_Error(
375 + 'invalid_item',
376 + __('Invalid item.', 'fluent-cart')
377 + );
336 378 }
337 379
338 380 $soldIndividually = $isCustom
339 381 ? !empty($variation->sold_individually)
@@ -372,9 +414,13 @@
372 414 }
373 415
374 416 $utmData = static::prepareUtmData($data);
375 417 if ($utmData) {
376 - $cart->utm_data = array_merge(is_array($cart->utm_data) ? $cart->utm_data : [], $utmData);
418 + // Replaced, not merged. A cart row is reused across visits, so merging
419 + // key by key accumulated a union of every touch that ever reached it and
420 + // the column stopped describing any single one. The browser has already
421 + // resolved which touch this is, so its block is the answer.
422 + $cart->utm_data = $utmData;
377 423 $cart->save();
378 424 }
379 425
380 426 return $cart;
@@ -545,8 +591,15 @@
545 591 if ($updatedQuantity < 0) {
546 592 $updatedQuantity = 0;
547 593 }
548 594
595 + if ($updatedQuantity > 0 && ($error = CartHelper::validateQuantity($updatedQuantity))) {
596 + return [
597 + 'code' => 'failed',
598 + 'message' => $error->get_error_message()
599 + ];
600 + }
601 +
549 602 if (!$isFilteredItem) {
550 603
551 604 if (!CartHelper::shouldAddItemToCart($productVariation, $updatedQuantity)) {
552 605 return [
@@ -575,8 +628,16 @@
575 628
576 629 if ($quantity < 1) {
577 630 $quantity = 1;
578 631 }
632 +
633 + if ($error = CartHelper::validateQuantity($quantity)) {
634 + return [
635 + 'code' => 'failed',
636 + 'message' => $error->get_error_message()
637 + ];
638 + }
639 +
579 640 if (!$isFilteredItem) {
580 641 if (!CartHelper::shouldAddItemToCart($productVariation, $quantity)) {
581 642 return [
582 643 'code' => 'failed',
@@ -742,12 +803,15 @@
742 803 }
743 804
744 805 $userId = get_current_user_id();
745 806 if ($userId) {
807 + // Latest cart first — without an order, first() picks by primary key
808 + // (cart_hash), which resurrects an arbitrary old cart for the user.
746 809 $cart = static::getQuery()
747 810 ->where('user_id', $userId)
748 811 ->where('stage', '!=', 'completed')
749 812 ->where('cart_group', 'global')
813 + ->orderBy('updated_at', 'DESC')
750 814 ->first();
751 815
752 816 if ($cart) {
753 817 return $cart;