PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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 1.3.20 1.3.19 trunk All 48 releases
← All changes | app/Modules/Tax/TaxCalculator.php +46 -20 1.5.3 → 1.6.5 View file →
@@ -13,8 +13,27 @@
13 13
14 14 class TaxCalculator
15 15 {
16 16
17 + /**
18 + * Per-request memos. In production every HTTP request runs in a fresh PHP
19 + * process, so these live exactly one request. Long-running processes that
20 + * simulate multiple requests (test suites, CLI) must clear them between
21 + * simulated requests via resetCache() — as function-statics they
22 + * were unreachable and leaked the first request's terms/overrides/tax-class
23 + * lookups into every subsequent one.
24 + */
25 + private static $termsCache = null;
26 + private static $overridesCache = null;
27 + private static $taxClassCache = [];
28 +
29 + public static function resetCache(): void
30 + {
31 + self::$termsCache = null;
32 + self::$overridesCache = null;
33 + self::$taxClassCache = [];
34 + }
35 +
17 36 protected $productIds = [];
18 37
19 38 protected $taxMaps = [];
20 39
@@ -694,13 +713,11 @@
694 713 // in one query (mirrors the bulk-load pattern in getTermsByProductId).
695 714 // Note: static scope is per-request, not per-instance — safe because each
696 715 // HTTP request processes a single cart/address combination.
697 716 // Each term_id stores an array of overrides (multiple location variants allowed).
698 - static $overridesByTermId = null;
717 + if (self::$overridesCache === null) {
718 + self::$overridesCache = [];
699 719
700 - if ($overridesByTermId === null) {
701 - $overridesByTermId = [];
702 -
703 720 $allTermIds = [];
704 721 foreach ($this->productIds as $pid) {
705 722 $allTermIds = array_merge($allTermIds, $this->getTermsByProductId($pid));
706 723 }
@@ -713,9 +730,9 @@
713 730 ->forTaxOverrideCountry($this->country)
714 731 ->get();
715 732
716 733 foreach ($overrides as $override) {
717 - $overridesByTermId[(int) $override->object_id][] = is_array($override->meta_value)
734 + self::$overridesCache[(int) $override->object_id][] = is_array($override->meta_value)
718 735 ? $override->meta_value
719 736 : [];
720 737 }
721 738 }
@@ -728,13 +745,13 @@
728 745 $best = null;
729 746 $bestScore = -1;
730 747
731 748 foreach ($termIds as $termId) {
732 - if (!array_key_exists((int) $termId, $overridesByTermId)) {
749 + if (!array_key_exists((int) $termId, self::$overridesCache)) {
733 750 continue;
734 751 }
735 752
736 - foreach ($overridesByTermId[(int) $termId] as $metaValue) {
753 + foreach (self::$overridesCache[(int) $termId] as $metaValue) {
737 754 $score = $this->scoreOverrideMatch($metaValue, $lineItemClassId);
738 755 if ($score === null) {
739 756 continue;
740 757 }
@@ -945,9 +962,9 @@
945 962 }
946 963
947 964 $variantTaxClassSlug = Arr::get($variantOtherInfo, 'tax_class');
948 965 if ($variantTaxClassSlug) {
949 - $class = TaxClass::query()->where('slug', sanitize_text_field($variantTaxClassSlug))->first();
966 + $class = $this->getTaxClassBySlug(sanitize_text_field($variantTaxClassSlug));
950 967 if ($class) {
951 968 return [$class];
952 969 }
953 970 }
@@ -962,37 +979,46 @@
962 979 }
963 980
964 981 protected function getTermsByProductId($productId)
965 982 {
966 - static $formattedTerms = null;
967 -
968 - if ($formattedTerms === null) {
983 + if (self::$termsCache === null) {
969 984 $terms = App::make('db')->table('term_relationships')
970 985 ->whereIn('object_id', $this->productIds)
971 986 ->get();
972 987
973 - $formattedTerms = [];
988 + self::$termsCache = [];
974 989
975 990 foreach ($terms as $term) {
976 - if (!isset($formattedTerms[$term->object_id])) {
977 - $formattedTerms[$term->object_id] = [];
991 + if (!isset(self::$termsCache[$term->object_id])) {
992 + self::$termsCache[$term->object_id] = [];
978 993 }
979 - $formattedTerms[$term->object_id][] = $term->term_taxonomy_id;
994 + self::$termsCache[$term->object_id][] = $term->term_taxonomy_id;
980 995 }
981 996 }
982 997
983 - return Arr::get($formattedTerms, $productId, []);
998 + return Arr::get(self::$termsCache, $productId, []);
984 999 }
985 1000
986 1001 protected function getStandardTaxClass()
987 1002 {
988 - static $standardTaxClass = false;
1003 + return $this->getTaxClassBySlug('standard');
1004 + }
989 1005
990 - if ($standardTaxClass === false) {
991 - $standardTaxClass = TaxClass::query()->where('slug', 'standard')->first() ?: null;
1006 + /**
1007 + * Lazily filled per-slug tax-class map — each slug is queried at most once
1008 + * per request, only when the app actually needs it (variant tax_class
1009 + * lookups and the standard-class fallback share the same cache).
1010 + *
1011 + * @return TaxClass|null
1012 + */
1013 + protected function getTaxClassBySlug($slug)
1014 + {
1015 + $slug = (string) $slug;
1016 + if (!array_key_exists($slug, self::$taxClassCache)) {
1017 + self::$taxClassCache[$slug] = TaxClass::query()->where('slug', $slug)->first() ?: null;
992 1018 }
993 1019
994 - return $standardTaxClass;
1020 + return self::$taxClassCache[$slug];
995 1021 }
996 1022
997 1023 private function roundTax($amount)
998 1024 {