PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.20
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.20
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 1.3.52 1.3.51 All 183 releases
← All changes | app/Utility/Conditions.php +116 -18 1.4.8 → 1.4.20 View file →
@@ -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.
@@ -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 /**
@@ -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 }