PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.22
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.22
1.4.22 1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 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 All 185 releases
← All changes | app/Utility/Conditions.php +124 -26 1.4.12 → 1.4.22 View file →
@@ -141,11 +141,11 @@
141 141 // Apply the operator
142 142 if ( $first ) {
143 143 $result = $condition;
144 144 $first = false;
145 - } elseif ( $operator === 'and' ) {
145 + } elseif ( 'and' === $operator ) {
146 146 $result = $result && $condition;// phpcs:ignore
147 - } elseif ( $operator === 'or' ) {
147 + } elseif ( 'or' === $operator ) {
148 148 $result = $result || $condition;// phpcs:ignore
149 149 }
150 150 }//end foreach
151 151
@@ -224,14 +224,14 @@
224 224 $type = 'array';
225 225 }
226 226
227 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 ) {
228 + if ( 'between' === $condition && is_array( $compare ) && 2 === count( $compare ) ) {
229 229 $type = 'integer';
230 230 }
231 231
232 232 // If $compare is an array, check if it is a date range. Only for date between condition.
233 - if ( $condition === 'date_between'
233 + if ( 'date_between' === $condition
234 234 && is_array( $compare )
235 235 && count( $compare ) === 2
236 236 && strtotime( $compare[0] )
237 237 && strtotime( $compare[1] )
@@ -337,8 +337,14 @@
337 337 // Check if a string $compare_with is not in the array $compare
338 338 return !in_array( $compare_with, $compare, true );
339 339 }
340 340
341 + // A single stored value against a single option: without this both
342 + // list conditions fell through and returned false either way.
343 + if ( is_string( $compare ) && is_string( $compare_with ) ) {
344 + return $compare !== $compare_with;
345 + }
346 +
341 347 return false;
342 348
343 349 case 'in_list':
344 350 // return $compare;
@@ -357,8 +363,13 @@
357 363 // Check if a string $compare_with is in the array $compare
358 364 return in_array( $compare_with, $compare, true );
359 365 }
360 366
367 + // A single stored value against a single option.
368 + if ( is_string( $compare ) && is_string( $compare_with ) ) {
369 + return $compare === $compare_with;
370 + }
371 +
361 372 return false;
362 373
363 374 default:
364 375 return false;
@@ -435,30 +446,36 @@
435 446 if ( !is_string( $compare ) ) {
436 447 $compare = (string) $compare;
437 448 }
438 449
439 - switch ( $condition ) {
440 - case 'equal':
441 - return strtolower( $compare ) === strtolower( $compare_with );
450 + /**
451 + * A field can hold more than one value.
452 + *
453 + * An ACF checkbox group, or any multi select, stores an array. Which
454 + * comparison runs is decided by {@see self::get_type()} from the value
455 + * the merchant typed and the condition, never from what the product
456 + * side holds, so a plain "contains red" rule arrives here even when the
457 + * stored value is an array. Handing that array to stripos(), strtolower()
458 + * or substr() is fatal on PHP 8.
459 + *
460 + * Each stored value is compared in turn: a positive condition passes
461 + * when any of them matches, a negative one only when none do.
462 + */
463 + $matched = false;
442 464
443 - case 'not_equal':
444 - return strtolower( $compare ) !== strtolower( $compare_with );
465 + foreach ( $this->comparable_values( $compare_with ) as $haystack ) {
466 + if ( $this->matches_string_condition( $haystack, $compare, $condition ) ) {
467 + $matched = true;
445 468
446 - case 'contain':
447 - return stripos( $compare_with, $compare ) !== false;
469 + break;
470 + }
471 + }
448 472
449 - case 'not_contain':
450 - return stripos( $compare_with, $compare ) === false;
473 + if ( in_array( $condition, array( 'not_equal', 'not_contain' ), true ) ) {
474 + return ! $matched;
475 + }
451 476
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
477 + return $matched;
461 478 }
462 479
463 480 /**
464 481 * @param array $filters Filter array.
@@ -481,11 +498,11 @@
481 498
482 499 if ( $first_group ) {
483 500 $final_result = $group_result;
484 501 $first_group = false;
485 - } elseif ( $group->base_operator === 'and' ) {
502 + } elseif ( 'and' === $group->base_operator ) {
486 503 $final_result = $final_result && $group_result; // phpcs:ignore
487 - } elseif ( $group->base_operator === 'or' ) {
504 + } elseif ( 'or' === $group->base_operator ) {
488 505 $final_result = $final_result || $group_result; // phpcs:ignore
489 506 }
490 507 }
491 508
@@ -514,8 +531,16 @@
514 531 if ( in_array( $compare_with, $attributes_by_ids, true ) ) {
515 532 return 'id';
516 533 }
517 534
535 + /**
536 + * ACF stores the choice value, while the option's name is its label, so
537 + * matching on the name would compare "Red" against a stored "red".
538 + */
539 + if ( is_string( $compare_with ) && strpos( $compare_with, 'acf_fields_' ) === 0 ) {
540 + return 'id';
541 + }
542 +
518 543 return 'name';
519 544 }
520 545
521 546 /**
@@ -539,9 +564,9 @@
539 564 ) {
540 565 return false;
541 566 }
542 567
543 - if ( $filter->compare_with === 'item_quantity' ) {
568 + if ( 'item_quantity' === $filter->compare_with ) {
544 569 $total_quantity = array_sum(
545 570 array_map(
546 571 function( $item ) {
547 572 if ( isset( $item['quantity'] ) ) {
@@ -554,9 +579,9 @@
554 579 )
555 580 );
556 581
557 582 $compare_value = $total_quantity;
558 - } elseif ( $filter->compare_with === 'item_count' ) {
583 + } elseif ( 'item_count' === $filter->compare_with ) {
559 584 $compare_value = count( $matched_products );
560 585 } else {
561 586 return false;
562 587 }
@@ -597,7 +622,80 @@
597 622
598 623 default:
599 624 return false;
600 625 }
626 + }
627 +
628 + /**
629 + * Run one string condition against a single stored value.
630 + *
631 + * The negative conditions share the comparison of their positive
632 + * counterpart; {@see self::string_compare()} inverts the result once every
633 + * stored value has been checked.
634 + *
635 + * @param string $haystack One stored value.
636 + * @param string $compare Value the merchant typed.
637 + * @param string $condition Condition name.
638 + */
639 + private function matches_string_condition( string $haystack, string $compare, string $condition ): bool {
640 + switch ( $condition ) {
641 + case 'equal':
642 + case 'not_equal':
643 + return strtolower( $haystack ) === strtolower( $compare );
644 +
645 + case 'contain':
646 + case 'not_contain':
647 + return stripos( $haystack, $compare ) !== false;
648 +
649 + case 'start_with':
650 + return stripos( $haystack, $compare ) === 0;
651 +
652 + case 'end_with':
653 + return substr( $haystack, -strlen( $compare ) ) === $compare;
654 +
655 + default:
656 + return false;
657 + }//end switch
658 + }
659 +
660 + /**
661 + * Flatten an attribute value into the strings a comparison can run against.
662 + *
663 + * A single value yields one string, a multi value field one per entry.
664 + * Anything that cannot be expressed as text, a nested array or an object,
665 + * is dropped. An empty result still yields one empty string, so a negative
666 + * condition has something to fail against rather than passing by default.
667 + *
668 + * @param mixed $value Attribute value.
669 + * @return array<int, string>
670 + */
671 + private function comparable_values( $value ): array {
672 + $values = array( $value );
673 +
674 + if ( is_array( $value ) ) {
675 + $values = $value;
676 + }
677 +
678 + $result = array();
679 +
680 + foreach ( $values as $entry ) {
681 + if ( null === $entry ) {
682 + $result[] = '';
683 +
684 + continue;
685 + }
686 +
687 + if ( ! is_scalar( $entry ) ) {
688 + continue;
689 + }
690 +
691 + $result[] = (string) $entry;
692 + }
693 +
694 + if ( empty( $result ) ) {
695 + return array( '' );
696 + }
697 +
698 + return $result;
601 699 }
602 700
603 701 }