PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.14
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.14
1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 1.3.47 1.3.46 1.3.45 All 177 releases
disco / app / Utility / Conditions.php

Conditions.php in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.4.14, at app/Utility/Conditions.php

604 lines 16.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore
2
3 /**
4 * Condition Utility
5 *
6 * @package Disco
7 * @subpackage App\Utility
8 * @since 1.0.0
9 * @category Utility
10 */
11
12 namespace Disco\App\Utility;
13
14 use Disco\App\Attributes\AttributeFactory;
15
16 /**
17 * Class Filter
18 *
19 * @package Disco
20 * @subpackage App\Utility
21 * @author Ohidul Islam <wahid0003@gmail.com>
22 * @link https://webappick.com
23 * @license https://opensource.org/licenses/gpl-license.php GNU Public License
24 * @category Utility
25 */
26 class Conditions { // phpcs:ignore
27
28 /**
29 * @var array $product Product Info.
30 */
31 private $product;
32
33 /**
34 * Contains the conditions array.
35 *
36 * @var array $conditions Filter object.
37 */
38 private $conditions;
39
40 /**
41 * Conditions constructor.
42 *
43 * @param \Disco\App\Utility\Config $config Campaign configuration.
44 * @param array $product Product Object.
45 */
46 public function __construct( $config, $product ) {
47 $this->conditions = $config->get_conditions();
48 $this->product = $product;
49 }
50
51 /**
52 * Check if the product is passed the filter
53 *
54 * @return bool
55 */
56 public function is_passed() {
57 if ( ! empty( $this->conditions ) ) {
58 return $this->check_all_conditions( $this->conditions );
59 }
60
61 return true;
62 }
63
64 /**
65 * Check if the product is passed the filter
66 *
67 * @param array $filters Filter array.
68 * array(
69 * array(
70 * 'base_operator' => 'and',
71 * 'base_filters' => array(
72 * 0 => array(
73 * 'filter' => 'id',
74 * 'operator' => 'and',
75 * 'condition' => 'between',
76 * 'compare' => array( 1, 100 ),
77 * ),
78 * 1 => array(
79 * 'filter' => 'Hat Wizard',
80 * 'operator' => 'and',
81 * 'condition' => 'contain',
82 * 'compare' => 'Hat',
83 * ),
84 * ),
85 * ),
86 * array(
87 * 'base_operator' => 'or',
88 * 'base_filters' => array(
89 * 0 => array(
90 * 'filter' => 'total_sold',
91 * 'operator' => 'and',
92 * 'condition' => 'greater_tan',
93 * 'compare' => 2000,
94 * ),
95 * 1 => array(
96 * 'filter' => 'total_order_name',
97 * 'operator' => 'or',
98 * 'condition' => 'equal',
99 * 'compare' => '100',
100 * ),
101 * ),
102 * array(
103 * 'base_operator' => 'and',
104 * 'base_filters' => array(
105 * 0 => array(
106 * 'filter' => 'total_sold',
107 * 'operator' => 'and',
108 * 'condition' => 'greater_tan',
109 * 'compare' => 2000,
110 * ),
111 * 2 => array(
112 * 'filter' => 'title',
113 * 'operator' => 'and',
114 * 'condition' => 'not_equal',
115 * 'compare' => '',
116 * ),
117 * ),
118 * ),
119 * ),.
120 * @return bool
121 */
122 public function check_group_conditions( $filters ) {// phpcs:ignore
123
124 $result = true;
125 // Start with true, will be updated based on conditions
126 $first = true;
127
128 foreach ( $filters as $filter ) {
129 if ( is_array( $filter ) ) {
130 $filter = (object) $filter;
131 }
132
133 if ( strtolower( $filter->operator ) ) {
134 $operator = strtolower( $filter->operator );
135 } else {
136 $operator = 'and';
137 }
138
139 $condition = $this->compare_value( $filter );
140
141 // Apply the operator
142 if ( $first ) {
143 $result = $condition;
144 $first = false;
145 } elseif ( $operator === 'and' ) {
146 $result = $result && $condition;// phpcs:ignore
147 } elseif ( $operator === 'or' ) {
148 $result = $result || $condition;// phpcs:ignore
149 }
150 }//end foreach
151
152 return $result;
153 }
154
155 /**
156 * Compare value based on a type
157 *
158 * @param object $filter Filter object.
159 * @return bool
160 */
161 public function compare_value( $filter ) {
162 if ( ! is_object( $filter ) || ! isset( $filter->condition, $filter->compare, $filter->compare_with ) ) {
163 return false;
164 }
165
166 /**
167 * Special case for item_quantity and item_count
168 * If the compare_with is item_quantity or item_count,
169 * we need to get the matched products from the cart
170 * and then compare the quantity or count of those products.
171 * This is because item_quantity and item_count are not product attributes,
172 */
173 if ( in_array( $filter->compare_with, array( 'item_quantity', 'item_count' ), true ) ) {
174 $matched_products = Helper::get_matched_conditions_cart_products( $this->conditions, $this->product );
175
176 $info = array(
177 'matched_products' => $matched_products,
178 );
179
180 return $this->compare_special_case( $filter, $info );
181 }
182
183 $type = $this->get_type( $filter->compare, $filter->condition );
184
185 switch ( $type ) {
186 case 'date':
187 return $this->date_compare( $filter );
188
189 case 'array':
190 return $this->array_compare( $filter );
191
192 case 'integer':
193 case 'double':
194 return $this->number_compare( $filter );
195
196 default:
197 return $this->string_compare( $filter );
198 }
199 }
200
201 /**
202 * Get the type of the compare value
203 *
204 * @param mixed $compare Compare value.
205 * @param string $condition Condition.
206 * @return string
207 */
208 public function get_type( $compare, $condition ) { // phpcs:ignore
209 // Get $compare variable value type
210 $type = 'string';
211
212 // If $compare is a number, check if it is an integer or double
213 if ( is_numeric( $compare ) ) {
214 $type = 'integer';
215 }
216
217 // If $compare is a date, check if it is a valid date
218 if ( !is_numeric( $compare ) && is_string( $compare ) && strtotime( $compare ) ) {
219 $type = 'date';
220 }
221
222 // If $condition is an array, check if it is an array of strings
223 if ( in_array( $condition, array( 'in_list', 'not_in_list' ), true ) ) {
224 $type = 'array';
225 }
226
227 // If $compare is an array, check if it is a number range. Only for between condition.
228 if ( $condition === 'between' && is_array( $compare ) && count( $compare ) === 2 ) {
229 $type = 'integer';
230 }
231
232 // If $compare is an array, check if it is a date range. Only for date between condition.
233 if ( $condition === 'date_between'
234 && is_array( $compare )
235 && count( $compare ) === 2
236 && strtotime( $compare[0] )
237 && strtotime( $compare[1] )
238 ) {
239 $type = 'date';
240 }
241
242 if ( is_bool( $compare ) ) {
243 $type = 'boolean';
244 }
245
246 return $type;
247 }
248
249 /**
250 * Compare value for a date type.
251 *
252 * @param object $filter Filter object.
253 * @return bool
254 */
255 public function date_compare( $filter ) {
256 if ( ! is_object( $filter ) || ! isset( $filter->condition, $filter->compare, $filter->compare_with ) ) {
257 return false;
258 }
259
260 $condition = $filter->condition;
261 $compare = $filter->compare;
262 $compare_with = AttributeFactory::get_value( $filter->compare_with, $this->product );
263
264 switch ( $condition ) {
265 case 'equal':
266 return $compare === $compare_with;
267
268 case 'not_equal':
269 return $compare !== $compare_with;
270
271 case 'greater':
272 return $compare_with > $compare;
273
274 case 'greater_equal':
275 return $compare_with >= $compare;
276
277 case 'lesser':
278 return $compare_with < $compare;
279
280 case 'lesser_equal':
281 return $compare_with <= $compare;
282
283 case 'date_between':
284 return $compare_with >= $compare[0] && $compare_with <= $compare[1];
285
286 default:
287 return false;
288 }//end switch
289 }
290
291 /**
292 * Compare value for an array type.
293 *
294 * @param object $filter Filter object.
295 * @return bool
296 */
297 public function array_compare( $filter ) {// phpcs:ignore
298 if ( ! is_object( $filter ) || ! isset( $filter->condition, $filter->compare, $filter->compare_with ) ) {
299 return false;
300 }
301
302 $condition = $filter->condition;
303 $compare = $filter->compare;
304
305 // Is compare_with needs to be compared with id or name
306 $column = $this->get_array_compare_column( $filter->compare_with );
307
308 if ( isset( $compare[0] ) && is_object( $compare[0] ) ) {
309 $compare = array_column( (array) $compare, $column );
310 } elseif ( isset( $compare[0] ) && is_array( $compare[0] ) ) {
311 $compare = array_column( $compare, $column );
312 }
313
314 $compare_with = AttributeFactory::get_value( $filter->compare_with, $this->product );
315
316 if ( is_array( $compare ) ) {
317 $compare = array_map( 'trim', $compare ); // Trim all values in the array
318 }
319
320 if ( is_array( $compare_with ) ) {
321 $compare_with = array_map( 'trim', $compare_with ); // Trim all values in the array
322 }
323
324 switch ( $condition ) {
325 case 'not_in_list':
326 if ( is_array( $compare ) && is_array( $compare_with ) ) {
327 // Check if there are no common elements between the two arrays
328 return !count( array_intersect( $compare, $compare_with ) );
329 }
330
331 if ( is_array( $compare_with ) ) {
332 // Check if a scalar $compare is not in the array $compare_with
333 return !in_array( $compare, $compare_with, true );
334 }
335
336 if ( is_array( $compare ) && is_string( $compare_with ) ) {
337 // Check if a string $compare_with is not in the array $compare
338 return !in_array( $compare_with, $compare, true );
339 }
340
341 return false;
342
343 case 'in_list':
344 // return $compare;
345 // Ensure $compare[0] is an array before calling array_intersect
346 if ( is_array( $compare ) && is_array( $compare_with ) ) {
347 // Extract IDs from $compare if $compare_with is an array of IDs
348 return (bool) count( array_intersect( $compare, $compare_with ) );
349 }
350
351 if ( is_array( $compare_with ) && is_string( $compare ) ) {
352 // Check if a scalar $compare is in the array $compare_with
353 return in_array( $compare, $compare_with, true );
354 }
355
356 if ( is_array( $compare ) && is_string( $compare_with ) ) {
357 // Check if a string $compare_with is in the array $compare
358 return in_array( $compare_with, $compare, true );
359 }
360
361 return false;
362
363 default:
364 return false;
365 }
366 }
367
368 /**
369 * Compare value for a number type.
370 *
371 * @param object $filter Filter object.
372 * @return bool
373 */
374 public function number_compare( $filter ) {// phpcs:ignore
375 if ( ! is_object( $filter ) || ! isset( $filter->condition, $filter->compare, $filter->compare_with ) ) {
376 return false;
377 }
378
379 $condition = $filter->condition;
380 $compare = is_array( $filter->compare ) ? array_map(
381 static function( $val ) {
382 return (float) $val;
383 },
384 $filter->compare
385 ) : (float) $filter->compare;
386 $compare_with = (float) AttributeFactory::get_value( $filter->compare_with, $this->product );
387
388 switch ( $condition ) {
389 case 'equal':
390 return $compare === $compare_with;
391
392 case 'not_equal':
393 return $compare !== $compare_with;
394
395 case 'greater':
396 return $compare_with > $compare;
397
398 case 'greater_equal':
399 return $compare_with >= $compare;
400
401 case 'lesser':
402 return $compare_with < $compare;
403
404 case 'lesser_equal':
405 return $compare_with <= $compare;
406
407 case 'between':
408 // @phpstan-ignore-next-line
409 return $compare_with >= $compare[0] && $compare_with <= $compare[1];
410
411 default:
412 return false;
413 }//end switch
414 }
415
416 /**
417 * Compare value for a string type.
418 *
419 * @param object $filter Filter object.
420 * @return bool
421 */
422 public function string_compare( $filter ) {
423 if ( ! is_object( $filter ) || ! isset( $filter->condition, $filter->compare, $filter->compare_with ) ) {
424 return false;
425 }
426
427 $condition = $filter->condition;
428 $compare = $filter->compare;
429 $compare_with = AttributeFactory::get_value( $filter->compare_with, $this->product );
430
431 if ( is_array( $compare ) ) {
432 $compare = wp_json_encode( $compare );
433 }
434
435 if ( !is_string( $compare ) ) {
436 $compare = (string) $compare;
437 }
438
439 switch ( $condition ) {
440 case 'equal':
441 return strtolower( $compare ) === strtolower( $compare_with );
442
443 case 'not_equal':
444 return strtolower( $compare ) !== strtolower( $compare_with );
445
446 case 'contain':
447 return stripos( $compare_with, $compare ) !== false;
448
449 case 'not_contain':
450 return stripos( $compare_with, $compare ) === false;
451
452 case 'start_with':
453 return stripos( $compare_with, $compare ) === 0;
454
455 case 'end_with':
456 return substr( $compare_with, -strlen( $compare ) ) === $compare;
457
458 default:
459 return false;
460 }//end switch
461 }
462
463 /**
464 * @param array $filters Filter array.
465 * @return bool
466 */
467 public function check_all_conditions( $filters ) {
468 $final_result = true;
469 $first_group = true;
470
471 if ( empty( $filters ) ) {
472 return true;
473 }
474
475 foreach ( $filters as $group ) {
476 if ( is_array( $group ) ) {
477 $group = (object) $group;
478 }
479
480 $group_result = $this->check_group_conditions( $group->base_filters );
481
482 if ( $first_group ) {
483 $final_result = $group_result;
484 $first_group = false;
485 } elseif ( $group->base_operator === 'and' ) {
486 $final_result = $final_result && $group_result; // phpcs:ignore
487 } elseif ( $group->base_operator === 'or' ) {
488 $final_result = $final_result || $group_result; // phpcs:ignore
489 }
490 }
491
492 return $final_result;
493 }
494
495 /**
496 * Get the column for array compare
497 * Sometime we need to get the column id for array compare.
498 * For example, we need to compare the product id with the array of product ids
499 *
500 * @param string $compare_with Compare with value.
501 * @return string
502 */
503 public function get_array_compare_column( $compare_with ) {
504 $attributes_by_ids = array(
505 'attributes', // Product Attributes
506 'customer_country', // Customer Country
507 'customer_state', // Customer State
508 'products_in_cart', // Products in Cart
509 'customer_history_total_order_made_by_ids', // Number of Orders Made with the Following Products
510 'customer_history_total_amount_sold_by_ids', // Total Amount Sold with the Following Products
511 'customer_history_total_quantity_sold_by_ids', // Number of Orders Made with the Following Products
512 );
513
514 if ( in_array( $compare_with, $attributes_by_ids, true ) ) {
515 return 'id';
516 }
517
518 return 'name';
519 }
520
521 /**
522 * Compare special cases for product filters, such as item quantity or item count.
523 *
524 * This function evaluates a filter condition (e.g., equal, greater, between) against either the total quantity
525 * of matched products (when comparing with 'item_quantity') or the count of matched products (when comparing with 'item_count').
526 *
527 * @param object $filter Filter object containing 'compare_with', 'condition', and 'compare' properties.
528 * @param array $info Array containing 'matched_products' (list of product data arrays).
529 * @return bool True if the condition is met, false otherwise.
530 */
531 public static function compare_special_case( $filter, $info ) { // phpcs:ignore
532 $matched_products = $info['matched_products'] ?? array();
533
534 if (
535 ! is_object( $filter ) ||
536 ! property_exists( $filter, 'compare_with' ) ||
537 ! property_exists( $filter, 'compare' ) ||
538 ! property_exists( $filter, 'condition' )
539 ) {
540 return false;
541 }
542
543 if ( $filter->compare_with === 'item_quantity' ) {
544 $total_quantity = array_sum(
545 array_map(
546 function( $item ) {
547 if ( isset( $item['quantity'] ) ) {
548 return $item['quantity'];
549 }
550
551 return 0;
552 },
553 $matched_products
554 )
555 );
556
557 $compare_value = $total_quantity;
558 } elseif ( $filter->compare_with === 'item_count' ) {
559 $compare_value = count( $matched_products );
560 } else {
561 return false;
562 }
563
564 $condition = $filter->condition;
565
566 if ( is_array( $filter->compare ) ) {
567 $target = (int) $filter->compare[0];
568 } else {
569 $target = (int) $filter->compare;
570 }
571
572 switch ( $condition ) {
573 case 'equal':
574 return $compare_value === $target;
575
576 case 'not_equal':
577 return $compare_value !== $target;
578
579 case 'greater':
580 return $compare_value > $target;
581
582 case 'greater_equal':
583 return $compare_value >= $target;
584
585 case 'lesser':
586 return $compare_value < $target;
587
588 case 'lesser_equal':
589 return $compare_value <= $target;
590
591 case 'between':
592 if ( is_array( $filter->compare ) && count( $filter->compare ) === 2 ) {
593 return $compare_value >= $filter->compare[0] && $compare_value <= $filter->compare[1];
594 }
595
596 return false;
597
598 default:
599 return false;
600 }
601 }
602
603 }
604